summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/core/compress_offload.c220
-rw-r--r--sound/core/info.c30
-rw-r--r--sound/core/init.c75
-rw-r--r--sound/core/jack.c8
-rw-r--r--sound/core/pcm.c12
-rw-r--r--sound/core/pcm_compat.c38
-rw-r--r--sound/core/pcm_lib.c200
-rw-r--r--sound/core/pcm_misc.c4
-rw-r--r--sound/core/pcm_native.c70
-rw-r--r--sound/core/pcm_timer.c11
-rw-r--r--sound/core/rawmidi.c58
-rw-r--r--sound/core/timer.c6
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/codecs/Kconfig82
-rw-r--r--sound/soc/codecs/Makefile46
-rw-r--r--sound/soc/codecs/audio-ext-clk-up.c618
-rw-r--r--sound/soc/codecs/audio-ext-clk.c349
-rw-r--r--sound/soc/codecs/msm_hdmi_codec_rx.c563
-rw-r--r--sound/soc/codecs/msm_sdw/Kconfig6
-rw-r--r--sound/soc/codecs/msm_sdw/Makefile3
-rw-r--r--sound/soc/codecs/msm_sdw/msm-sdw-tables.c319
-rw-r--r--sound/soc/codecs/msm_sdw/msm_sdw.h169
-rw-r--r--sound/soc/codecs/msm_sdw/msm_sdw_cdc.c2006
-rw-r--r--sound/soc/codecs/msm_sdw/msm_sdw_cdc_utils.c211
-rw-r--r--sound/soc/codecs/msm_sdw/msm_sdw_registers.h126
-rw-r--r--sound/soc/codecs/msm_sdw/msm_sdw_regmap.c161
-rw-r--r--sound/soc/codecs/msm_stub.c94
-rw-r--r--sound/soc/codecs/sdm660_cdc/Kconfig3
-rw-r--r--sound/soc/codecs/sdm660_cdc/Makefile2
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c4687
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h241
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-cdc-common.h67
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c2237
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h101
-rw-r--r--sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.c413
-rw-r--r--sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.h34
-rw-r--r--sound/soc/codecs/sdm660_cdc/sdm660-cdc-registers.h603
-rw-r--r--sound/soc/codecs/sdm660_cdc/sdm660-regmap.c611
-rw-r--r--sound/soc/codecs/wcd-dsp-mgr.c1231
-rw-r--r--sound/soc/codecs/wcd-dsp-utils.c221
-rw-r--r--sound/soc/codecs/wcd-dsp-utils.h51
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c3034
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.h571
-rw-r--r--sound/soc/codecs/wcd-spi-registers.h43
-rw-r--r--sound/soc/codecs/wcd-spi.c1521
-rw-r--r--sound/soc/codecs/wcd9330-tables.c1675
-rw-r--r--sound/soc/codecs/wcd9330.c9111
-rw-r--r--sound/soc/codecs/wcd9330.h128
-rw-r--r--sound/soc/codecs/wcd9335.c14441
-rw-r--r--sound/soc/codecs/wcd9335.h203
-rw-r--r--sound/soc/codecs/wcd934x/Makefile9
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsd.c772
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsd.h97
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c1373
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h119
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-mbhc.c1104
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-mbhc.h86
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-routing.h1171
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.c10133
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.h195
-rw-r--r--sound/soc/codecs/wcd9xxx-common-v2.c1365
-rw-r--r--sound/soc/codecs/wcd9xxx-common-v2.h236
-rw-r--r--sound/soc/codecs/wcd9xxx-common.c1478
-rw-r--r--sound/soc/codecs/wcd9xxx-common.h286
-rw-r--r--sound/soc/codecs/wcd9xxx-mbhc.c5643
-rw-r--r--sound/soc/codecs/wcd9xxx-mbhc.h492
-rw-r--r--sound/soc/codecs/wcd9xxx-resmgr-v2.c682
-rw-r--r--sound/soc/codecs/wcd9xxx-resmgr-v2.h93
-rw-r--r--sound/soc/codecs/wcd9xxx-resmgr.c1099
-rw-r--r--sound/soc/codecs/wcd9xxx-resmgr.h280
-rw-r--r--sound/soc/codecs/wcd_cmi_api.h43
-rw-r--r--sound/soc/codecs/wcd_cpe_core.c4614
-rw-r--r--sound/soc/codecs/wcd_cpe_core.h229
-rw-r--r--sound/soc/codecs/wcd_cpe_services.c3016
-rw-r--r--sound/soc/codecs/wcd_cpe_services.h179
-rwxr-xr-xsound/soc/codecs/wcdcal-hwdep.c228
-rw-r--r--sound/soc/codecs/wcdcal-hwdep.h40
-rw-r--r--sound/soc/codecs/wsa881x-analog.c1445
-rw-r--r--sound/soc/codecs/wsa881x-analog.h50
-rw-r--r--sound/soc/codecs/wsa881x-irq.c610
-rw-r--r--sound/soc/codecs/wsa881x-irq.h82
-rw-r--r--sound/soc/codecs/wsa881x-registers-analog.h206
-rw-r--r--sound/soc/codecs/wsa881x-registers.h178
-rw-r--r--sound/soc/codecs/wsa881x-regmap-analog.c499
-rw-r--r--sound/soc/codecs/wsa881x-regmap.c266
-rw-r--r--sound/soc/codecs/wsa881x-tables-analog.c171
-rw-r--r--sound/soc/codecs/wsa881x-tables.c171
-rw-r--r--sound/soc/codecs/wsa881x-temp-sensor.c149
-rw-r--r--sound/soc/codecs/wsa881x-temp-sensor.h39
-rw-r--r--sound/soc/codecs/wsa881x.c1460
-rw-r--r--sound/soc/codecs/wsa881x.h34
-rw-r--r--sound/soc/msm/Kconfig263
-rw-r--r--sound/soc/msm/Makefile39
-rw-r--r--sound/soc/msm/apq8096-auto.c7761
-rw-r--r--sound/soc/msm/device_event.h20
-rw-r--r--sound/soc/msm/msm-audio-pinctrl.c316
-rw-r--r--sound/soc/msm/msm-audio-pinctrl.h43
-rw-r--r--sound/soc/msm/msm-cpe-lsm.c3358
-rw-r--r--sound/soc/msm/msm-dai-fe.c3024
-rw-r--r--sound/soc/msm/msm-pcm-hostless.c84
-rw-r--r--sound/soc/msm/msm8996.c8926
-rw-r--r--sound/soc/msm/msm8998.c9501
-rw-r--r--sound/soc/msm/qdsp6v2/Makefile24
-rw-r--r--sound/soc/msm/qdsp6v2/adsp_err.c167
-rw-r--r--sound/soc/msm/qdsp6v2/audio_cal_utils.c968
-rw-r--r--sound/soc/msm/qdsp6v2/audio_calibration.c628
-rw-r--r--sound/soc/msm/qdsp6v2/audio_slimslave.c177
-rw-r--r--sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c1339
-rw-r--r--sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c4993
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c555
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c9794
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-slim.c670
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c394
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dolby-common.h266
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h85
-rw-r--r--sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c2314
-rw-r--r--sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h150
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c354
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h40
-rw-r--r--sound/soc/msm/qdsp6v2/msm-lsm-client.c2454
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c924
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h49
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c596
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c1552
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c1839
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c1363
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c3257
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h133
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c137
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h34
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c22571
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h575
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c1062
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h42
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c1713
-rw-r--r--sound/soc/msm/qdsp6v2/msm-qti-pp-config.c1487
-rw-r--r--sound/soc/msm/qdsp6v2/msm-qti-pp-config.h53
-rw-r--r--sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c1451
-rw-r--r--sound/soc/msm/qdsp6v2/q6adm.c4735
-rw-r--r--sound/soc/msm/qdsp6v2/q6afe.c7327
-rw-r--r--sound/soc/msm/qdsp6v2/q6asm.c10113
-rw-r--r--sound/soc/msm/qdsp6v2/q6audio-v2.c962
-rw-r--r--sound/soc/msm/qdsp6v2/q6common.c85
-rw-r--r--sound/soc/msm/qdsp6v2/q6core.c1277
-rw-r--r--sound/soc/msm/qdsp6v2/q6lsm.c2139
-rw-r--r--sound/soc/msm/qdsp6v2/q6voice.c8828
-rw-r--r--sound/soc/msm/qdsp6v2/q6voice.h1892
-rw-r--r--sound/soc/msm/qdsp6v2/rtac.c2075
-rw-r--r--sound/soc/msm/sdm660-common.c3268
-rw-r--r--sound/soc/msm/sdm660-common.h128
-rw-r--r--sound/soc/msm/sdm660-ext-dai-links.c2094
-rw-r--r--sound/soc/msm/sdm660-external.c1971
-rw-r--r--sound/soc/msm/sdm660-external.h54
-rw-r--r--sound/soc/msm/sdm660-internal.c3305
-rw-r--r--sound/soc/msm/sdm660-internal.h32
-rw-r--r--sound/soc/soc-compress.c194
-rw-r--r--sound/soc/soc-core.c96
-rw-r--r--sound/soc/soc-dapm.c46
-rw-r--r--sound/soc/soc-ops.c17
-rw-r--r--sound/soc/soc-pcm.c430
-rw-r--r--sound/soc/soc-utils.c3
-rw-r--r--sound/usb/Kconfig8
-rw-r--r--sound/usb/Makefile4
-rw-r--r--sound/usb/badd.c137
-rw-r--r--sound/usb/card.c148
-rw-r--r--sound/usb/card.h4
-rw-r--r--sound/usb/clock.c4
-rw-r--r--sound/usb/endpoint.c4
-rw-r--r--sound/usb/format.c54
-rw-r--r--sound/usb/mixer.c517
-rw-r--r--sound/usb/pcm.c70
-rw-r--r--sound/usb/pcm.h3
-rw-r--r--sound/usb/stream.c105
-rw-r--r--sound/usb/usb_audio_qmi_svc.c1357
-rw-r--r--sound/usb/usb_audio_qmi_v01.c833
-rw-r--r--sound/usb/usb_audio_qmi_v01.h150
-rw-r--r--sound/usb/usbaudio.h4
178 files changed, 238820 insertions, 336 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index e788c7e1929b..6352bf07cb2e 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -169,10 +169,13 @@ static int snd_compr_free(struct inode *inode, struct file *f)
static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
struct snd_compr_tstamp *tstamp)
{
+ int err = 0;
if (!stream->ops->pointer)
return -ENOTSUPP;
- stream->ops->pointer(stream, tstamp);
- pr_debug("dsp consumed till %d total %d bytes\n",
+ err = stream->ops->pointer(stream, tstamp);
+ if (err)
+ return err;
+ pr_debug("dsp consumed till %d total %llu bytes\n",
tstamp->byte_offset, tstamp->copied_total);
if (stream->direction == SND_COMPRESS_PLAYBACK)
stream->runtime->total_bytes_transferred = tstamp->copied_total;
@@ -252,8 +255,8 @@ static int snd_compr_write_data(struct snd_compr_stream *stream,
(app_pointer * runtime->buffer_size);
dstn = runtime->buffer + app_pointer;
- pr_debug("copying %ld at %lld\n",
- (unsigned long)count, app_pointer);
+ pr_debug("copying %zu at %lld\n",
+ count, app_pointer);
if (count < runtime->buffer_size - app_pointer) {
if (copy_from_user(dstn, buf, count))
return -EFAULT;
@@ -291,7 +294,7 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
}
avail = snd_compr_get_avail(stream);
- pr_debug("avail returned %ld\n", (unsigned long)avail);
+ pr_debug("avail returned %zu\n", avail);
/* calculate how much we can write to buffer */
if (avail > count)
avail = count;
@@ -346,7 +349,7 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
}
avail = snd_compr_get_avail(stream);
- pr_debug("avail returned %ld\n", (unsigned long)avail);
+ pr_debug("avail returned %zu\n", avail);
/* calculate how much we can read from buffer */
if (avail > count)
avail = count;
@@ -399,7 +402,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
poll_wait(f, &stream->runtime->sleep, wait);
avail = snd_compr_get_avail(stream);
- pr_debug("avail is %ld\n", (unsigned long)avail);
+ pr_debug("avail is %zu\n", avail);
/* check if we have at least one fragment to fill */
switch (stream->runtime->state) {
case SNDRV_PCM_STATE_DRAINING:
@@ -629,9 +632,10 @@ snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
static inline int
snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
{
- struct snd_compr_tstamp tstamp = {0};
+ struct snd_compr_tstamp tstamp;
int ret;
+ memset(&tstamp, 0, sizeof(tstamp));
ret = snd_compr_update_tstamp(stream, &tstamp);
if (ret == 0)
ret = copy_to_user((struct snd_compr_tstamp __user *)arg,
@@ -702,72 +706,48 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
/* clear flags and stop any drain wait */
stream->partial_drain = false;
stream->metadata_set = false;
- snd_compr_drain_notify(stream);
+ stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+ wake_up(&stream->runtime->sleep);
stream->runtime->total_bytes_available = 0;
stream->runtime->total_bytes_transferred = 0;
}
return retval;
}
-static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
-{
- int ret;
-
- /*
- * We are called with lock held. So drop the lock while we wait for
- * drain complete notfication from the driver
- *
- * It is expected that driver will notify the drain completion and then
- * stream will be moved to SETUP state, even if draining resulted in an
- * error. We can trigger next track after this.
- */
- stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
- mutex_unlock(&stream->device->lock);
-
- /* we wait for drain to complete here, drain can return when
- * interruption occurred, wait returned error or success.
- * For the first two cases we don't do anything different here and
- * return after waking up
- */
-
- ret = wait_event_interruptible(stream->runtime->sleep,
- (stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
- if (ret == -ERESTARTSYS)
- pr_debug("wait aborted by a signal");
- else if (ret)
- pr_debug("wait for drain failed with %d\n", ret);
-
-
- wake_up(&stream->runtime->sleep);
- mutex_lock(&stream->device->lock);
-
- return ret;
-}
-
+/* this fn is called without lock being held and we change stream states here
+ * so while using the stream state auquire the lock but relase before invoking
+ * DSP as the call will possibly take a while
+ */
static int snd_compr_drain(struct snd_compr_stream *stream)
{
int retval;
+ mutex_lock(&stream->device->lock);
switch (stream->runtime->state) {
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_SETUP:
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_PAUSED:
- return -EPERM;
+ retval = -EPERM;
+ goto ret;
case SNDRV_PCM_STATE_XRUN:
- return -EPIPE;
+ retval = -EPIPE;
+ goto ret;
default:
break;
}
+ mutex_unlock(&stream->device->lock);
retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
- if (retval) {
- pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
+ mutex_lock(&stream->device->lock);
+ if (!retval) {
+ stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
wake_up(&stream->runtime->sleep);
- return retval;
}
- return snd_compress_wait_for_drain(stream);
+ret:
+ mutex_unlock(&stream->device->lock);
+ return retval;
}
static int snd_compr_next_track(struct snd_compr_stream *stream)
@@ -796,17 +776,21 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
{
int retval;
+ mutex_lock(&stream->device->lock);
switch (stream->runtime->state) {
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_SETUP:
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_PAUSED:
+ mutex_unlock(&stream->device->lock);
return -EPERM;
case SNDRV_PCM_STATE_XRUN:
+ mutex_unlock(&stream->device->lock);
return -EPIPE;
default:
break;
}
+ mutex_unlock(&stream->device->lock);
/* stream can be drained only when next track has been signalled */
if (stream->next_track == false)
@@ -814,14 +798,74 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
stream->partial_drain = true;
retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
- if (retval) {
- pr_debug("Partial drain returned failure\n");
- wake_up(&stream->runtime->sleep);
- return retval;
- }
stream->next_track = false;
- return snd_compress_wait_for_drain(stream);
+ return retval;
+}
+
+static int snd_compr_set_next_track_param(struct snd_compr_stream *stream,
+ unsigned long arg)
+{
+ union snd_codec_options codec_options;
+ int retval;
+
+ /* set next track params when stream is running or has been setup */
+ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
+ stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
+ return -EPERM;
+
+ if (copy_from_user(&codec_options, (void __user *)arg,
+ sizeof(codec_options)))
+ return -EFAULT;
+
+ retval = stream->ops->set_next_track_param(stream, &codec_options);
+ return retval;
+}
+
+static int snd_compress_simple_ioctls(struct file *file,
+ struct snd_compr_stream *stream,
+ unsigned int cmd, unsigned long arg)
+{
+ int retval = -ENOTTY;
+
+ switch (_IOC_NR(cmd)) {
+ case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
+ retval = put_user(SNDRV_COMPRESS_VERSION,
+ (int __user *)arg) ? -EFAULT : 0;
+ break;
+
+ case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
+ retval = snd_compr_get_caps(stream, arg);
+ break;
+
+ case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
+ retval = snd_compr_get_codec_caps(stream, arg);
+ break;
+
+
+ case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
+ retval = snd_compr_tstamp(stream, arg);
+ break;
+
+ case _IOC_NR(SNDRV_COMPRESS_AVAIL):
+ retval = snd_compr_ioctl_avail(stream, arg);
+ break;
+
+ /* drain and partial drain need special handling
+ * we need to drop the locks here as the streams would get blocked on
+ * the dsp to get drained. The locking would be handled in respective
+ * function here
+ */
+ case _IOC_NR(SNDRV_COMPRESS_DRAIN):
+ retval = snd_compr_drain(stream);
+ break;
+
+ case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
+ retval = snd_compr_partial_drain(stream);
+ break;
+ }
+
+ return retval;
}
static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
@@ -835,15 +879,9 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
stream = &data->stream;
if (snd_BUG_ON(!stream))
return -EFAULT;
+
mutex_lock(&stream->device->lock);
switch (_IOC_NR(cmd)) {
- case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
- retval = put_user(SNDRV_COMPRESS_VERSION,
- (int __user *)arg) ? -EFAULT : 0;
- break;
- case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
- retval = snd_compr_get_caps(stream, arg);
- break;
#ifndef COMPR_CODEC_CAPS_OVERFLOW
case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
retval = snd_compr_get_codec_caps(stream, arg);
@@ -852,44 +890,49 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
retval = snd_compr_set_params(stream, arg);
break;
+
case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
retval = snd_compr_get_params(stream, arg);
break;
+
case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
retval = snd_compr_set_metadata(stream, arg);
break;
+
case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
retval = snd_compr_get_metadata(stream, arg);
break;
- case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
- retval = snd_compr_tstamp(stream, arg);
- break;
- case _IOC_NR(SNDRV_COMPRESS_AVAIL):
- retval = snd_compr_ioctl_avail(stream, arg);
- break;
+
case _IOC_NR(SNDRV_COMPRESS_PAUSE):
retval = snd_compr_pause(stream);
break;
+
case _IOC_NR(SNDRV_COMPRESS_RESUME):
retval = snd_compr_resume(stream);
break;
+
case _IOC_NR(SNDRV_COMPRESS_START):
retval = snd_compr_start(stream);
break;
+
case _IOC_NR(SNDRV_COMPRESS_STOP):
retval = snd_compr_stop(stream);
break;
- case _IOC_NR(SNDRV_COMPRESS_DRAIN):
- retval = snd_compr_drain(stream);
- break;
- case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
- retval = snd_compr_partial_drain(stream);
- break;
+
case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
retval = snd_compr_next_track(stream);
break;
+ case _IOC_NR(SNDRV_COMPRESS_SET_NEXT_TRACK_PARAM):
+ retval = snd_compr_set_next_track_param(stream, arg);
+ break;
+
+ default:
+ mutex_unlock(&stream->device->lock);
+ return snd_compress_simple_ioctls(f, stream, cmd, arg);
+
}
+
mutex_unlock(&stream->device->lock);
return retval;
}
@@ -904,17 +947,19 @@ static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd,
#endif
static const struct file_operations snd_compr_file_ops = {
- .owner = THIS_MODULE,
- .open = snd_compr_open,
- .release = snd_compr_free,
- .write = snd_compr_write,
- .read = snd_compr_read,
+ .owner = THIS_MODULE,
+ .open = snd_compr_open,
+ .release = snd_compr_free,
+ .write = snd_compr_write,
+ .read = snd_compr_read,
.unlocked_ioctl = snd_compr_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = snd_compr_ioctl_compat,
+#else
+ .compat_ioctl = snd_compr_ioctl,
#endif
- .mmap = snd_compr_mmap,
- .poll = snd_compr_poll,
+ .mmap = snd_compr_mmap,
+ .poll = snd_compr_poll,
};
static int snd_compress_dev_register(struct snd_device *device)
@@ -985,6 +1030,17 @@ int snd_compress_new(struct snd_card *card, int device,
}
EXPORT_SYMBOL_GPL(snd_compress_new);
+/*
+ * snd_compress_free: free compress device
+ * @card: sound card pointer
+ * @compr: compress device pointer
+ */
+void snd_compress_free(struct snd_card *card, struct snd_compr *compr)
+{
+ snd_device_free(card, compr);
+}
+EXPORT_SYMBOL_GPL(snd_compress_free);
+
static int snd_compress_add_device(struct snd_compr *device)
{
int ret;
diff --git a/sound/core/info.c b/sound/core/info.c
index 8a6fa8fd0aab..3ff6604ecab3 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -619,6 +619,36 @@ int snd_info_card_free(struct snd_card *card)
return 0;
}
+/*
+ * snd_register_module_info - create and register module
+ * @module: the module pointer
+ * @name: the module name
+ * @parent: the parent directory
+ *
+ * Creates and registers new module entry.
+ *
+ * Return: The pointer of the new instance, or NULL on failure.
+ */
+struct snd_info_entry *snd_register_module_info(struct module *module,
+ const char *name,
+ struct snd_info_entry *parent)
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_module_entry(module, name, parent);
+ if (!entry)
+ return NULL;
+
+ entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ return NULL;
+ }
+
+ return entry;
+}
+EXPORT_SYMBOL(snd_register_module_info);
/**
* snd_info_get_line - read one line from the procfs buffer
diff --git a/sound/core/init.c b/sound/core/init.c
index d0f8405fdfd8..f2b2102c0a72 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -58,6 +58,8 @@ static char *slots[SNDRV_CARDS];
module_param_array(slots, charp, NULL, 0444);
MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
+#define SND_CARD_STATE_MAX_LEN 16
+
/* return non-zero if the given index is reserved for the given
* module via slots option
*/
@@ -107,9 +109,39 @@ static void snd_card_id_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "%s\n", entry->card->id);
}
+static ssize_t snd_card_state_read(struct snd_info_entry *entry,
+ void *file_private_data, struct file *file,
+ char __user *buf, size_t count, loff_t pos)
+{
+ int len;
+ char buffer[SND_CARD_STATE_MAX_LEN];
+
+ /* make sure offline is updated prior to wake up */
+ rmb();
+ len = snprintf(buffer, sizeof(buffer), "%s\n",
+ entry->card->offline ? "OFFLINE" : "ONLINE");
+ return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static unsigned int snd_card_state_poll(struct snd_info_entry *entry,
+ void *private_data, struct file *file,
+ poll_table *wait)
+{
+ poll_wait(file, &entry->card->offline_poll_wait, wait);
+ if (xchg(&entry->card->offline_change, 0))
+ return POLLIN | POLLPRI | POLLRDNORM;
+ else
+ return 0;
+}
+
+static struct snd_info_entry_ops snd_card_state_proc_ops = {
+ .read = snd_card_state_read,
+ .poll = snd_card_state_poll,
+};
+
static int init_info_for_card(struct snd_card *card)
{
- struct snd_info_entry *entry;
+ struct snd_info_entry *entry, *entry_state;
entry = snd_info_create_card_entry(card, "id", card->proc_root);
if (!entry) {
@@ -119,6 +151,17 @@ static int init_info_for_card(struct snd_card *card)
entry->c.text.read = snd_card_id_read;
card->proc_id = entry;
+ entry_state = snd_info_create_card_entry(card, "state",
+ card->proc_root);
+ if (!entry_state) {
+ dev_dbg(card->dev, "unable to create card entry state\n");
+ card->proc_id = NULL;
+ return -ENOMEM;
+ }
+ entry_state->size = SND_CARD_STATE_MAX_LEN;
+ entry_state->content = SNDRV_INFO_CONTENT_DATA;
+ entry_state->c.ops = &snd_card_state_proc_ops;
+
return snd_info_card_register(card);
}
#else /* !CONFIG_SND_PROC_FS */
@@ -258,6 +301,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
init_waitqueue_head(&card->power_sleep);
#endif
+ init_waitqueue_head(&card->offline_poll_wait);
device_initialize(&card->card_dev);
card->card_dev.parent = parent;
card->card_dev.class = sound_class;
@@ -970,6 +1014,35 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
EXPORT_SYMBOL(snd_card_file_remove);
+/**
+ * snd_card_change_online_state - mark card's online/offline state
+ * @card: Card to mark
+ * @online: whether online of offline
+ *
+ * Mutes the DAI DAC.
+ */
+void snd_card_change_online_state(struct snd_card *card, int online)
+{
+ snd_printd("snd card %s state change %d -> %d\n",
+ card->shortname, !card->offline, online);
+ card->offline = !online;
+ /* make sure offline is updated prior to wake up */
+ wmb();
+ xchg(&card->offline_change, 1);
+ wake_up_interruptible(&card->offline_poll_wait);
+}
+EXPORT_SYMBOL(snd_card_change_online_state);
+
+/**
+ * snd_card_is_online_state - return true if card is online state
+ * @card: Card to query
+ */
+bool snd_card_is_online_state(struct snd_card *card)
+{
+ return !card->offline;
+}
+EXPORT_SYMBOL(snd_card_is_online_state);
+
#ifdef CONFIG_PM
/**
* snd_power_wait - wait until the power-state is changed.
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 7237acbdcbbc..45e9781b592e 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -32,13 +32,17 @@ struct snd_jack_kctl {
unsigned int mask_bits; /* only masked status bits are reported via kctl */
};
-static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
+static int jack_switch_types[] = {
SW_HEADPHONE_INSERT,
SW_MICROPHONE_INSERT,
SW_LINEOUT_INSERT,
SW_JACK_PHYSICAL_INSERT,
SW_VIDEOOUT_INSERT,
SW_LINEIN_INSERT,
+ SW_HPHL_OVERCURRENT,
+ SW_HPHR_OVERCURRENT,
+ SW_UNSUPPORT_INSERT,
+ SW_MICROPHONE2_INSERT,
};
static int snd_jack_dev_disconnect(struct snd_device *device)
@@ -240,7 +244,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
jack->type = type;
- for (i = 0; i < SND_JACK_SWITCH_TYPES; i++)
+ for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++)
if (type & (1 << i))
input_set_capability(jack->input_dev, EV_SW,
jack_switch_types[i]);
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index cdff5f976480..8239ebc9349c 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -747,6 +747,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
}
substream->group = &substream->self_group;
spin_lock_init(&substream->self_group.lock);
+ spin_lock_init(&substream->runtime_lock);
mutex_init(&substream->self_group.mutex);
INIT_LIST_HEAD(&substream->self_group.substreams);
list_add_tail(&substream->link_list, &substream->self_group.substreams);
@@ -860,6 +861,14 @@ static void free_chmap(struct snd_pcm_str *pstr)
snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl);
pstr->chmap_kctl = NULL;
}
+ if (pstr->vol_kctl) {
+ snd_ctl_remove(pstr->pcm->card, pstr->vol_kctl);
+ pstr->vol_kctl = NULL;
+ }
+ if (pstr->usr_kctl) {
+ snd_ctl_remove(pstr->pcm->card, pstr->usr_kctl);
+ pstr->usr_kctl = NULL;
+ }
}
static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
@@ -1017,9 +1026,11 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
+ unsigned long flags = 0;
if (PCM_RUNTIME_CHECK(substream))
return;
+ spin_lock_irqsave(&substream->runtime_lock, flags);
runtime = substream->runtime;
if (runtime->private_free != NULL)
runtime->private_free(runtime);
@@ -1038,6 +1049,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
put_pid(substream->pid);
substream->pid = NULL;
substream->pstr->substream_opened--;
+ spin_unlock_irqrestore(&substream->runtime_lock, flags);
}
static ssize_t show_pcm_class(struct device *dev,
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 7ae080bae15c..0c81e2657950 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -657,6 +657,39 @@ enum {
#endif /* CONFIG_X86_X32 */
};
+static int snd_compressed_ioctl32(struct snd_pcm_substream *substream,
+ unsigned int cmd, void __user *arg)
+{
+ struct snd_pcm_runtime *runtime;
+ int err = 0;
+
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
+ runtime = substream->runtime;
+ if (substream->ops->compat_ioctl) {
+ err = substream->ops->compat_ioctl(substream, cmd, arg);
+ } else {
+ err = -ENOIOCTLCMD;
+ pr_err("%s failed cmd = %d\n", __func__, cmd);
+ }
+ pr_debug("%s called with cmd = %d\n", __func__, cmd);
+ return err;
+}
+static int snd_user_ioctl32(struct snd_pcm_substream *substream,
+ unsigned int cmd, void __user *arg)
+{
+ struct snd_pcm_runtime *runtime;
+ int err = -ENOIOCTLCMD;
+
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
+ runtime = substream->runtime;
+ if (substream->ops->compat_ioctl)
+ err = substream->ops->compat_ioctl(substream, cmd, arg);
+ return err;
+}
+
+
static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
{
struct snd_pcm_file *pcm_file;
@@ -736,6 +769,11 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32:
return snd_pcm_ioctl_channel_info_x32(substream, argp);
#endif /* CONFIG_X86_X32 */
+ default:
+ if (_IOC_TYPE(cmd) == 'C')
+ return snd_compressed_ioctl32(substream, cmd, argp);
+ else if (_IOC_TYPE(cmd) == 'U')
+ return snd_user_ioctl32(substream, cmd, argp);
}
return -ENOIOCTLCMD;
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index ab8846e7e8ff..ef5743af33de 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -41,6 +41,9 @@
#define trace_hw_ptr_error(substream, reason)
#endif
+#define STRING_LENGTH_OF_INT 12
+#define MAX_USR_CTRL_CNT 128
+
/*
* fill ring buffer with silence
* runtime->silence_start: starting pointer to silence area
@@ -374,7 +377,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
* the elapsed time to detect xruns.
*/
jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
- if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
+ if ((jdelta < runtime->hw_ptr_buffer_jiffies / 2) ||
+ (runtime->hw_ptr_buffer_jiffies <= 0))
goto no_delta_check;
hdelta = jdelta - delta * HZ / runtime->rate;
xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1;
@@ -1798,6 +1802,11 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
switch (runtime->access) {
case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
+ if ((UINT_MAX/width) < info->channel) {
+ snd_printd("%s: integer overflow while multiply\n",
+ __func__);
+ return -EINVAL;
+ }
info->first = info->channel * width;
info->step = runtime->channels * width;
break;
@@ -1805,6 +1814,12 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED:
{
size_t size = runtime->dma_bytes / runtime->channels;
+
+ if ((size > 0) && ((UINT_MAX/(size * 8)) < info->channel)) {
+ snd_printd("%s: integer overflow while multiply\n",
+ __func__);
+ return -EINVAL;
+ }
info->first = info->channel * size * 8;
info->step = width;
break;
@@ -2125,6 +2140,9 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime;
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
+ /* TODO: consider and -EINVAL here */
+ if (substream->hw_no_buffer)
+ snd_printd("%s: warning this PCM is host less\n", __func__);
runtime = substream->runtime;
if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
return -EINVAL;
@@ -2575,6 +2593,23 @@ static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
kfree(info);
}
+static int pcm_volume_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0x2000;
+ return 0;
+}
+
+static void pcm_volume_ctl_private_free(struct snd_kcontrol *kcontrol)
+{
+ struct snd_pcm_volume *info = snd_kcontrol_chip(kcontrol);
+ info->pcm->streams[info->stream].vol_kctl = NULL;
+ kfree(info);
+}
+
/**
* snd_pcm_add_chmap_ctls - create channel-mapping control elements
* @pcm: the assigned PCM instance
@@ -2634,3 +2669,166 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
return 0;
}
EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls);
+
+/**
+ * snd_pcm_add_volume_ctls - create volume control elements
+ * @pcm: the assigned PCM instance
+ * @stream: stream direction
+ * @max_length: the max length of the volume parameter of stream
+ * @private_value: the value passed to each kcontrol's private_value field
+ * @info_ret: store struct snd_pcm_volume instance if non-NULL
+ *
+ * Create volume control elements assigned to the given PCM stream(s).
+ * Returns zero if succeed, or a negative error value.
+ */
+int snd_pcm_add_volume_ctls(struct snd_pcm *pcm, int stream,
+ const struct snd_pcm_volume_elem *volume,
+ int max_length,
+ unsigned long private_value,
+ struct snd_pcm_volume **info_ret)
+{
+ struct snd_pcm_volume *info;
+ struct snd_kcontrol_new knew = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = pcm_volume_ctl_info,
+ };
+ int err;
+ int size;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->pcm = pcm;
+ info->stream = stream;
+ info->volume = volume;
+ info->max_length = max_length;
+ size = sizeof("Playback ") + sizeof(" Volume") +
+ STRING_LENGTH_OF_INT*sizeof(char) + 1;
+ knew.name = kzalloc(size, GFP_KERNEL);
+ if (!knew.name) {
+ kfree(info);
+ return -ENOMEM;
+ }
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snprintf((char *)knew.name, size, "%s %d %s",
+ "Playback", pcm->device, "Volume");
+ else
+ snprintf((char *)knew.name, size, "%s %d %s",
+ "Capture", pcm->device, "Volume");
+ knew.device = pcm->device;
+ knew.count = pcm->streams[stream].substream_count;
+ knew.private_value = private_value;
+ info->kctl = snd_ctl_new1(&knew, info);
+ if (!info->kctl) {
+ kfree(info);
+ kfree(knew.name);
+ return -ENOMEM;
+ }
+ info->kctl->private_free = pcm_volume_ctl_private_free;
+ err = snd_ctl_add(pcm->card, info->kctl);
+ if (err < 0) {
+ kfree(info);
+ kfree(knew.name);
+ return -ENOMEM;
+ }
+ pcm->streams[stream].vol_kctl = info->kctl;
+ if (info_ret)
+ *info_ret = info;
+ kfree(knew.name);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_add_volume_ctls);
+
+static int pcm_usr_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = MAX_USR_CTRL_CNT;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = INT_MAX;
+ return 0;
+}
+
+static void pcm_usr_ctl_private_free(struct snd_kcontrol *kcontrol)
+{
+ struct snd_pcm_usr *info = snd_kcontrol_chip(kcontrol);
+ info->pcm->streams[info->stream].usr_kctl = NULL;
+ kfree(info);
+}
+
+/**
+ * snd_pcm_add_usr_ctls - create user control elements
+ * @pcm: the assigned PCM instance
+ * @stream: stream direction
+ * @max_length: the max length of the user parameter of stream
+ * @private_value: the value passed to each kcontrol's private_value field
+ * @info_ret: store struct snd_pcm_usr instance if non-NULL
+ *
+ * Create usr control elements assigned to the given PCM stream(s).
+ * Returns zero if succeed, or a negative error value.
+ */
+int snd_pcm_add_usr_ctls(struct snd_pcm *pcm, int stream,
+ const struct snd_pcm_usr_elem *usr,
+ int max_length, int max_kctrl_str_len,
+ unsigned long private_value,
+ struct snd_pcm_usr **info_ret)
+{
+ struct snd_pcm_usr *info;
+ struct snd_kcontrol_new knew = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = pcm_usr_ctl_info,
+ };
+ int err;
+ char *buf;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ pr_err("%s: snd_pcm_usr alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ info->pcm = pcm;
+ info->stream = stream;
+ info->usr = usr;
+ info->max_length = max_length;
+ buf = kzalloc(max_kctrl_str_len, GFP_KERNEL);
+ if (!buf) {
+ pr_err("%s: buffer allocation failed\n", __func__);
+ kfree(info);
+ return -ENOMEM;
+ }
+ knew.name = buf;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snprintf(buf, max_kctrl_str_len, "%s %d %s",
+ "Playback", pcm->device, "User kcontrol");
+ else
+ snprintf(buf, max_kctrl_str_len, "%s %d %s",
+ "Capture", pcm->device, "User kcontrol");
+ knew.device = pcm->device;
+ knew.count = pcm->streams[stream].substream_count;
+ knew.private_value = private_value;
+ info->kctl = snd_ctl_new1(&knew, info);
+ if (!info->kctl) {
+ kfree(info);
+ kfree(knew.name);
+ pr_err("%s: snd_ctl_new failed\n", __func__);
+ return -ENOMEM;
+ }
+ info->kctl->private_free = pcm_usr_ctl_private_free;
+ err = snd_ctl_add(pcm->card, info->kctl);
+ if (err < 0) {
+ kfree(info);
+ kfree(knew.name);
+ pr_err("%s: snd_ctl_add failed:%d\n", __func__,
+ err);
+ return -ENOMEM;
+ }
+ pcm->streams[stream].usr_kctl = info->kctl;
+ if (info_ret)
+ *info_ret = info;
+ kfree(knew.name);
+ return 0;
+}
+EXPORT_SYMBOL(snd_pcm_add_usr_ctls);
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index ebe8444de6c6..959f3a181ed2 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -168,7 +168,9 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = {
.le = -1, .signd = -1,
},
[SNDRV_PCM_FORMAT_SPECIAL] = {
- .le = -1, .signd = -1,
+ /* set the width and phys same as S16_LE */
+ .width = 16, .phys = 16, .le = -1, .signd = -1,
+ .silence = {},
},
[SNDRV_PCM_FORMAT_S24_3LE] = {
.width = 24, .phys = 24, .le = 1, .signd = 1,
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 8c0887d597bc..9408bf8fb822 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -29,6 +29,7 @@
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/compress_offload.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -197,7 +198,6 @@ static inline void snd_leave_user(mm_segment_t fs)
int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
{
- struct snd_pcm_runtime *runtime;
struct snd_pcm *pcm = substream->pcm;
struct snd_pcm_str *pstr = substream->pstr;
@@ -213,7 +213,6 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
info->subdevices_count = pstr->substream_count;
info->subdevices_avail = pstr->substream_count - pstr->substream_opened;
strlcpy(info->subname, substream->name, sizeof(info->subname));
- runtime = substream->runtime;
return 0;
}
@@ -584,7 +583,8 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
runtime->silence_threshold = 0;
runtime->silence_size = 0;
runtime->boundary = runtime->buffer_size;
- while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
+ while (runtime->boundary * 2 * runtime->channels <=
+ LONG_MAX - runtime->buffer_size)
runtime->boundary *= 2;
/* clear the buffer for avoiding possible kernel info leaks */
@@ -653,7 +653,9 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
if (substream->ops->hw_free)
result = substream->ops->hw_free(substream);
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
- pm_qos_remove_request(&substream->latency_pm_qos_req);
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
+
return result;
}
@@ -1035,6 +1037,7 @@ static int snd_pcm_pre_start(struct snd_pcm_substream *substream, int state)
if (runtime->status->state != SNDRV_PCM_STATE_PREPARED)
return -EBADFD;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ !substream->hw_no_buffer &&
!snd_pcm_playback_data(substream))
return -EPIPE;
runtime->trigger_tstamp_latched = false;
@@ -1088,6 +1091,33 @@ int snd_pcm_start(struct snd_pcm_substream *substream)
SNDRV_PCM_STATE_RUNNING);
}
+static int snd_compressed_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void __user *arg)
+{
+ struct snd_pcm_runtime *runtime;
+ int err = 0;
+
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
+ runtime = substream->runtime;
+ pr_debug("%s called with cmd = %d\n", __func__, cmd);
+ err = substream->ops->ioctl(substream, cmd, arg);
+ return err;
+}
+
+static int snd_user_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void __user *arg)
+{
+ struct snd_pcm_runtime *runtime;
+ int err = 0;
+
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
+ runtime = substream->runtime;
+ err = substream->ops->ioctl(substream, cmd, arg);
+ return err;
+}
+
/*
* stop callbacks
*/
@@ -2007,7 +2037,8 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
#endif
static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
- 48000, 64000, 88200, 96000, 176400, 192000 };
+ 48000, 64000, 88200, 96000, 176400, 192000,
+ 352800, 384000 };
const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
.count = ARRAY_SIZE(rates),
@@ -2723,6 +2754,7 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
volatile struct snd_pcm_mmap_status *status;
volatile struct snd_pcm_mmap_control *control;
int err;
+ snd_pcm_uframes_t hw_avail;
memset(&sync_ptr, 0, sizeof(sync_ptr));
if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags)))
@@ -2745,6 +2777,16 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
control->avail_min = sync_ptr.c.control.avail_min;
else
sync_ptr.c.control.avail_min = control->avail_min;
+
+ if (runtime->render_flag & SNDRV_NON_DMA_MODE) {
+ hw_avail = snd_pcm_playback_hw_avail(runtime);
+ if ((hw_avail >= runtime->start_threshold)
+ && (runtime->render_flag &
+ SNDRV_RENDER_STOPPED)) {
+ if (substream->ops->restart)
+ substream->ops->restart(substream);
+ }
+ }
sync_ptr.s.status.state = status->state;
sync_ptr.s.status.hw_ptr = status->hw_ptr;
sync_ptr.s.status.tstamp = status->tstamp;
@@ -2834,6 +2876,16 @@ static int snd_pcm_common_ioctl1(struct file *file,
snd_pcm_stream_unlock_irq(substream);
return res;
}
+ case SNDRV_COMPRESS_GET_CAPS:
+ case SNDRV_COMPRESS_GET_CODEC_CAPS:
+ case SNDRV_COMPRESS_SET_PARAMS:
+ case SNDRV_COMPRESS_GET_PARAMS:
+ case SNDRV_COMPRESS_TSTAMP:
+ case SNDRV_COMPRESS_DRAIN:
+ return snd_compressed_ioctl(substream, cmd, arg);
+ default:
+ if (((cmd >> 8) & 0xff) == 'U')
+ return snd_user_ioctl(substream, cmd, arg);
}
pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd);
return -ENOTTY;
@@ -3003,10 +3055,12 @@ static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct snd_pcm_file *pcm_file;
+ unsigned char ioctl_magic;
pcm_file = file->private_data;
+ ioctl_magic = ((cmd >> 8) & 0xff);
- if (((cmd >> 8) & 0xff) != 'A')
+ if (ioctl_magic != 'A' && ioctl_magic != 'C' && ioctl_magic != 'U')
return -ENOTTY;
return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,
@@ -3017,10 +3071,12 @@ static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct snd_pcm_file *pcm_file;
+ unsigned char ioctl_magic;
pcm_file = file->private_data;
+ ioctl_magic = ((cmd >> 8) & 0xff);
- if (((cmd >> 8) & 0xff) != 'A')
+ if (ioctl_magic != 'A' && ioctl_magic != 'U')
return -ENOTTY;
return snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd,
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index 20ecd8f18080..11ea73f019ba 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -65,9 +65,16 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)
static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer)
{
struct snd_pcm_substream *substream;
-
+ unsigned long ret = 0, flags = 0;
+
substream = timer->private_data;
- return substream->runtime ? substream->runtime->timer_resolution : 0;
+ spin_lock_irqsave(&substream->runtime_lock, flags);
+ if (substream->runtime)
+ ret = substream->runtime->timer_resolution;
+ else
+ ret = 0;
+ spin_unlock_irqrestore(&substream->runtime_lock, flags);
+ return ret;
}
static int snd_pcm_timer_start(struct snd_timer * timer)
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 481c1ad1db57..11fdc1d9797e 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -127,6 +127,7 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
return -ENOMEM;
runtime->substream = substream;
spin_lock_init(&runtime->lock);
+ mutex_init(&runtime->realloc_mutex);
init_waitqueue_head(&runtime->sleep);
INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work);
runtime->event = NULL;
@@ -648,9 +649,11 @@ static int snd_rawmidi_info_select_user(struct snd_card *card,
int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
struct snd_rawmidi_params * params)
{
- char *newbuf, *oldbuf;
+ char *newbuf;
+ char *oldbuf;
struct snd_rawmidi_runtime *runtime = substream->runtime;
-
+ unsigned long flags;
+
if (substream->append && substream->use_count > 1)
return -EBUSY;
snd_rawmidi_drain_output(substream);
@@ -661,22 +664,22 @@ int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
return -EINVAL;
}
if (params->buffer_size != runtime->buffer_size) {
- newbuf = kzalloc(params->buffer_size, GFP_KERNEL);
- if (!newbuf)
+ mutex_lock(&runtime->realloc_mutex);
+ newbuf = __krealloc(runtime->buffer, params->buffer_size,
+ GFP_KERNEL);
+ if (!newbuf) {
+ mutex_unlock(&runtime->realloc_mutex);
return -ENOMEM;
- spin_lock_irq(&runtime->lock);
- if (runtime->buffer_ref) {
- spin_unlock_irq(&runtime->lock);
- kfree(newbuf);
- return -EBUSY;
}
+ spin_lock_irqsave(&runtime->lock, flags);
oldbuf = runtime->buffer;
runtime->buffer = newbuf;
runtime->buffer_size = params->buffer_size;
runtime->avail = runtime->buffer_size;
- runtime->appl_ptr = runtime->hw_ptr = 0;
- spin_unlock_irq(&runtime->lock);
- kfree(oldbuf);
+ spin_unlock_irqrestore(&runtime->lock, flags);
+ if (oldbuf != newbuf)
+ kfree(oldbuf);
+ mutex_unlock(&runtime->realloc_mutex);
}
runtime->avail_min = params->avail_min;
substream->active_sensing = !params->no_active_sensing;
@@ -687,8 +690,10 @@ EXPORT_SYMBOL(snd_rawmidi_output_params);
int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
struct snd_rawmidi_params * params)
{
- char *newbuf, *oldbuf;
+ char *newbuf;
+ char *oldbuf;
struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned long flags;
snd_rawmidi_drain_input(substream);
if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
@@ -698,16 +703,21 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
return -EINVAL;
}
if (params->buffer_size != runtime->buffer_size) {
- newbuf = kmalloc(params->buffer_size, GFP_KERNEL);
- if (!newbuf)
+ mutex_lock(&runtime->realloc_mutex);
+ newbuf = __krealloc(runtime->buffer, params->buffer_size,
+ GFP_KERNEL);
+ if (!newbuf) {
+ mutex_unlock(&runtime->realloc_mutex);
return -ENOMEM;
- spin_lock_irq(&runtime->lock);
+ }
+ spin_lock_irqsave(&runtime->lock, flags);
oldbuf = runtime->buffer;
runtime->buffer = newbuf;
runtime->buffer_size = params->buffer_size;
- runtime->appl_ptr = runtime->hw_ptr = 0;
- spin_unlock_irq(&runtime->lock);
- kfree(oldbuf);
+ spin_unlock_irqrestore(&runtime->lock, flags);
+ if (oldbuf != newbuf)
+ kfree(oldbuf);
+ mutex_unlock(&runtime->realloc_mutex);
}
runtime->avail_min = params->avail_min;
return 0;
@@ -980,6 +990,8 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
unsigned long appl_ptr;
int err = 0;
+ if (userbuf)
+ mutex_lock(&runtime->realloc_mutex);
spin_lock_irqsave(&runtime->lock, flags);
snd_rawmidi_buffer_ref(runtime);
while (count > 0 && runtime->avail) {
@@ -1012,6 +1024,8 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
out:
snd_rawmidi_buffer_unref(runtime);
spin_unlock_irqrestore(&runtime->lock, flags);
+ if (userbuf)
+ mutex_unlock(&runtime->realloc_mutex);
return result > 0 ? result : err;
}
@@ -1276,10 +1290,14 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
return -EINVAL;
result = 0;
+ if (userbuf)
+ mutex_lock(&runtime->realloc_mutex);
spin_lock_irqsave(&runtime->lock, flags);
if (substream->append) {
if ((long)runtime->avail < count) {
spin_unlock_irqrestore(&runtime->lock, flags);
+ if (userbuf)
+ mutex_unlock(&runtime->realloc_mutex);
return -EAGAIN;
}
}
@@ -1317,6 +1335,8 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
count1 = runtime->avail < runtime->buffer_size;
snd_rawmidi_buffer_unref(runtime);
spin_unlock_irqrestore(&runtime->lock, flags);
+ if (userbuf)
+ mutex_unlock(&runtime->realloc_mutex);
if (count1)
snd_rawmidi_output_trigger(substream, 1);
return result;
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 7e4509de01c4..ce18099f95de 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1131,7 +1131,11 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
mutex_lock(&register_mutex);
list_for_each_entry(timer, &snd_timer_list, device_list) {
- if (timer->card && timer->card->shutdown)
+ if (timer->card == NULL) {
+ pr_debug("%s: timer->card is NULL\n", __func__);
+ continue;
+ }
+ if (timer->card->shutdown)
continue;
switch (timer->tmr_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 7ff7d88e46dd..aabd73bb74d2 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -51,6 +51,7 @@ source "sound/soc/nuc900/Kconfig"
source "sound/soc/omap/Kconfig"
source "sound/soc/kirkwood/Kconfig"
source "sound/soc/intel/Kconfig"
+source "sound/soc/msm/Kconfig"
source "sound/soc/mediatek/Kconfig"
source "sound/soc/mxs/Kconfig"
source "sound/soc/pxa/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 8eb06db32fa0..6c9e9d65546d 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_SND_SOC) += dwc/
obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += jz4740/
obj-$(CONFIG_SND_SOC) += intel/
+obj-$(CONFIG_SND_SOC) += msm/
obj-$(CONFIG_SND_SOC) += mediatek/
obj-$(CONFIG_SND_SOC) += mxs/
obj-$(CONFIG_SND_SOC) += nuc900/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index cfdafc4c11ea..8f3e787501f1 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -81,6 +81,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX9877 if I2C
select SND_SOC_MC13783 if MFD_MC13XXX
select SND_SOC_ML26124 if I2C
+ select SND_SOC_HDMI_CODEC
select SND_SOC_NAU8825 if I2C
select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM1792A if SPI_MASTER
@@ -454,6 +455,9 @@ config SND_SOC_BT_SCO
config SND_SOC_DMIC
tristate
+config SND_SOC_HDMI_CODEC
+ tristate "HDMI stub CODEC"
+
config SND_SOC_ES8328
tristate "Everest Semi ES8328 CODEC"
@@ -709,6 +713,71 @@ config SND_SOC_UDA134X
config SND_SOC_UDA1380
tristate
+config SND_SOC_WCD934X_DSD
+ tristate
+
+config SND_SOC_WCD9320
+ tristate
+
+config SND_SOC_WCD9330
+ tristate
+ depends on WCD9330_CODEC
+
+config SND_SOC_WCD9335
+ tristate
+ depends on WCD9335_CODEC
+
+config SND_SOC_WCD934X
+ tristate
+ depends on WCD934X_CODEC
+ select SND_SOC_WCD9XXX_V2
+ select AUDIO_EXT_CLK
+ select SND_SOC_WCD_DSP_MGR
+ select SND_SOC_WCD_SPI
+ select SND_SOC_WCD934X_MBHC
+ select SND_SOC_WCD934X_DSD
+
+config SND_SOC_WCD934X_MBHC
+ tristate
+ depends on SND_SOC_WCD934X
+ select SND_SOC_WCD_MBHC
+
+config SND_SOC_WSA881X
+ tristate
+ select MSM_CDC_PINCTRL
+ select REGMAP_SWR
+
+config SND_SOC_WSA881X_ANALOG
+ tristate
+ select REGMAP_I2C
+
+config SND_SOC_WCD9XXX
+ tristate
+ default y if SND_SOC_WCD9320=y || SND_SOC_WCD9330=y || SND_SOC_WCD9335=y
+
+config SND_SOC_WCD9XXX_V2
+ tristate
+ default y if SND_SOC_WCD9335=y
+
+config SND_SOC_WCD_CPE
+ tristate
+ default y if SND_SOC_WCD9330=y || SND_SOC_WCD9335=y
+
+config AUDIO_EXT_CLK
+ tristate
+ default y if SND_SOC_WCD9335=y || SND_SOC_WCD9330=y || SND_SOC_SDM660_CDC=y
+
+config SND_SOC_WCD_MBHC
+ tristate
+ default y if (SND_SOC_MSM8909_WCD=y || SND_SOC_SDM660_CDC=y || SND_SOC_WCD9335=y) && SND_SOC_MDMCALIFORNIUM!=y
+
+config SND_SOC_WCD_DSP_MGR
+ tristate
+
+config SND_SOC_WCD_SPI
+ depends on SPI
+ tristate
+
config SND_SOC_WL1273
tristate
@@ -915,4 +984,17 @@ config SND_SOC_TPA6130A2
tristate "Texas Instruments TPA6130A2 headphone amplifier"
depends on I2C
+config SND_SOC_MSM_STUB
+ tristate
+
+config SND_SOC_MSM_HDMI_CODEC_RX
+ bool "HDMI Audio Playback"
+ depends on FB_MSM_MDSS_HDMI_PANEL && (SND_SOC_APQ8084 || SND_SOC_MSM8994 || SND_SOC_MSM8996 || SND_SOC_MSM8998 || SND_SOC_SDM660_COMMON)
+ help
+ HDMI audio drivers should be built only if the platform
+ supports hdmi panel.
+
+source "sound/soc/codecs/sdm660_cdc/Kconfig"
+source "sound/soc/codecs/msm_sdw/Kconfig"
+
endmenu
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f632fc42f59f..5305cc6071e8 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -74,6 +74,7 @@ snd-soc-max98925-objs := max98925.o
snd-soc-max9850-objs := max9850.o
snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o
+snd-soc-hdmi-codec-objs := hdmi.o
snd-soc-nau8825-objs := nau8825.o
snd-soc-pcm1681-objs := pcm1681.o
snd-soc-pcm1792a-codec-objs := pcm1792a.o
@@ -129,6 +130,27 @@ snd-soc-twl4030-objs := twl4030.o
snd-soc-twl6040-objs := twl6040.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
+snd-soc-wcd9320-objs := wcd9320.o wcd9320-tables.o
+snd-soc-wcd9330-objs := wcd9330.o wcd9330-tables.o
+snd-soc-wcd9335-objs := wcd9335.o
+snd-soc-wcd934x-objs := wcd934x.o
+snd-soc-wcd9xxx-objs := wcd9xxx-resmgr.o wcd9xxx-mbhc.o wcd9xxx-common.o wcdcal-hwdep.o
+snd-soc-wcd9xxx-v2-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o
+ifeq ($(CONFIG_COMMON_CLK_MSM), y)
+ audio-ext-clock-objs := audio-ext-clk.o
+endif
+
+ifeq ($(CONFIG_COMMON_CLK_QCOM), y)
+ audio-ext-clock-up-objs := audio-ext-clk-up.o
+endif
+snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o
+snd-soc-wsa881x-objs := wsa881x.o wsa881x-tables.o wsa881x-regmap.o wsa881x-temp-sensor.o
+snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o
+snd-soc-wsa881x-analog-objs := wsa881x-analog.o wsa881x-tables-analog.o
+snd-soc-wsa881x-analog-objs += wsa881x-regmap-analog.o wsa881x-irq.o
+snd-soc-wcd-dsp-utils-objs := wcd-dsp-utils.o
+snd-soc-wcd-dsp-mgr-objs := wcd-dsp-mgr.o
+snd-soc-wcd-spi-objs := wcd-spi.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm-adsp-objs := wm_adsp.o
snd-soc-wm0010-objs := wm0010.o
@@ -185,6 +207,8 @@ snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
+snd-soc-msm-stub-objs := msm_stub.o
+obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) := msm_hdmi_codec_rx.o
# Amp
snd-soc-max9877-objs := max9877.o
@@ -269,6 +293,7 @@ obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
+obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
@@ -321,7 +346,25 @@ obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WCD9320) += snd-soc-wcd9320.o
+obj-$(CONFIG_SND_SOC_WCD9330) += snd-soc-wcd9330.o
+obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o
+obj-$(CONFIG_SND_SOC_WCD934X) += wcd934x/
+ifeq ($(CONFIG_COMMON_CLK_MSM), y)
+ obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clock.o
+endif
+ifeq ($(CONFIG_COMMON_CLK_QCOM), y)
+ obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clock-up.o
+endif
+obj-$(CONFIG_SND_SOC_WCD9XXX) += snd-soc-wcd9xxx.o
+obj-$(CONFIG_SND_SOC_WCD9XXX_V2) += snd-soc-wcd9xxx-v2.o
+obj-$(CONFIG_SND_SOC_WCD_CPE) += snd-soc-wcd-cpe.o
+obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o
+obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o
+obj-$(CONFIG_SND_SOC_WSA881X_ANALOG) += snd-soc-wsa881x-analog.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WCD_DSP_MGR) += snd-soc-wcd-dsp-mgr.o snd-soc-wcd-dsp-utils.o
+obj-$(CONFIG_SND_SOC_WCD_SPI) += snd-soc-wcd-spi.o
obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
@@ -377,7 +420,10 @@ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
+obj-$(CONFIG_SND_SOC_MSM_STUB) += snd-soc-msm-stub.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
+obj-y += sdm660_cdc/
+obj-y += msm_sdw/
diff --git a/sound/soc/codecs/audio-ext-clk-up.c b/sound/soc/codecs/audio-ext-clk-up.c
new file mode 100644
index 000000000000..cb230f38db22
--- /dev/null
+++ b/sound/soc/codecs/audio-ext-clk-up.c
@@ -0,0 +1,618 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include "../../../drivers/clk/qcom/common.h"
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <dt-bindings/clock/audio-ext-clk.h>
+#include <sound/q6afe-v2.h>
+
+enum audio_clk_mux {
+ AP_CLK2,
+ LPASS_MCLK,
+ LPASS_MCLK2,
+};
+
+struct pinctrl_info {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *sleep;
+ struct pinctrl_state *active;
+ char __iomem *base;
+};
+
+struct audio_ext_ap_clk {
+ bool enabled;
+ int gpio;
+ struct clk_fixed_factor fact;
+};
+
+struct audio_ext_pmi_clk {
+ int gpio;
+ struct clk_fixed_factor fact;
+};
+
+struct audio_ext_ap_clk2 {
+ bool enabled;
+ struct pinctrl_info pnctrl_info;
+ struct clk_fixed_factor fact;
+};
+
+struct audio_ext_lpass_mclk {
+ struct pinctrl_info pnctrl_info;
+ struct clk_fixed_factor fact;
+};
+
+static struct afe_clk_set clk2_config = {
+ Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
+ Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
+ Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static struct afe_clk_set lpass_default = {
+ Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
+ Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
+ Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static struct afe_clk_set lpass_mclk = {
+ Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
+ Q6AFE_LPASS_CLK_ID_MCLK_1,
+ Q6AFE_LPASS_OSR_CLK_11_P2896_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk_hw *hw)
+{
+ return container_of(hw, struct audio_ext_ap_clk, fact.hw);
+}
+
+static int audio_ext_clk_prepare(struct clk_hw *hw)
+{
+ struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw);
+
+ pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
+ if (gpio_is_valid(audio_clk->gpio))
+ return gpio_direction_output(audio_clk->gpio, 1);
+ return 0;
+}
+
+static void audio_ext_clk_unprepare(struct clk_hw *hw)
+{
+ struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw);
+
+ pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
+ if (gpio_is_valid(audio_clk->gpio))
+ gpio_direction_output(audio_clk->gpio, 0);
+}
+
+static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk_hw *hw)
+{
+ return container_of(hw, struct audio_ext_ap_clk2, fact.hw);
+}
+
+static int audio_ext_clk2_prepare(struct clk_hw *hw)
+{
+ struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw);
+ struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
+ int ret;
+
+
+ if (!pnctrl_info->pinctrl || !pnctrl_info->active)
+ return 0;
+
+ ret = pinctrl_select_state(pnctrl_info->pinctrl,
+ pnctrl_info->active);
+ if (ret) {
+ pr_err("%s: active state select failed with %d\n",
+ __func__, ret);
+ return -EIO;
+ }
+
+ clk2_config.enable = 1;
+ ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
+ if (ret < 0) {
+ pr_err("%s: failed to set clock, ret = %d\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void audio_ext_clk2_unprepare(struct clk_hw *hw)
+{
+ struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw);
+ struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
+ int ret;
+
+ if (!pnctrl_info->pinctrl || !pnctrl_info->sleep)
+ return;
+
+ ret = pinctrl_select_state(pnctrl_info->pinctrl,
+ pnctrl_info->sleep);
+ if (ret)
+ pr_err("%s: sleep state select failed with %d\n",
+ __func__, ret);
+
+ clk2_config.enable = 0;
+ ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
+ if (ret < 0)
+ pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret);
+}
+
+static inline struct audio_ext_lpass_mclk *to_audio_lpass_mclk(
+ struct clk_hw *hw)
+{
+ return container_of(hw, struct audio_ext_lpass_mclk, fact.hw);
+}
+
+static int audio_ext_lpass_mclk_prepare(struct clk_hw *hw)
+{
+ struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw);
+ struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info;
+ int ret;
+
+ if (pnctrl_info->pinctrl) {
+ ret = pinctrl_select_state(pnctrl_info->pinctrl,
+ pnctrl_info->active);
+ if (ret) {
+ pr_err("%s: active state select failed with %d\n",
+ __func__, ret);
+ return -EIO;
+ }
+ }
+
+ lpass_mclk.enable = 1;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX,
+ &lpass_mclk);
+ if (ret < 0) {
+ pr_err("%s afe_set_digital_codec_core_clock failed\n",
+ __func__);
+ return ret;
+ }
+
+ if (pnctrl_info->base)
+ iowrite32(1, pnctrl_info->base);
+ return 0;
+}
+
+static void audio_ext_lpass_mclk_unprepare(struct clk_hw *hw)
+{
+ struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw);
+ struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info;
+ int ret;
+
+ if (pnctrl_info->pinctrl) {
+ ret = pinctrl_select_state(pnctrl_info->pinctrl,
+ pnctrl_info->sleep);
+ if (ret) {
+ pr_err("%s: active state select failed with %d\n",
+ __func__, ret);
+ return;
+ }
+ }
+
+ lpass_mclk.enable = 0;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX,
+ &lpass_mclk);
+ if (ret < 0)
+ pr_err("%s: afe_set_digital_codec_core_clock failed, ret = %d\n",
+ __func__, ret);
+ if (pnctrl_info->base)
+ iowrite32(0, pnctrl_info->base);
+}
+
+static int audio_ext_lpass_mclk2_prepare(struct clk_hw *hw)
+{
+ struct audio_ext_lpass_mclk *audio_lpass_mclk2 =
+ to_audio_lpass_mclk(hw);
+ struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info;
+ int ret;
+
+ if (pnctrl_info->pinctrl) {
+ ret = pinctrl_select_state(pnctrl_info->pinctrl,
+ pnctrl_info->active);
+ if (ret) {
+ pr_err("%s: active state select failed with %d\n",
+ __func__, ret);
+ return -EIO;
+ }
+ }
+
+ lpass_default.enable = 1;
+ ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default);
+ if (ret < 0) {
+ pr_err("%s: failed to set clock, ret = %d\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void audio_ext_lpass_mclk2_unprepare(struct clk_hw *hw)
+{
+ struct audio_ext_lpass_mclk *audio_lpass_mclk2 =
+ to_audio_lpass_mclk(hw);
+ struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info;
+ int ret;
+
+ if (pnctrl_info->pinctrl) {
+ ret = pinctrl_select_state(pnctrl_info->pinctrl,
+ pnctrl_info->sleep);
+ if (ret)
+ pr_err("%s: sleep state select failed with %d\n",
+ __func__, ret);
+ }
+
+ lpass_default.enable = 0;
+ ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default);
+ if (ret < 0)
+ pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret);
+}
+
+static struct clk_ops audio_ext_ap_clk_ops = {
+ .prepare = audio_ext_clk_prepare,
+ .unprepare = audio_ext_clk_unprepare,
+};
+
+static struct clk_ops audio_ext_ap_clk2_ops = {
+ .prepare = audio_ext_clk2_prepare,
+ .unprepare = audio_ext_clk2_unprepare,
+};
+
+static struct clk_ops audio_ext_lpass_mclk_ops = {
+ .prepare = audio_ext_lpass_mclk_prepare,
+ .unprepare = audio_ext_lpass_mclk_unprepare,
+};
+
+static struct clk_ops audio_ext_lpass_mclk2_ops = {
+ .prepare = audio_ext_lpass_mclk2_prepare,
+ .unprepare = audio_ext_lpass_mclk2_unprepare,
+};
+
+static struct audio_ext_pmi_clk audio_pmi_clk = {
+ .gpio = -EINVAL,
+ .fact = {
+ .mult = 1,
+ .div = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "audio_ext_pmi_clk",
+ .parent_names = (const char *[]){ "div_clk1" },
+ .num_parents = 1,
+ .ops = &clk_dummy_ops,
+ },
+ },
+};
+
+static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = {
+ .gpio = -EINVAL,
+ .fact = {
+ .mult = 1,
+ .div = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "audio_ext_pmi_lnbb_clk",
+ .parent_names = (const char *[]){ "ln_bb_clk2" },
+ .num_parents = 1,
+ .ops = &clk_dummy_ops,
+ },
+ },
+};
+
+static struct audio_ext_ap_clk audio_ap_clk = {
+ .gpio = -EINVAL,
+ .fact = {
+ .mult = 1,
+ .div = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "audio_ap_clk",
+ .ops = &audio_ext_ap_clk_ops,
+ },
+ },
+};
+
+static struct audio_ext_ap_clk2 audio_ap_clk2 = {
+ .enabled = false,
+ .pnctrl_info = {NULL},
+ .fact = {
+ .mult = 1,
+ .div = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "audio_ap_clk2",
+ .ops = &audio_ext_ap_clk2_ops,
+ },
+ },
+};
+
+static struct audio_ext_lpass_mclk audio_lpass_mclk = {
+ .pnctrl_info = {NULL},
+ .fact = {
+ .mult = 1,
+ .div = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "audio_lpass_mclk",
+ .ops = &audio_ext_lpass_mclk_ops,
+ },
+ },
+};
+
+static struct audio_ext_lpass_mclk audio_lpass_mclk2 = {
+ .pnctrl_info = {NULL},
+ .fact = {
+ .mult = 1,
+ .div = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "audio_lpass_mclk2",
+ .ops = &audio_ext_lpass_mclk2_ops,
+ },
+ },
+};
+
+static struct clk_hw *audio_msm_hws[] = {
+ &audio_pmi_clk.fact.hw,
+ &audio_ap_clk.fact.hw,
+ &audio_ap_clk2.fact.hw,
+ &audio_lpass_mclk.fact.hw,
+ &audio_lpass_mclk2.fact.hw,
+};
+
+static struct clk_hw *audio_msm_hws1[] = {
+ &audio_pmi_lnbb_clk.fact.hw,
+};
+
+static int audio_get_pinctrl(struct platform_device *pdev,
+ enum audio_clk_mux mux)
+{
+ struct device *dev = &pdev->dev;
+ struct pinctrl_info *pnctrl_info;
+ struct pinctrl *pinctrl;
+ int ret;
+ u32 reg;
+
+ switch (mux) {
+ case AP_CLK2:
+ pnctrl_info = &audio_ap_clk2.pnctrl_info;
+ break;
+ case LPASS_MCLK:
+ pnctrl_info = &audio_lpass_mclk.pnctrl_info;
+ break;
+ case LPASS_MCLK2:
+ pnctrl_info = &audio_lpass_mclk2.pnctrl_info;
+ break;
+ default:
+ dev_err(dev, "%s Not a valid MUX ID: %d\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+
+ if (pnctrl_info->pinctrl) {
+ dev_dbg(dev, "%s: already requested before\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR_OR_NULL(pinctrl)) {
+ dev_dbg(dev, "%s: Unable to get pinctrl handle\n",
+ __func__);
+ return -EINVAL;
+ }
+ pnctrl_info->pinctrl = pinctrl;
+ /* get all state handles from Device Tree */
+ pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep");
+ if (IS_ERR(pnctrl_info->sleep)) {
+ dev_err(dev, "%s: could not get sleep pinstate\n",
+ __func__);
+ goto err;
+ }
+ pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active");
+ if (IS_ERR(pnctrl_info->active)) {
+ dev_err(dev, "%s: could not get active pinstate\n",
+ __func__);
+ goto err;
+ }
+ /* Reset the TLMM pins to a default state */
+ ret = pinctrl_select_state(pnctrl_info->pinctrl,
+ pnctrl_info->sleep);
+ if (ret) {
+ dev_err(dev, "%s: Disable TLMM pins failed with %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "qcom,mclk-clk-reg", &reg);
+ if (ret < 0) {
+ dev_dbg(dev, "miss mclk reg\n");
+ } else {
+ pnctrl_info->base = ioremap(reg, sizeof(u32));
+ if (pnctrl_info->base == NULL) {
+ dev_err(dev, "%s ioremap failed\n", __func__);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ devm_pinctrl_put(pnctrl_info->pinctrl);
+ return -EINVAL;
+}
+
+static int audio_ref_clk_probe(struct platform_device *pdev)
+{
+ int clk_gpio;
+ int ret;
+ u32 mclk_freq;
+ struct clk *audio_clk;
+ struct device *dev = &pdev->dev;
+ int i;
+ struct clk_onecell_data *clk_data;
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,codec-mclk-clk-freq",
+ &mclk_freq);
+ if (!ret) {
+ lpass_mclk.clk_freq_in_hz = mclk_freq;
+
+ ret = audio_get_pinctrl(pdev, LPASS_MCLK);
+ if (ret)
+ dev_err(&pdev->dev, "%s: Parsing pinctrl %s failed\n",
+ __func__, "LPASS_MCLK");
+ ret = audio_get_pinctrl(pdev, LPASS_MCLK2);
+ if (ret)
+ dev_dbg(&pdev->dev, "%s: Parsing pinctrl %s failed\n",
+ __func__, "LPASS_MCLK2");
+ }
+
+ clk_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,audio-ref-clk-gpio", 0);
+ if (clk_gpio > 0) {
+ ret = gpio_request(clk_gpio, "EXT_CLK");
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Request ext clk gpio failed %d, err:%d\n",
+ clk_gpio, ret);
+ goto err;
+ }
+ if (of_property_read_bool(pdev->dev.of_node,
+ "qcom,node_has_rpm_clock")) {
+ audio_pmi_clk.gpio = clk_gpio;
+ } else
+ audio_ap_clk.gpio = clk_gpio;
+
+ }
+
+ ret = audio_get_pinctrl(pdev, AP_CLK2);
+ if (ret)
+ dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n",
+ __func__);
+
+ clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ goto err_gpio;
+
+ clk_data->clk_num = ARRAY_SIZE(audio_msm_hws);
+ clk_data->clks = devm_kzalloc(&pdev->dev,
+ clk_data->clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+ if (!clk_data->clks)
+ goto err_clk;
+
+
+ clk_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,audio-ref-clk-gpio", 0);
+ if (clk_gpio > 0) {
+ for (i = 0; i < ARRAY_SIZE(audio_msm_hws); i++) {
+ audio_clk = devm_clk_register(dev, audio_msm_hws[i]);
+ if (IS_ERR(audio_clk)) {
+ dev_err(&pdev->dev,
+ "%s: ref clock: %d register failed\n",
+ __func__, i);
+ return PTR_ERR(audio_clk);
+ }
+ clk_data->clks[i] = audio_clk;
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(audio_msm_hws1); i++) {
+ audio_clk = devm_clk_register(dev, audio_msm_hws1[i]);
+ if (IS_ERR(audio_clk)) {
+ dev_err(&pdev->dev,
+ "%s: ref clock: %d register failed\n",
+ __func__, i);
+ return PTR_ERR(audio_clk);
+ }
+ clk_data->clks[i] = audio_clk;
+ }
+ }
+
+ ret = of_clk_add_provider(pdev->dev.of_node,
+ of_clk_src_onecell_get, clk_data);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: audio ref clock register failed\n",
+ __func__);
+ goto err_gpio;
+ }
+
+ return 0;
+
+err_clk:
+ if (clk_data)
+ devm_kfree(&pdev->dev, clk_data->clks);
+ devm_kfree(&pdev->dev, clk_data);
+err_gpio:
+ gpio_free(clk_gpio);
+
+err:
+ return ret;
+}
+
+static int audio_ref_clk_remove(struct platform_device *pdev)
+{
+ struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info;
+
+ if (audio_pmi_clk.gpio > 0)
+ gpio_free(audio_pmi_clk.gpio);
+ else if (audio_ap_clk.gpio > 0)
+ gpio_free(audio_ap_clk.gpio);
+
+ if (pnctrl_info->pinctrl) {
+ devm_pinctrl_put(pnctrl_info->pinctrl);
+ pnctrl_info->pinctrl = NULL;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id audio_ref_clk_match[] = {
+ {.compatible = "qcom,audio-ref-clk"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, audio_ref_clk_match);
+
+static struct platform_driver audio_ref_clk_driver = {
+ .driver = {
+ .name = "audio-ref-clk",
+ .owner = THIS_MODULE,
+ .of_match_table = audio_ref_clk_match,
+ },
+ .probe = audio_ref_clk_probe,
+ .remove = audio_ref_clk_remove,
+};
+
+static int __init audio_ref_clk_platform_init(void)
+{
+ return platform_driver_register(&audio_ref_clk_driver);
+}
+module_init(audio_ref_clk_platform_init);
+
+static void __exit audio_ref_clk_platform_exit(void)
+{
+ platform_driver_unregister(&audio_ref_clk_driver);
+}
+module_exit(audio_ref_clk_platform_exit);
+
+MODULE_DESCRIPTION("Audio Ref Up Clock module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/audio-ext-clk.c b/sound/soc/codecs/audio-ext-clk.c
new file mode 100644
index 000000000000..34b1d9481457
--- /dev/null
+++ b/sound/soc/codecs/audio-ext-clk.c
@@ -0,0 +1,349 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <dt-bindings/clock/audio-ext-clk.h>
+#include <sound/q6afe-v2.h>
+
+struct pinctrl_info {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *sleep;
+ struct pinctrl_state *active;
+};
+
+struct audio_ext_ap_clk {
+ bool enabled;
+ int gpio;
+ struct clk c;
+};
+
+struct audio_ext_pmi_clk {
+ int gpio;
+ struct clk c;
+};
+
+struct audio_ext_ap_clk2 {
+ bool enabled;
+ struct pinctrl_info pnctrl_info;
+ struct clk c;
+};
+
+static struct afe_clk_set clk2_config = {
+ Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
+ Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
+ Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk *clk)
+{
+ return container_of(clk, struct audio_ext_ap_clk, c);
+}
+
+static int audio_ext_clk_prepare(struct clk *clk)
+{
+ struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk);
+
+ pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
+ if (gpio_is_valid(audio_clk->gpio))
+ return gpio_direction_output(audio_clk->gpio, 1);
+ return 0;
+}
+
+static void audio_ext_clk_unprepare(struct clk *clk)
+{
+ struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk);
+
+ pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
+ if (gpio_is_valid(audio_clk->gpio))
+ gpio_direction_output(audio_clk->gpio, 0);
+}
+
+static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk *clk)
+{
+ return container_of(clk, struct audio_ext_ap_clk2, c);
+}
+
+static int audio_ext_clk2_prepare(struct clk *clk)
+{
+ struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk);
+ struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
+ int ret;
+
+
+ if (!pnctrl_info->pinctrl || !pnctrl_info->active)
+ return 0;
+
+ ret = pinctrl_select_state(pnctrl_info->pinctrl,
+ pnctrl_info->active);
+ if (ret) {
+ pr_err("%s: active state select failed with %d\n",
+ __func__, ret);
+ return -EIO;
+ }
+
+ clk2_config.enable = 1;
+ ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
+ if (ret < 0) {
+ pr_err("%s: failed to set clock, ret = %d\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void audio_ext_clk2_unprepare(struct clk *clk)
+{
+ struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk);
+ struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
+ int ret;
+
+ if (!pnctrl_info->pinctrl || !pnctrl_info->sleep)
+ return;
+
+ ret = pinctrl_select_state(pnctrl_info->pinctrl,
+ pnctrl_info->sleep);
+ if (ret)
+ pr_err("%s: sleep state select failed with %d\n",
+ __func__, ret);
+
+ clk2_config.enable = 0;
+ ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
+ if (ret < 0)
+ pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret);
+}
+
+static struct clk_ops audio_ext_ap_clk_ops = {
+ .prepare = audio_ext_clk_prepare,
+ .unprepare = audio_ext_clk_unprepare,
+};
+
+static struct clk_ops audio_ext_ap_clk2_ops = {
+ .prepare = audio_ext_clk2_prepare,
+ .unprepare = audio_ext_clk2_unprepare,
+};
+
+static struct audio_ext_pmi_clk audio_pmi_clk = {
+ .gpio = -EINVAL,
+ .c = {
+ .dbg_name = "audio_ext_pmi_clk",
+ .ops = &clk_ops_dummy,
+ CLK_INIT(audio_pmi_clk.c),
+ },
+};
+
+static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = {
+ .gpio = -EINVAL,
+ .c = {
+ .dbg_name = "audio_ext_pmi_lnbb_clk",
+ .ops = &clk_ops_dummy,
+ CLK_INIT(audio_pmi_lnbb_clk.c),
+ },
+};
+
+static struct audio_ext_ap_clk audio_ap_clk = {
+ .gpio = -EINVAL,
+ .c = {
+ .dbg_name = "audio_ext_ap_clk",
+ .ops = &audio_ext_ap_clk_ops,
+ CLK_INIT(audio_ap_clk.c),
+ },
+};
+
+static struct audio_ext_ap_clk2 audio_ap_clk2 = {
+ .c = {
+ .dbg_name = "audio_ext_ap_clk2",
+ .ops = &audio_ext_ap_clk2_ops,
+ CLK_INIT(audio_ap_clk2.c),
+ },
+};
+
+static struct clk_lookup audio_ref_clock[] = {
+ CLK_LIST(audio_ap_clk),
+ CLK_LIST(audio_pmi_clk),
+ CLK_LIST(audio_pmi_lnbb_clk),
+ CLK_LIST(audio_ap_clk2),
+};
+
+static int audio_get_pinctrl(struct platform_device *pdev)
+{
+ struct pinctrl_info *pnctrl_info;
+ struct pinctrl *pinctrl;
+ int ret;
+
+ pnctrl_info = &audio_ap_clk2.pnctrl_info;
+
+ if (pnctrl_info->pinctrl) {
+ dev_dbg(&pdev->dev, "%s: already requested before\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR_OR_NULL(pinctrl)) {
+ dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n",
+ __func__);
+ return -EINVAL;
+ }
+ pnctrl_info->pinctrl = pinctrl;
+ /* get all state handles from Device Tree */
+ pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep");
+ if (IS_ERR(pnctrl_info->sleep)) {
+ dev_err(&pdev->dev, "%s: could not get sleep pinstate\n",
+ __func__);
+ goto err;
+ }
+ pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active");
+ if (IS_ERR(pnctrl_info->active)) {
+ dev_err(&pdev->dev, "%s: could not get active pinstate\n",
+ __func__);
+ goto err;
+ }
+ /* Reset the TLMM pins to a default state */
+ ret = pinctrl_select_state(pnctrl_info->pinctrl,
+ pnctrl_info->sleep);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: Disable TLMM pins failed with %d\n",
+ __func__, ret);
+ goto err;
+ }
+ return 0;
+
+err:
+ devm_pinctrl_put(pnctrl_info->pinctrl);
+ return -EINVAL;
+}
+
+static int audio_ref_clk_probe(struct platform_device *pdev)
+{
+ int clk_gpio;
+ int ret;
+ struct clk *audio_clk;
+
+ clk_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,audio-ref-clk-gpio", 0);
+ if (clk_gpio > 0) {
+ ret = gpio_request(clk_gpio, "EXT_CLK");
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Request ext clk gpio failed %d, err:%d\n",
+ clk_gpio, ret);
+ goto err;
+ }
+ if (of_property_read_bool(pdev->dev.of_node,
+ "qcom,node_has_rpm_clock")) {
+ audio_clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(audio_clk)) {
+ dev_err(&pdev->dev, "Failed to get RPM div clk\n");
+ ret = PTR_ERR(audio_clk);
+ goto err_gpio;
+ }
+ audio_pmi_clk.c.parent = audio_clk;
+ audio_pmi_clk.gpio = clk_gpio;
+ } else
+ audio_ap_clk.gpio = clk_gpio;
+
+ } else {
+ if (of_property_read_bool(pdev->dev.of_node,
+ "qcom,node_has_rpm_clock")) {
+ audio_clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(audio_clk)) {
+ dev_err(&pdev->dev, "Failed to get lnbbclk2\n");
+ ret = PTR_ERR(audio_clk);
+ goto err;
+ }
+ audio_pmi_lnbb_clk.c.parent = audio_clk;
+ audio_pmi_lnbb_clk.gpio = -EINVAL;
+ }
+ }
+
+ ret = audio_get_pinctrl(pdev);
+ if (ret)
+ dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n",
+ __func__);
+
+ ret = of_msm_clock_register(pdev->dev.of_node, audio_ref_clock,
+ ARRAY_SIZE(audio_ref_clock));
+ if (ret) {
+ dev_err(&pdev->dev, "%s: audio ref clock register failed\n",
+ __func__);
+ goto err_gpio;
+ }
+
+ return 0;
+
+err_gpio:
+ gpio_free(clk_gpio);
+
+err:
+ return ret;
+}
+
+static int audio_ref_clk_remove(struct platform_device *pdev)
+{
+ struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info;
+
+ if (audio_pmi_clk.gpio > 0)
+ gpio_free(audio_pmi_clk.gpio);
+ else if (audio_ap_clk.gpio > 0)
+ gpio_free(audio_ap_clk.gpio);
+
+ if (pnctrl_info->pinctrl) {
+ devm_pinctrl_put(pnctrl_info->pinctrl);
+ pnctrl_info->pinctrl = NULL;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id audio_ref_clk_match[] = {
+ {.compatible = "qcom,audio-ref-clk"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, audio_ref_clk_match);
+
+static struct platform_driver audio_ref_clk_driver = {
+ .driver = {
+ .name = "audio-ref-clk",
+ .owner = THIS_MODULE,
+ .of_match_table = audio_ref_clk_match,
+ },
+ .probe = audio_ref_clk_probe,
+ .remove = audio_ref_clk_remove,
+};
+
+static int __init audio_ref_clk_platform_init(void)
+{
+ return platform_driver_register(&audio_ref_clk_driver);
+}
+module_init(audio_ref_clk_platform_init);
+
+static void __exit audio_ref_clk_platform_exit(void)
+{
+ platform_driver_unregister(&audio_ref_clk_driver);
+}
+module_exit(audio_ref_clk_platform_exit);
+
+MODULE_DESCRIPTION("Audio Ref Clock module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/msm_hdmi_codec_rx.c b/sound/soc/codecs/msm_hdmi_codec_rx.c
new file mode 100644
index 000000000000..e75e1939df33
--- /dev/null
+++ b/sound/soc/codecs/msm_hdmi_codec_rx.c
@@ -0,0 +1,563 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/err.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <linux/msm_ext_display.h>
+
+#define MSM_EXT_DISP_PCM_RATES SNDRV_PCM_RATE_48000
+#define AUD_EXT_DISP_ACK_DISCONNECT (AUDIO_ACK_CONNECT ^ AUDIO_ACK_CONNECT)
+#define AUD_EXT_DISP_ACK_CONNECT (AUDIO_ACK_CONNECT)
+#define AUD_EXT_DISP_ACK_ENABLE (AUDIO_ACK_SET_ENABLE | AUDIO_ACK_ENABLE)
+
+static const char *const ext_disp_audio_type_text[] = {"None", "HDMI", "DP"};
+static const char *const ext_disp_audio_ack_text[] = {"Disconnect", "Connect",
+ "Ack_Enable"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_type, ext_disp_audio_type_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_ack_state,
+ ext_disp_audio_ack_text);
+
+struct msm_ext_disp_audio_codec_rx_data {
+ struct platform_device *ext_disp_core_pdev;
+ struct msm_ext_disp_audio_codec_ops ext_disp_ops;
+ int cable_status;
+};
+
+static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
+ struct msm_ext_disp_audio_edid_blk edid_blk;
+ int rc;
+
+ codec_data = snd_soc_codec_get_drvdata(codec);
+
+ if (!codec_data) {
+ dev_err(codec->dev, "%s: codec_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!codec_data->ext_disp_ops.get_audio_edid_blk) {
+ dev_dbg(codec->dev, "%s: get_audio_edid_blk() is NULL\n",
+ __func__);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = 0;
+ return 0;
+ }
+
+ rc = codec_data->ext_disp_ops.get_audio_edid_blk(
+ codec_data->ext_disp_core_pdev, &edid_blk);
+
+ if (!IS_ERR_VALUE(rc)) {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = edid_blk.audio_data_blk_size +
+ edid_blk.spk_alloc_data_blk_size;
+ }
+
+ dev_dbg(codec->dev, "%s: count: %d\n", __func__, uinfo->count);
+
+ return rc;
+}
+
+static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol) {
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
+ struct msm_ext_disp_audio_edid_blk edid_blk;
+ int rc;
+
+ codec_data = snd_soc_codec_get_drvdata(codec);
+ if (!codec_data || !codec_data->ext_disp_ops.get_audio_edid_blk) {
+ dev_err(codec->dev, "%s: codec_data or get_audio_edid_blk() is NULL\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ rc = codec_data->ext_disp_ops.get_audio_edid_blk(
+ codec_data->ext_disp_core_pdev, &edid_blk);
+ if (!IS_ERR_VALUE(rc)) {
+ if (sizeof(ucontrol->value.bytes.data) <
+ (edid_blk.audio_data_blk_size +
+ edid_blk.spk_alloc_data_blk_size)) {
+ dev_err(codec->dev,
+ "%s: Not enough memory to copy EDID data\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(ucontrol->value.bytes.data,
+ edid_blk.audio_data_blk,
+ edid_blk.audio_data_blk_size);
+ memcpy((ucontrol->value.bytes.data +
+ edid_blk.audio_data_blk_size),
+ edid_blk.spk_alloc_data_blk,
+ edid_blk.spk_alloc_data_blk_size);
+
+ dev_dbg(codec->dev, "%s: data_blk_size:%d, spk_alloc_data_blk_size:%d\n",
+ __func__, edid_blk.audio_data_blk_size,
+ edid_blk.spk_alloc_data_blk_size);
+ }
+
+ return rc;
+}
+
+static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
+ enum msm_ext_disp_cable_state cable_state;
+ enum msm_ext_disp_type disp_type;
+ int rc;
+
+ codec_data = snd_soc_codec_get_drvdata(codec);
+ if (!codec_data ||
+ !codec_data->ext_disp_ops.get_audio_edid_blk ||
+ !codec_data->ext_disp_ops.get_intf_id) {
+ dev_err(codec->dev, "%s: codec_data, get_audio_edid_blk() or get_intf_id is NULL\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ cable_state = codec_data->ext_disp_ops.cable_status(
+ codec_data->ext_disp_core_pdev, 1);
+ if (IS_ERR_VALUE(cable_state)) {
+ dev_err(codec->dev, "%s: Error retrieving cable state from ext_disp, err:%d\n",
+ __func__, cable_state);
+ rc = cable_state;
+ goto done;
+ }
+
+ codec_data->cable_status = cable_state;
+ if (cable_state == EXT_DISPLAY_CABLE_DISCONNECT) {
+ dev_err(codec->dev, "%s: Display cable disconnected\n",
+ __func__);
+ ucontrol->value.integer.value[0] = 0;
+ rc = 0;
+ goto done;
+ }
+
+ disp_type = codec_data->ext_disp_ops.get_intf_id(
+ codec_data->ext_disp_core_pdev);
+ if (!IS_ERR_VALUE(disp_type)) {
+ switch (disp_type) {
+ case EXT_DISPLAY_TYPE_DP:
+ ucontrol->value.integer.value[0] = 2;
+ rc = 0;
+ break;
+ case EXT_DISPLAY_TYPE_HDMI:
+ ucontrol->value.integer.value[0] = 1;
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ dev_err(codec->dev, "%s: Invalid disp_type:%d\n",
+ __func__, disp_type);
+ goto done;
+ }
+ dev_dbg(codec->dev, "%s: Display type: %d\n",
+ __func__, disp_type);
+ } else {
+ dev_err(codec->dev, "%s: Error retrieving disp_type from ext_disp, err:%d\n",
+ __func__, disp_type);
+ rc = disp_type;
+ }
+
+done:
+ return rc;
+}
+
+static int msm_ext_disp_audio_ack_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
+ u32 ack_state = 0;
+ int rc;
+
+ codec_data = snd_soc_codec_get_drvdata(codec);
+ if (!codec_data ||
+ !codec_data->ext_disp_ops.acknowledge) {
+ dev_err(codec->dev,
+ "%s: codec_data or ops acknowledge() is NULL\n",
+ __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ switch (ucontrol->value.enumerated.item[0]) {
+ case 0:
+ ack_state = AUD_EXT_DISP_ACK_DISCONNECT;
+ break;
+ case 1:
+ ack_state = AUD_EXT_DISP_ACK_CONNECT;
+ break;
+ case 2:
+ ack_state = AUD_EXT_DISP_ACK_ENABLE;
+ break;
+ default:
+ rc = -EINVAL;
+ dev_err(codec->dev,
+ "%s: invalid value %d for mixer ctl\n",
+ __func__, ucontrol->value.enumerated.item[0]);
+ goto done;
+ }
+ dev_dbg(codec->dev, "%s: control %d, ack set value 0x%x\n",
+ __func__, ucontrol->value.enumerated.item[0], ack_state);
+
+ rc = codec_data->ext_disp_ops.acknowledge(
+ codec_data->ext_disp_core_pdev, ack_state);
+ if (rc < 0) {
+ dev_err(codec->dev, "%s: error from acknowledge(), err:%d\n",
+ __func__, rc);
+ }
+
+done:
+ return rc;
+}
+
+static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "HDMI EDID",
+ .info = msm_ext_disp_edid_ctl_info,
+ .get = msm_ext_disp_edid_get,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Display Port EDID",
+ .info = msm_ext_disp_edid_ctl_info,
+ .get = msm_ext_disp_edid_get,
+ },
+ SOC_ENUM_EXT("External Display Type", ext_disp_audio_type,
+ msm_ext_disp_audio_type_get, NULL),
+ SOC_ENUM_EXT("External Display Audio Ack", ext_disp_audio_ack_state,
+ NULL, msm_ext_disp_audio_ack_set),
+};
+
+static int msm_ext_disp_audio_codec_rx_dai_startup(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+ struct msm_ext_disp_audio_codec_rx_data *codec_data =
+ dev_get_drvdata(dai->codec->dev);
+
+ if (!codec_data || !codec_data->ext_disp_ops.cable_status) {
+ dev_err(dai->dev, "%s() codec_data or cable_status is null\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ codec_data->cable_status =
+ codec_data->ext_disp_ops.cable_status(
+ codec_data->ext_disp_core_pdev, 1);
+ if (IS_ERR_VALUE(codec_data->cable_status)) {
+ dev_err(dai->dev,
+ "%s() ext disp core is not ready (ret val = %d)\n",
+ __func__, codec_data->cable_status);
+ ret = codec_data->cable_status;
+ } else if (!codec_data->cable_status) {
+ dev_err(dai->dev,
+ "%s() ext disp cable is not connected (ret val = %d)\n",
+ __func__, codec_data->cable_status);
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+static int msm_ext_disp_audio_codec_rx_dai_hw_params(
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ u32 channel_allocation = 0;
+ u32 level_shift = 0; /* 0dB */
+ bool down_mix = 0;
+ u32 num_channels = params_channels(params);
+ int rc = 0;
+ struct msm_ext_disp_audio_setup_params audio_setup_params = {0};
+
+ struct msm_ext_disp_audio_codec_rx_data *codec_data =
+ dev_get_drvdata(dai->codec->dev);
+
+ if (!codec_data || !codec_data->ext_disp_ops.audio_info_setup) {
+ dev_err(dai->dev, "%s: codec_data or audio_info_setup is null\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (IS_ERR_VALUE(codec_data->cable_status)) {
+ dev_err_ratelimited(dai->dev,
+ "%s() ext disp core is not ready (ret val = %d)\n",
+ __func__, codec_data->cable_status);
+ return codec_data->cable_status;
+ } else if (!codec_data->cable_status) {
+ dev_err_ratelimited(dai->dev,
+ "%s() ext disp cable is not connected (ret val = %d)\n",
+ __func__, codec_data->cable_status);
+ return -ENODEV;
+ }
+
+ /*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/
+ switch (num_channels) {
+ case 2:
+ channel_allocation = 0;
+ break;
+ case 3:
+ channel_allocation = 0x02;/*default to FL/FR/FC*/
+ audio_setup_params.sample_present = 0x3;
+ break;
+ case 4:
+ channel_allocation = 0x06;/*default to FL/FR/FC/RC*/
+ audio_setup_params.sample_present = 0x7;
+ break;
+ case 5:
+ channel_allocation = 0x0A;/*default to FL/FR/FC/RR/RL*/
+ audio_setup_params.sample_present = 0x7;
+ break;
+ case 6:
+ channel_allocation = 0x0B;
+ audio_setup_params.sample_present = 0x7;
+ break;
+ case 7:
+ channel_allocation = 0x12;/*default to FL/FR/FC/RL/RR/RRC/RLC*/
+ audio_setup_params.sample_present = 0xf;
+ break;
+ case 8:
+ channel_allocation = 0x13;
+ audio_setup_params.sample_present = 0xf;
+ break;
+ default:
+ dev_err(dai->dev, "invalid Channels = %u\n", num_channels);
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev,
+ "%s() num_ch %u samplerate %u channel_allocation = %u\n",
+ __func__, num_channels, params_rate(params),
+ channel_allocation);
+
+ audio_setup_params.sample_rate_hz = params_rate(params);
+ audio_setup_params.num_of_channels = num_channels;
+ audio_setup_params.channel_allocation = channel_allocation;
+ audio_setup_params.level_shift = level_shift;
+ audio_setup_params.down_mix = down_mix;
+
+ rc = codec_data->ext_disp_ops.audio_info_setup(
+ codec_data->ext_disp_core_pdev, &audio_setup_params);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err_ratelimited(dai->dev,
+ "%s() ext disp core is not ready, rc: %d\n",
+ __func__, rc);
+ }
+
+ return rc;
+}
+
+static void msm_ext_disp_audio_codec_rx_dai_shutdown(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int rc;
+
+ struct msm_ext_disp_audio_codec_rx_data *codec_data =
+ dev_get_drvdata(dai->codec->dev);
+
+ if (!codec_data || !codec_data->ext_disp_ops.teardown_done ||
+ !codec_data->ext_disp_ops.cable_status) {
+ dev_err(dai->dev, "%s: codec data or teardown_done or cable_status is null\n",
+ __func__);
+ return;
+ }
+
+ rc = codec_data->ext_disp_ops.cable_status(
+ codec_data->ext_disp_core_pdev, 0);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev,
+ "%s: ext disp core had problems releasing audio flag\n",
+ __func__);
+ }
+
+ codec_data->ext_disp_ops.teardown_done(
+ codec_data->ext_disp_core_pdev);
+ return;
+}
+
+static int msm_ext_disp_audio_codec_rx_probe(struct snd_soc_codec *codec)
+{
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
+ struct device_node *of_node_parent = NULL;
+
+ codec_data = kzalloc(sizeof(struct msm_ext_disp_audio_codec_rx_data),
+ GFP_KERNEL);
+
+ if (!codec_data) {
+ dev_err(codec->dev, "%s(): fail to allocate dai data\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ of_node_parent = of_get_parent(codec->dev->of_node);
+ if (!of_node_parent) {
+ dev_err(codec->dev, "%s(): Parent device tree node not found\n",
+ __func__);
+ kfree(codec_data);
+ return -ENODEV;
+ }
+
+ codec_data->ext_disp_core_pdev = of_find_device_by_node(of_node_parent);
+ if (!codec_data->ext_disp_core_pdev) {
+ dev_err(codec->dev, "%s(): can't get parent pdev\n", __func__);
+ kfree(codec_data);
+ return -ENODEV;
+ }
+
+ if (msm_ext_disp_register_audio_codec(codec_data->ext_disp_core_pdev,
+ &codec_data->ext_disp_ops)) {
+ dev_err(codec->dev, "%s(): can't register with ext disp core",
+ __func__);
+ kfree(codec_data);
+ return -ENODEV;
+ }
+
+ dev_set_drvdata(codec->dev, codec_data);
+
+ dev_dbg(codec->dev, "%s(): registered %s with ext disp core\n",
+ __func__, codec->component.name);
+
+ return 0;
+}
+
+static int msm_ext_disp_audio_codec_rx_remove(struct snd_soc_codec *codec)
+{
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
+
+ codec_data = dev_get_drvdata(codec->dev);
+ kfree(codec_data);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_ext_disp_audio_codec_rx_dai_ops = {
+ .startup = msm_ext_disp_audio_codec_rx_dai_startup,
+ .hw_params = msm_ext_disp_audio_codec_rx_dai_hw_params,
+ .shutdown = msm_ext_disp_audio_codec_rx_dai_shutdown
+};
+
+static struct snd_soc_dai_driver msm_ext_disp_audio_codec_rx_dais[] = {
+ {
+ .name = "msm_hdmi_audio_codec_rx_dai",
+ .playback = {
+ .stream_name = "HDMI Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .rates = MSM_EXT_DISP_PCM_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
+ },
+ {
+ .name = "msm_dp_audio_codec_rx_dai",
+ .playback = {
+ .stream_name = "Display Port Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 48000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
+ },
+};
+
+static struct snd_soc_codec_driver msm_ext_disp_audio_codec_rx_soc_driver = {
+ .probe = msm_ext_disp_audio_codec_rx_probe,
+ .remove = msm_ext_disp_audio_codec_rx_remove,
+ .controls = msm_ext_disp_codec_rx_controls,
+ .num_controls = ARRAY_SIZE(msm_ext_disp_codec_rx_controls),
+};
+
+static int msm_ext_disp_audio_codec_rx_plat_probe(
+ struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "%s(): dev name %s\n", __func__,
+ dev_name(&pdev->dev));
+
+ return snd_soc_register_codec(&pdev->dev,
+ &msm_ext_disp_audio_codec_rx_soc_driver,
+ msm_ext_disp_audio_codec_rx_dais,
+ ARRAY_SIZE(msm_ext_disp_audio_codec_rx_dais));
+}
+
+static int msm_ext_disp_audio_codec_rx_plat_remove(
+ struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+static const struct of_device_id msm_ext_disp_audio_codec_rx_dt_match[] = {
+ { .compatible = "qcom,msm-ext-disp-audio-codec-rx", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_ext_disp_audio_codec_rx_dt_match);
+
+static struct platform_driver msm_ext_disp_audio_codec_rx_driver = {
+ .driver = {
+ .name = "msm-ext-disp-audio-codec-rx",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ext_disp_audio_codec_rx_dt_match,
+ },
+ .probe = msm_ext_disp_audio_codec_rx_plat_probe,
+ .remove = msm_ext_disp_audio_codec_rx_plat_remove,
+};
+
+static int __init msm_ext_disp_audio_codec_rx_init(void)
+{
+ int rc;
+
+ rc = platform_driver_register(&msm_ext_disp_audio_codec_rx_driver);
+ if (rc) {
+ pr_err("%s: failed to register ext disp codec driver err:%d\n",
+ __func__, rc);
+ }
+
+ return rc;
+}
+module_init(msm_ext_disp_audio_codec_rx_init);
+
+static void __exit msm_ext_disp_audio_codec_rx_exit(void)
+{
+ platform_driver_unregister(&msm_ext_disp_audio_codec_rx_driver);
+}
+module_exit(msm_ext_disp_audio_codec_rx_exit);
+
+MODULE_DESCRIPTION("MSM External Display Audio CODEC Driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/msm_sdw/Kconfig b/sound/soc/codecs/msm_sdw/Kconfig
new file mode 100644
index 000000000000..abd7c8c7dfb0
--- /dev/null
+++ b/sound/soc/codecs/msm_sdw/Kconfig
@@ -0,0 +1,6 @@
+config SND_SOC_MSM_SDW
+ tristate "MSM Internal soundwire codec"
+ help
+ MSM-based soundwire codec core driver
+ supported along with internal digital
+ codec core.
diff --git a/sound/soc/codecs/msm_sdw/Makefile b/sound/soc/codecs/msm_sdw/Makefile
new file mode 100644
index 000000000000..64e932b9d262
--- /dev/null
+++ b/sound/soc/codecs/msm_sdw/Makefile
@@ -0,0 +1,3 @@
+snd-soc-msm-sdw-objs := msm_sdw_cdc.o msm_sdw_regmap.o msm-sdw-tables.o msm_sdw_cdc_utils.o
+obj-$(CONFIG_SND_SOC_MSM_SDW) += snd-soc-msm-sdw.o
+ccflags-y += -I$(srctree)/sound/soc/msm
diff --git a/sound/soc/codecs/msm_sdw/msm-sdw-tables.c b/sound/soc/codecs/msm_sdw/msm-sdw-tables.c
new file mode 100644
index 000000000000..1b51805bb92e
--- /dev/null
+++ b/sound/soc/codecs/msm_sdw/msm-sdw-tables.c
@@ -0,0 +1,319 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include "msm_sdw.h"
+
+const u8 msm_sdw_page_map[MSM_SDW_MAX_REGISTER] = {
+ [MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 0xa,
+ [MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 0xa,
+ [MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 0xa,
+ [MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 0xa,
+ [MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 0xa,
+ [MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 0xa,
+ [MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 0xa,
+ [MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 0xa,
+ [MSM_SDW_COMPANDER7_CTL0] = 0xb,
+ [MSM_SDW_COMPANDER7_CTL1] = 0xb,
+ [MSM_SDW_COMPANDER7_CTL2] = 0xb,
+ [MSM_SDW_COMPANDER7_CTL3] = 0xb,
+ [MSM_SDW_COMPANDER7_CTL4] = 0xb,
+ [MSM_SDW_COMPANDER7_CTL5] = 0xb,
+ [MSM_SDW_COMPANDER7_CTL6] = 0xb,
+ [MSM_SDW_COMPANDER7_CTL7] = 0xb,
+ [MSM_SDW_COMPANDER8_CTL0] = 0xb,
+ [MSM_SDW_COMPANDER8_CTL1] = 0xb,
+ [MSM_SDW_COMPANDER8_CTL2] = 0xb,
+ [MSM_SDW_COMPANDER8_CTL3] = 0xb,
+ [MSM_SDW_COMPANDER8_CTL4] = 0xb,
+ [MSM_SDW_COMPANDER8_CTL5] = 0xb,
+ [MSM_SDW_COMPANDER8_CTL6] = 0xb,
+ [MSM_SDW_COMPANDER8_CTL7] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_CTL] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_CFG0] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_CFG1] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_CFG2] = 0xb,
+ [MSM_SDW_RX7_RX_VOL_CTL] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_MIX_CTL] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_MIX_CFG] = 0xb,
+ [MSM_SDW_RX7_RX_VOL_MIX_CTL] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_SEC0] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_SEC1] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_SEC2] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_SEC3] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_SEC5] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_SEC6] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_SEC7] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 0xb,
+ [MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_CTL] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_CFG0] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_CFG1] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_CFG2] = 0xb,
+ [MSM_SDW_RX8_RX_VOL_CTL] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_MIX_CTL] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_MIX_CFG] = 0xb,
+ [MSM_SDW_RX8_RX_VOL_MIX_CTL] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_SEC0] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_SEC1] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_SEC2] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_SEC3] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_SEC5] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_SEC6] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_SEC7] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 0xb,
+ [MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 0xb,
+ [MSM_SDW_BOOST0_BOOST_PATH_CTL] = 0xc,
+ [MSM_SDW_BOOST0_BOOST_CTL] = 0xc,
+ [MSM_SDW_BOOST0_BOOST_CFG1] = 0xc,
+ [MSM_SDW_BOOST0_BOOST_CFG2] = 0xc,
+ [MSM_SDW_BOOST1_BOOST_PATH_CTL] = 0xc,
+ [MSM_SDW_BOOST1_BOOST_CTL] = 0xc,
+ [MSM_SDW_BOOST1_BOOST_CFG1] = 0xc,
+ [MSM_SDW_BOOST1_BOOST_CFG2] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_RD_DATA_0] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_RD_DATA_1] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_RD_DATA_2] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_RD_DATA_3] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 0xc,
+ [MSM_SDW_AHB_BRIDGE_ACCESS_STATUS] = 0xc,
+ [MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 0xd,
+ [MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 0xd,
+ [MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 0xd,
+ [MSM_SDW_TOP_TOP_CFG0] = 0xd,
+ [MSM_SDW_TOP_TOP_CFG1] = 0xd,
+ [MSM_SDW_TOP_RX_I2S_CTL] = 0xd,
+ [MSM_SDW_TOP_TX_I2S_CTL] = 0xd,
+ [MSM_SDW_TOP_I2S_CLK] = 0xd,
+ [MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 0xd,
+ [MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 0xd,
+ [MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 0xd,
+ [MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 0xd,
+ [MSM_SDW_TOP_FREQ_MCLK] = 0xd,
+ [MSM_SDW_TOP_DEBUG_BUS_SEL] = 0xd,
+ [MSM_SDW_TOP_DEBUG_EN] = 0xd,
+ [MSM_SDW_TOP_I2S_RESET] = 0xd,
+ [MSM_SDW_TOP_BLOCKS_RESET] = 0xd,
+};
+
+const u8 msm_sdw_reg_readable[MSM_SDW_MAX_REGISTER] = {
+ [MSM_SDW_PAGE_REGISTER] = 1,
+ [MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 1,
+ [MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 1,
+ [MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 1,
+ [MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 1,
+ [MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 1,
+ [MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 1,
+ [MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 1,
+ [MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 1,
+ [MSM_SDW_COMPANDER7_CTL0] = 1,
+ [MSM_SDW_COMPANDER7_CTL1] = 1,
+ [MSM_SDW_COMPANDER7_CTL2] = 1,
+ [MSM_SDW_COMPANDER7_CTL3] = 1,
+ [MSM_SDW_COMPANDER7_CTL4] = 1,
+ [MSM_SDW_COMPANDER7_CTL5] = 1,
+ [MSM_SDW_COMPANDER7_CTL6] = 1,
+ [MSM_SDW_COMPANDER7_CTL7] = 1,
+ [MSM_SDW_COMPANDER8_CTL0] = 1,
+ [MSM_SDW_COMPANDER8_CTL1] = 1,
+ [MSM_SDW_COMPANDER8_CTL2] = 1,
+ [MSM_SDW_COMPANDER8_CTL3] = 1,
+ [MSM_SDW_COMPANDER8_CTL4] = 1,
+ [MSM_SDW_COMPANDER8_CTL5] = 1,
+ [MSM_SDW_COMPANDER8_CTL6] = 1,
+ [MSM_SDW_COMPANDER8_CTL7] = 1,
+ [MSM_SDW_RX7_RX_PATH_CTL] = 1,
+ [MSM_SDW_RX7_RX_PATH_CFG0] = 1,
+ [MSM_SDW_RX7_RX_PATH_CFG1] = 1,
+ [MSM_SDW_RX7_RX_PATH_CFG2] = 1,
+ [MSM_SDW_RX7_RX_VOL_CTL] = 1,
+ [MSM_SDW_RX7_RX_PATH_MIX_CTL] = 1,
+ [MSM_SDW_RX7_RX_PATH_MIX_CFG] = 1,
+ [MSM_SDW_RX7_RX_VOL_MIX_CTL] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC0] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC1] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC2] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC3] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC5] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC6] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC7] = 1,
+ [MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 1,
+ [MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 1,
+ [MSM_SDW_RX8_RX_PATH_CTL] = 1,
+ [MSM_SDW_RX8_RX_PATH_CFG0] = 1,
+ [MSM_SDW_RX8_RX_PATH_CFG1] = 1,
+ [MSM_SDW_RX8_RX_PATH_CFG2] = 1,
+ [MSM_SDW_RX8_RX_VOL_CTL] = 1,
+ [MSM_SDW_RX8_RX_PATH_MIX_CTL] = 1,
+ [MSM_SDW_RX8_RX_PATH_MIX_CFG] = 1,
+ [MSM_SDW_RX8_RX_VOL_MIX_CTL] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC0] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC1] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC2] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC3] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC5] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC6] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC7] = 1,
+ [MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 1,
+ [MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 1,
+ [MSM_SDW_BOOST0_BOOST_PATH_CTL] = 1,
+ [MSM_SDW_BOOST0_BOOST_CTL] = 1,
+ [MSM_SDW_BOOST0_BOOST_CFG1] = 1,
+ [MSM_SDW_BOOST0_BOOST_CFG2] = 1,
+ [MSM_SDW_BOOST1_BOOST_PATH_CTL] = 1,
+ [MSM_SDW_BOOST1_BOOST_CTL] = 1,
+ [MSM_SDW_BOOST1_BOOST_CFG1] = 1,
+ [MSM_SDW_BOOST1_BOOST_CFG2] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_DATA_0] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_DATA_1] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_DATA_2] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_DATA_3] = 1,
+ [MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 1,
+ [MSM_SDW_AHB_BRIDGE_ACCESS_STATUS] = 1,
+ [MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 1,
+ [MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 1,
+ [MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 1,
+ [MSM_SDW_TOP_TOP_CFG0] = 1,
+ [MSM_SDW_TOP_TOP_CFG1] = 1,
+ [MSM_SDW_TOP_RX_I2S_CTL] = 1,
+ [MSM_SDW_TOP_TX_I2S_CTL] = 1,
+ [MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 1,
+ [MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 1,
+ [MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 1,
+ [MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 1,
+ [MSM_SDW_TOP_FREQ_MCLK] = 1,
+ [MSM_SDW_TOP_DEBUG_BUS_SEL] = 1,
+ [MSM_SDW_TOP_DEBUG_EN] = 1,
+ [MSM_SDW_TOP_I2S_RESET] = 1,
+ [MSM_SDW_TOP_BLOCKS_RESET] = 1,
+};
+
+const u8 msm_sdw_reg_writeable[MSM_SDW_MAX_REGISTER] = {
+ [MSM_SDW_PAGE_REGISTER] = 1,
+ [MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 1,
+ [MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 1,
+ [MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 1,
+ [MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 1,
+ [MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 1,
+ [MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 1,
+ [MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 1,
+ [MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 1,
+ [MSM_SDW_COMPANDER7_CTL0] = 1,
+ [MSM_SDW_COMPANDER7_CTL1] = 1,
+ [MSM_SDW_COMPANDER7_CTL2] = 1,
+ [MSM_SDW_COMPANDER7_CTL3] = 1,
+ [MSM_SDW_COMPANDER7_CTL4] = 1,
+ [MSM_SDW_COMPANDER7_CTL5] = 1,
+ [MSM_SDW_COMPANDER7_CTL7] = 1,
+ [MSM_SDW_COMPANDER8_CTL0] = 1,
+ [MSM_SDW_COMPANDER8_CTL1] = 1,
+ [MSM_SDW_COMPANDER8_CTL2] = 1,
+ [MSM_SDW_COMPANDER8_CTL3] = 1,
+ [MSM_SDW_COMPANDER8_CTL4] = 1,
+ [MSM_SDW_COMPANDER8_CTL5] = 1,
+ [MSM_SDW_COMPANDER8_CTL7] = 1,
+ [MSM_SDW_RX7_RX_PATH_CTL] = 1,
+ [MSM_SDW_RX7_RX_PATH_CFG0] = 1,
+ [MSM_SDW_RX7_RX_PATH_CFG1] = 1,
+ [MSM_SDW_RX7_RX_PATH_CFG2] = 1,
+ [MSM_SDW_RX7_RX_VOL_CTL] = 1,
+ [MSM_SDW_RX7_RX_PATH_MIX_CTL] = 1,
+ [MSM_SDW_RX7_RX_PATH_MIX_CFG] = 1,
+ [MSM_SDW_RX7_RX_VOL_MIX_CTL] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC0] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC1] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC2] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC3] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC5] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC6] = 1,
+ [MSM_SDW_RX7_RX_PATH_SEC7] = 1,
+ [MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 1,
+ [MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 1,
+ [MSM_SDW_RX8_RX_PATH_CTL] = 1,
+ [MSM_SDW_RX8_RX_PATH_CFG0] = 1,
+ [MSM_SDW_RX8_RX_PATH_CFG1] = 1,
+ [MSM_SDW_RX8_RX_PATH_CFG2] = 1,
+ [MSM_SDW_RX8_RX_VOL_CTL] = 1,
+ [MSM_SDW_RX8_RX_PATH_MIX_CTL] = 1,
+ [MSM_SDW_RX8_RX_PATH_MIX_CFG] = 1,
+ [MSM_SDW_RX8_RX_VOL_MIX_CTL] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC0] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC1] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC2] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC3] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC5] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC6] = 1,
+ [MSM_SDW_RX8_RX_PATH_SEC7] = 1,
+ [MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 1,
+ [MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 1,
+ [MSM_SDW_BOOST0_BOOST_PATH_CTL] = 1,
+ [MSM_SDW_BOOST0_BOOST_CTL] = 1,
+ [MSM_SDW_BOOST0_BOOST_CFG1] = 1,
+ [MSM_SDW_BOOST0_BOOST_CFG2] = 1,
+ [MSM_SDW_BOOST1_BOOST_PATH_CTL] = 1,
+ [MSM_SDW_BOOST1_BOOST_CTL] = 1,
+ [MSM_SDW_BOOST1_BOOST_CFG1] = 1,
+ [MSM_SDW_BOOST1_BOOST_CFG2] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 1,
+ [MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 1,
+ [MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 1,
+ [MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 1,
+ [MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 1,
+ [MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 1,
+ [MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 1,
+ [MSM_SDW_TOP_TOP_CFG0] = 1,
+ [MSM_SDW_TOP_TOP_CFG1] = 1,
+ [MSM_SDW_TOP_RX_I2S_CTL] = 1,
+ [MSM_SDW_TOP_TX_I2S_CTL] = 1,
+ [MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 1,
+ [MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 1,
+ [MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 1,
+ [MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 1,
+ [MSM_SDW_TOP_FREQ_MCLK] = 1,
+ [MSM_SDW_TOP_DEBUG_BUS_SEL] = 1,
+ [MSM_SDW_TOP_DEBUG_EN] = 1,
+ [MSM_SDW_TOP_I2S_RESET] = 1,
+ [MSM_SDW_TOP_BLOCKS_RESET] = 1,
+};
diff --git a/sound/soc/codecs/msm_sdw/msm_sdw.h b/sound/soc/codecs/msm_sdw/msm_sdw.h
new file mode 100644
index 000000000000..db991481e07d
--- /dev/null
+++ b/sound/soc/codecs/msm_sdw/msm_sdw.h
@@ -0,0 +1,169 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef MSM_SDW_H
+#define MSM_SDW_H
+
+#include <sound/soc.h>
+#include <sound/q6afe-v2.h>
+#include "msm_sdw_registers.h"
+
+#define MSM_SDW_MAX_REGISTER 0x400
+
+extern const struct regmap_config msm_sdw_regmap_config;
+extern const u8 msm_sdw_page_map[MSM_SDW_MAX_REGISTER];
+extern const u8 msm_sdw_reg_readable[MSM_SDW_MAX_REGISTER];
+extern const u8 msm_sdw_reg_writeable[MSM_SDW_MAX_REGISTER];
+
+enum {
+ MSM_SDW_RX4 = 0,
+ MSM_SDW_RX5,
+ MSM_SDW_RX_MAX,
+};
+
+enum {
+ MSM_SDW_TX0 = 0,
+ MSM_SDW_TX1,
+ MSM_SDW_TX_MAX,
+};
+
+enum {
+ COMP1, /* SPK_L */
+ COMP2, /* SPK_R */
+ COMP_MAX
+};
+
+/*
+ * Structure used to update codec
+ * register defaults after reset
+ */
+struct msm_sdw_reg_mask_val {
+ u16 reg;
+ u8 mask;
+ u8 val;
+};
+
+/*
+ * Selects compander and smart boost settings
+ * for a given speaker mode
+ */
+enum {
+ SPKR_MODE_DEFAULT,
+ SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */
+};
+
+/* Rx path gain offsets */
+enum {
+ RX_GAIN_OFFSET_M1P5_DB,
+ RX_GAIN_OFFSET_0_DB,
+};
+
+struct msm_sdw_reg_val {
+ unsigned short reg; /* register address */
+ u8 *buf; /* buffer to be written to reg. addr */
+ int bytes; /* number of bytes to be written */
+};
+
+/* Hold instance to soundwire platform device */
+struct msm_sdw_ctrl_data {
+ struct platform_device *sdw_pdev;
+};
+
+struct wcd_sdw_ctrl_platform_data {
+ void *handle; /* holds codec private data */
+ int (*read)(void *handle, int reg);
+ int (*write)(void *handle, int reg, int val);
+ int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
+ int (*clk)(void *handle, bool enable);
+ int (*handle_irq)(void *handle,
+ irqreturn_t (*swrm_irq_handler)(int irq,
+ void *data),
+ void *swrm_handle,
+ int action);
+};
+
+struct msm_sdw_priv {
+ struct device *dev;
+ struct mutex io_lock;
+
+ int (*read_dev)(struct msm_sdw_priv *msm_sdw, unsigned short reg,
+ int bytes, void *dest);
+ int (*write_dev)(struct msm_sdw_priv *msm_sdw, unsigned short reg,
+ int bytes, void *src);
+ int (*multi_reg_write)(struct msm_sdw_priv *msm_sdw, const void *data,
+ size_t count);
+ struct snd_soc_codec *codec;
+ struct device_node *sdw_gpio_p; /* used by pinctrl API */
+ /* SoundWire data structure */
+ struct msm_sdw_ctrl_data *sdw_ctrl_data;
+ int nr;
+
+ /* compander */
+ int comp_enabled[COMP_MAX];
+ int ear_spkr_gain;
+
+ /* to track the status */
+ unsigned long status_mask;
+
+ struct work_struct msm_sdw_add_child_devices_work;
+ struct wcd_sdw_ctrl_platform_data sdw_plat_data;
+
+ unsigned int vi_feed_value;
+
+ struct mutex sdw_read_lock;
+ struct mutex sdw_write_lock;
+ struct mutex sdw_clk_lock;
+ int sdw_clk_users;
+ int sdw_mclk_users;
+
+ int sdw_irq;
+ int int_mclk1_rsc_ref;
+ bool int_mclk1_enabled;
+ bool sdw_npl_clk_enabled;
+ struct mutex cdc_int_mclk1_mutex;
+ struct mutex sdw_npl_clk_mutex;
+ struct delayed_work disable_int_mclk1_work;
+ struct afe_clk_set sdw_cdc_core_clk;
+ struct afe_clk_set sdw_npl_clk;
+ struct notifier_block service_nb;
+ int (*sdw_cdc_gpio_fn)(bool enable, struct snd_soc_codec *codec);
+ bool dev_up;
+
+ int spkr_gain_offset;
+ int spkr_mode;
+ struct mutex codec_mutex;
+ int rx_4_count;
+ int rx_5_count;
+ u32 mclk_rate;
+ struct regmap *regmap;
+
+ bool prev_pg_valid;
+ u8 prev_pg;
+ u32 sdw_base_addr;
+ char __iomem *sdw_base;
+ u32 version;
+
+ /* Entry for version info */
+ struct snd_info_entry *entry;
+ struct snd_info_entry *version_entry;
+};
+
+extern int msm_sdw_set_spkr_mode(struct snd_soc_codec *codec, int mode);
+extern int msm_sdw_set_spkr_gain_offset(struct snd_soc_codec *codec,
+ int offset);
+extern void msm_sdw_gpio_cb(
+ int (*sdw_cdc_gpio_fn)(bool enable, struct snd_soc_codec *codec),
+ struct snd_soc_codec *codec);
+extern struct regmap *msm_sdw_regmap_init(struct device *dev,
+ const struct regmap_config *config);
+extern int msm_sdw_codec_info_create_codec_entry(struct snd_info_entry *,
+ struct snd_soc_codec *);
+#endif
diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c b/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c
new file mode 100644
index 000000000000..66be0ae2d014
--- /dev/null
+++ b/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c
@@ -0,0 +1,2006 @@
+/* Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <linux/printk.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/soundwire/swr-wcd.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/q6core.h>
+#include <sound/tlv.h>
+#include "msm_sdw.h"
+#include "msm_sdw_registers.h"
+
+#define MSM_SDW_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+#define MSM_SDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define MSM_SDW_STRING_LEN 80
+
+#define INT_MCLK1_FREQ 9600000
+#define SDW_NPL_FREQ 153600000
+
+#define MSM_SDW_VERSION_1_0 0x0001
+#define MSM_SDW_VERSION_ENTRY_SIZE 32
+
+/*
+ * 200 Milliseconds sufficient for DSP bring up in the modem
+ * after Sub System Restart
+ */
+#define ADSP_STATE_READY_TIMEOUT_MS 200
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+static struct snd_soc_dai_driver msm_sdw_dai[];
+static bool skip_irq = true;
+
+static int msm_sdw_config_ear_spkr_gain(struct snd_soc_codec *codec,
+ int event, int gain_reg);
+static int msm_sdw_config_compander(struct snd_soc_codec *, int, int);
+static int msm_sdw_mclk_enable(struct msm_sdw_priv *msm_sdw,
+ int mclk_enable, bool dapm);
+static int msm_int_enable_sdw_cdc_clk(struct msm_sdw_priv *msm_sdw,
+ int enable, bool dapm);
+
+enum {
+ VI_SENSE_1,
+ VI_SENSE_2,
+};
+
+enum {
+ AIF1_SDW_PB = 0,
+ AIF1_SDW_VIFEED,
+ NUM_CODEC_DAIS,
+};
+
+static const struct msm_sdw_reg_mask_val msm_sdw_spkr_default[] = {
+ {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x80},
+ {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x80},
+ {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x01},
+ {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x01},
+ {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x50},
+ {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x50},
+};
+
+static const struct msm_sdw_reg_mask_val msm_sdw_spkr_mode1[] = {
+ {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x00},
+ {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x00},
+ {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x00},
+ {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x00},
+ {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x44},
+ {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x44},
+};
+
+/**
+ * msm_sdw_set_spkr_gain_offset - offset the speaker path
+ * gain with the given offset value.
+ *
+ * @codec: codec instance
+ * @offset: Indicates speaker path gain offset value.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int msm_sdw_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset)
+{
+ struct msm_sdw_priv *priv;
+
+ if (!codec) {
+ pr_err("%s: NULL codec pointer!\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = snd_soc_codec_get_drvdata(codec);
+ if (!priv)
+ return -EINVAL;
+
+ priv->spkr_gain_offset = offset;
+ return 0;
+}
+EXPORT_SYMBOL(msm_sdw_set_spkr_gain_offset);
+
+/**
+ * msm_sdw_set_spkr_mode - Configures speaker compander and smartboost
+ * settings based on speaker mode.
+ *
+ * @codec: codec instance
+ * @mode: Indicates speaker configuration mode.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int msm_sdw_set_spkr_mode(struct snd_soc_codec *codec, int mode)
+{
+ struct msm_sdw_priv *priv;
+ int i;
+ const struct msm_sdw_reg_mask_val *regs;
+ int size;
+
+ if (!codec) {
+ pr_err("%s: NULL codec pointer!\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = snd_soc_codec_get_drvdata(codec);
+ if (!priv)
+ return -EINVAL;
+
+ switch (mode) {
+ case SPKR_MODE_1:
+ regs = msm_sdw_spkr_mode1;
+ size = ARRAY_SIZE(msm_sdw_spkr_mode1);
+ break;
+ default:
+ regs = msm_sdw_spkr_default;
+ size = ARRAY_SIZE(msm_sdw_spkr_default);
+ break;
+ }
+
+ priv->spkr_mode = mode;
+ for (i = 0; i < size; i++)
+ snd_soc_update_bits(codec, regs[i].reg,
+ regs[i].mask, regs[i].val);
+ return 0;
+}
+EXPORT_SYMBOL(msm_sdw_set_spkr_mode);
+
+static int msm_enable_sdw_npl_clk(struct msm_sdw_priv *msm_sdw, int enable)
+{
+ int ret = 0;
+
+ dev_dbg(msm_sdw->dev, "%s: enable %d\n", __func__, enable);
+
+ mutex_lock(&msm_sdw->sdw_npl_clk_mutex);
+ if (enable) {
+ if (msm_sdw->sdw_npl_clk_enabled == false) {
+ msm_sdw->sdw_npl_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT4_MI2S_RX,
+ &msm_sdw->sdw_npl_clk);
+ if (ret < 0) {
+ dev_err(msm_sdw->dev,
+ "%s: failed to enable SDW NPL CLK\n",
+ __func__);
+ mutex_unlock(&msm_sdw->sdw_npl_clk_mutex);
+ return ret;
+ }
+ dev_dbg(msm_sdw->dev, "enabled sdw npl clk\n");
+ msm_sdw->sdw_npl_clk_enabled = true;
+ }
+ } else {
+ if (msm_sdw->sdw_npl_clk_enabled == true) {
+ msm_sdw->sdw_npl_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT4_MI2S_RX,
+ &msm_sdw->sdw_npl_clk);
+ if (ret < 0)
+ dev_err(msm_sdw->dev,
+ "%s: failed to disable SDW NPL CLK\n",
+ __func__);
+ msm_sdw->sdw_npl_clk_enabled = false;
+ }
+ }
+ mutex_unlock(&msm_sdw->sdw_npl_clk_mutex);
+ return ret;
+}
+
+static int msm_int_enable_sdw_cdc_clk(struct msm_sdw_priv *msm_sdw,
+ int enable, bool dapm)
+{
+ int ret = 0;
+
+ mutex_lock(&msm_sdw->cdc_int_mclk1_mutex);
+ dev_dbg(msm_sdw->dev, "%s: enable %d mclk1 ref counter %d\n",
+ __func__, enable, msm_sdw->int_mclk1_rsc_ref);
+ if (enable) {
+ if (msm_sdw->int_mclk1_rsc_ref == 0) {
+ cancel_delayed_work_sync(
+ &msm_sdw->disable_int_mclk1_work);
+ if (msm_sdw->int_mclk1_enabled == false) {
+ msm_sdw->sdw_cdc_core_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT4_MI2S_RX,
+ &msm_sdw->sdw_cdc_core_clk);
+ if (ret < 0) {
+ dev_err(msm_sdw->dev,
+ "%s: failed to enable SDW MCLK\n",
+ __func__);
+ goto rtn;
+ }
+ dev_dbg(msm_sdw->dev,
+ "enabled sdw codec core mclk\n");
+ msm_sdw->int_mclk1_enabled = true;
+ }
+ }
+ msm_sdw->int_mclk1_rsc_ref++;
+ } else {
+ cancel_delayed_work_sync(&msm_sdw->disable_int_mclk1_work);
+ if (msm_sdw->int_mclk1_rsc_ref > 0) {
+ msm_sdw->int_mclk1_rsc_ref--;
+ dev_dbg(msm_sdw->dev,
+ "%s: decrementing mclk_res_ref %d\n",
+ __func__, msm_sdw->int_mclk1_rsc_ref);
+ }
+ if (msm_sdw->int_mclk1_enabled == true &&
+ msm_sdw->int_mclk1_rsc_ref == 0) {
+ msm_sdw->sdw_cdc_core_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT4_MI2S_RX,
+ &msm_sdw->sdw_cdc_core_clk);
+ if (ret < 0)
+ dev_err(msm_sdw->dev,
+ "%s: failed to disable SDW MCLK\n",
+ __func__);
+ msm_sdw->int_mclk1_enabled = false;
+ }
+ }
+rtn:
+ mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(msm_int_enable_sdw_cdc_clk);
+
+static void msm_disable_int_mclk1(struct work_struct *work)
+{
+ struct msm_sdw_priv *msm_sdw = NULL;
+ struct delayed_work *dwork;
+ int ret = 0;
+
+ dwork = to_delayed_work(work);
+ msm_sdw = container_of(dwork, struct msm_sdw_priv,
+ disable_int_mclk1_work);
+ mutex_lock(&msm_sdw->cdc_int_mclk1_mutex);
+ dev_dbg(msm_sdw->dev, "%s: mclk1_enabled %d mclk1_rsc_ref %d\n",
+ __func__, msm_sdw->int_mclk1_enabled,
+ msm_sdw->int_mclk1_rsc_ref);
+ if (msm_sdw->int_mclk1_enabled == true
+ && msm_sdw->int_mclk1_rsc_ref == 0) {
+ dev_dbg(msm_sdw->dev, "Disable the mclk1\n");
+ msm_sdw->sdw_cdc_core_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT4_MI2S_RX,
+ &msm_sdw->sdw_cdc_core_clk);
+ if (ret < 0)
+ dev_err(msm_sdw->dev,
+ "%s failed to disable the MCLK1\n",
+ __func__);
+ msm_sdw->int_mclk1_enabled = false;
+ }
+ mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex);
+}
+
+static int msm_int_mclk1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ dev_dbg(msm_sdw->dev, "%s: event = %d\n", __func__, event);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* enable the codec mclk config */
+ msm_int_enable_sdw_cdc_clk(msm_sdw, 1, true);
+ msm_sdw_mclk_enable(msm_sdw, 1, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* disable the codec mclk config */
+ msm_sdw_mclk_enable(msm_sdw, 0, true);
+ msm_int_enable_sdw_cdc_clk(msm_sdw, 0, true);
+ break;
+ default:
+ dev_err(msm_sdw->dev,
+ "%s: invalid DAPM event %d\n", __func__, event);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int msm_sdw_ahb_write_device(struct msm_sdw_priv *msm_sdw,
+ u16 reg, u8 *value)
+{
+ u32 temp = (u32)(*value) & 0x000000FF;
+
+ if (!msm_sdw->dev_up) {
+ dev_err_ratelimited(msm_sdw->dev, "%s: q6 not ready\n",
+ __func__);
+ return 0;
+ }
+
+ iowrite32(temp, msm_sdw->sdw_base + reg);
+ return 0;
+}
+
+static int msm_sdw_ahb_read_device(struct msm_sdw_priv *msm_sdw,
+ u16 reg, u8 *value)
+{
+ u32 temp;
+
+ if (!msm_sdw->dev_up) {
+ dev_err_ratelimited(msm_sdw->dev, "%s: q6 not ready\n",
+ __func__);
+ return 0;
+ }
+
+ temp = ioread32(msm_sdw->sdw_base + reg);
+ *value = (u8)temp;
+ return 0;
+}
+
+static int __msm_sdw_reg_read(struct msm_sdw_priv *msm_sdw, unsigned short reg,
+ int bytes, void *dest)
+{
+ int ret = -EINVAL, i;
+ u8 temp = 0;
+
+ dev_dbg(msm_sdw->dev, "%s reg = %x\n", __func__, reg);
+ mutex_lock(&msm_sdw->cdc_int_mclk1_mutex);
+ if (msm_sdw->int_mclk1_enabled == false) {
+ msm_sdw->sdw_cdc_core_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT4_MI2S_RX,
+ &msm_sdw->sdw_cdc_core_clk);
+ if (ret < 0) {
+ dev_err(msm_sdw->dev,
+ "%s:failed to enable the INT_MCLK1\n",
+ __func__);
+ goto unlock_exit;
+ }
+ dev_dbg(msm_sdw->dev, "%s:enabled sdw codec core clk\n",
+ __func__);
+ for (i = 0; i < bytes; i++) {
+ ret = msm_sdw_ahb_read_device(
+ msm_sdw, reg + (4 * i), &temp);
+ ((u8 *)dest)[i] = temp;
+ }
+ msm_sdw->int_mclk1_enabled = true;
+ schedule_delayed_work(&msm_sdw->disable_int_mclk1_work, 50);
+ goto unlock_exit;
+ }
+ for (i = 0; i < bytes; i++) {
+ ret = msm_sdw_ahb_read_device(
+ msm_sdw, reg + (4 * i), &temp);
+ ((u8 *)dest)[i] = temp;
+ }
+unlock_exit:
+ mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex);
+ if (ret < 0) {
+ dev_err_ratelimited(msm_sdw->dev,
+ "%s: codec read failed for reg 0x%x\n",
+ __func__, reg);
+ return ret;
+ }
+ dev_dbg(msm_sdw->dev, "Read 0x%02x from 0x%x\n", temp, reg);
+
+ return 0;
+}
+
+static int __msm_sdw_reg_write(struct msm_sdw_priv *msm_sdw, unsigned short reg,
+ int bytes, void *src)
+{
+ int ret = -EINVAL, i;
+
+ mutex_lock(&msm_sdw->cdc_int_mclk1_mutex);
+ if (msm_sdw->int_mclk1_enabled == false) {
+ msm_sdw->sdw_cdc_core_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_INT4_MI2S_RX,
+ &msm_sdw->sdw_cdc_core_clk);
+ if (ret < 0) {
+ dev_err(msm_sdw->dev,
+ "%s: failed to enable the INT_MCLK1\n",
+ __func__);
+ ret = 0;
+ goto unlock_exit;
+ }
+ dev_dbg(msm_sdw->dev, "%s: enabled INT_MCLK1\n", __func__);
+ for (i = 0; i < bytes; i++)
+ ret = msm_sdw_ahb_write_device(msm_sdw, reg + (4 * i),
+ &((u8 *)src)[i]);
+ msm_sdw->int_mclk1_enabled = true;
+ schedule_delayed_work(&msm_sdw->disable_int_mclk1_work, 50);
+ goto unlock_exit;
+ }
+ for (i = 0; i < bytes; i++)
+ ret = msm_sdw_ahb_write_device(msm_sdw, reg + (4 * i),
+ &((u8 *)src)[i]);
+unlock_exit:
+ mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex);
+ dev_dbg(msm_sdw->dev, "Write 0x%x val 0x%02x\n",
+ reg, (u32)(*(u32 *)src));
+
+ return ret;
+}
+
+static int msm_sdw_codec_enable_vi_feedback(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = NULL;
+ struct msm_sdw_priv *msm_sdw_p = NULL;
+ int ret = 0;
+
+ if (!w) {
+ pr_err("%s invalid params\n", __func__);
+ return -EINVAL;
+ }
+ codec = snd_soc_dapm_to_codec(w->dapm);
+ msm_sdw_p = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: num_dai %d stream name %s\n",
+ __func__, codec->component.num_dai, w->sname);
+
+ dev_dbg(codec->dev, "%s(): w->name %s event %d w->shift %d\n",
+ __func__, w->name, event, w->shift);
+ if (w->shift != AIF1_SDW_VIFEED) {
+ dev_err(codec->dev,
+ "%s:Error in enabling the vi feedback path\n",
+ __func__);
+ ret = -EINVAL;
+ goto out_vi;
+ }
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (test_bit(VI_SENSE_1, &msm_sdw_p->status_mask)) {
+ dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__);
+ /* Enable V&I sensing */
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x0F, 0x00);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20,
+ 0x00);
+ }
+ if (test_bit(VI_SENSE_2, &msm_sdw_p->status_mask)) {
+ dev_dbg(codec->dev, "%s: spkr2 enabled\n", __func__);
+ /* Enable V&I sensing */
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x0F,
+ 0x00);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x0F,
+ 0x00);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20,
+ 0x00);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20,
+ 0x00);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (test_bit(VI_SENSE_1, &msm_sdw_p->status_mask)) {
+ /* Disable V&I sensing */
+ dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x10,
+ 0x00);
+ }
+ if (test_bit(VI_SENSE_2, &msm_sdw_p->status_mask)) {
+ /* Disable V&I sensing */
+ dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x10,
+ 0x00);
+ snd_soc_update_bits(codec,
+ MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x10,
+ 0x00);
+ }
+ break;
+ }
+out_vi:
+ return ret;
+}
+
+static int msm_sdwm_handle_irq(void *handle,
+ irqreturn_t (*swrm_irq_handler)(int irq,
+ void *data),
+ void *swrm_handle,
+ int action)
+{
+ struct msm_sdw_priv *msm_sdw;
+ int ret = 0;
+
+ if (!handle) {
+ pr_err("%s: null handle received\n", __func__);
+ return -EINVAL;
+ }
+ msm_sdw = (struct msm_sdw_priv *) handle;
+
+ if (skip_irq)
+ return ret;
+
+ if (action) {
+ ret = request_threaded_irq(msm_sdw->sdw_irq, NULL,
+ swrm_irq_handler,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "swr_master_irq", swrm_handle);
+ if (ret)
+ dev_err(msm_sdw->dev, "%s: Failed to request irq %d\n",
+ __func__, ret);
+ } else
+ free_irq(msm_sdw->sdw_irq, swrm_handle);
+
+ return ret;
+}
+
+static void msm_sdw_codec_hd2_control(struct snd_soc_codec *codec,
+ u16 reg, int event)
+{
+ u16 hd2_scale_reg;
+ u16 hd2_enable_reg = 0;
+
+ if (reg == MSM_SDW_RX7_RX_PATH_CTL) {
+ hd2_scale_reg = MSM_SDW_RX7_RX_PATH_SEC3;
+ hd2_enable_reg = MSM_SDW_RX7_RX_PATH_CFG0;
+ }
+ if (reg == MSM_SDW_RX8_RX_PATH_CTL) {
+ hd2_scale_reg = MSM_SDW_RX8_RX_PATH_SEC3;
+ hd2_enable_reg = MSM_SDW_RX8_RX_PATH_CFG0;
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10);
+ snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01);
+ snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04);
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00);
+ snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00);
+ snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00);
+ }
+}
+
+static int msm_sdw_enable_swr(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct msm_sdw_priv *msm_sdw;
+ int i, ch_cnt;
+
+ msm_sdw = snd_soc_codec_get_drvdata(codec);
+
+ if (!msm_sdw->nr)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (!(strnstr(w->name, "RX4", sizeof("RX4 MIX"))) &&
+ !msm_sdw->rx_4_count)
+ msm_sdw->rx_4_count++;
+ if (!(strnstr(w->name, "RX5", sizeof("RX5 MIX"))) &&
+ !msm_sdw->rx_5_count)
+ msm_sdw->rx_5_count++;
+ ch_cnt = msm_sdw->rx_4_count + msm_sdw->rx_5_count;
+
+ for (i = 0; i < msm_sdw->nr; i++) {
+ swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev,
+ SWR_DEVICE_UP, NULL);
+ swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev,
+ SWR_SET_NUM_RX_CH, &ch_cnt);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!(strnstr(w->name, "RX4", sizeof("RX4 MIX"))) &&
+ msm_sdw->rx_4_count)
+ msm_sdw->rx_4_count--;
+ if (!(strnstr(w->name, "RX5", sizeof("RX5 MIX"))) &&
+ msm_sdw->rx_5_count)
+ msm_sdw->rx_5_count--;
+ ch_cnt = msm_sdw->rx_4_count + msm_sdw->rx_5_count;
+
+ for (i = 0; i < msm_sdw->nr; i++)
+ swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev,
+ SWR_SET_NUM_RX_CH, &ch_cnt);
+ break;
+ }
+ dev_dbg(msm_sdw->dev, "%s: current swr ch cnt: %d\n",
+ __func__, msm_sdw->rx_4_count + msm_sdw->rx_5_count);
+
+ return 0;
+}
+
+static int msm_sdw_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec);
+ u16 gain_reg;
+ u16 reg;
+ int val;
+ int offset_val = 0;
+
+ dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
+
+ if (!(strcmp(w->name, "RX INT4 INTERP"))) {
+ reg = MSM_SDW_RX7_RX_PATH_CTL;
+ gain_reg = MSM_SDW_RX7_RX_VOL_CTL;
+ } else if (!(strcmp(w->name, "RX INT5 INTERP"))) {
+ reg = MSM_SDW_RX8_RX_PATH_CTL;
+ gain_reg = MSM_SDW_RX8_RX_VOL_CTL;
+ } else {
+ dev_err(codec->dev, "%s: Interpolator reg not found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, reg, 0x10, 0x10);
+ msm_sdw_codec_hd2_control(codec, reg, event);
+ snd_soc_update_bits(codec, reg, 1 << 0x5, 1 << 0x5);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ msm_sdw_config_compander(codec, w->shift, event);
+ /* apply gain after int clk is enabled */
+ if ((msm_sdw->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
+ (msm_sdw->comp_enabled[COMP1] ||
+ msm_sdw->comp_enabled[COMP2]) &&
+ (gain_reg == MSM_SDW_RX7_RX_VOL_CTL ||
+ gain_reg == MSM_SDW_RX8_RX_VOL_CTL)) {
+ snd_soc_update_bits(codec, MSM_SDW_RX7_RX_PATH_SEC1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ MSM_SDW_RX7_RX_PATH_MIX_SEC0,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec, MSM_SDW_RX8_RX_PATH_SEC1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ MSM_SDW_RX8_RX_PATH_MIX_SEC0,
+ 0x01, 0x01);
+ offset_val = -2;
+ }
+ val = snd_soc_read(codec, gain_reg);
+ val += offset_val;
+ snd_soc_write(codec, gain_reg, val);
+ msm_sdw_config_ear_spkr_gain(codec, event, gain_reg);
+ snd_soc_update_bits(codec, reg, 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, reg, 1 << 0x5, 0 << 0x5);
+ snd_soc_update_bits(codec, reg, 0x40, 0x40);
+ snd_soc_update_bits(codec, reg, 0x40, 0x00);
+ msm_sdw_codec_hd2_control(codec, reg, event);
+ msm_sdw_config_compander(codec, w->shift, event);
+ if ((msm_sdw->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
+ (msm_sdw->comp_enabled[COMP1] ||
+ msm_sdw->comp_enabled[COMP2]) &&
+ (gain_reg == MSM_SDW_RX7_RX_VOL_CTL ||
+ gain_reg == MSM_SDW_RX8_RX_VOL_CTL)) {
+ snd_soc_update_bits(codec, MSM_SDW_RX7_RX_PATH_SEC1,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ MSM_SDW_RX7_RX_PATH_MIX_SEC0,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, MSM_SDW_RX8_RX_PATH_SEC1,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ MSM_SDW_RX8_RX_PATH_MIX_SEC0,
+ 0x01, 0x00);
+ offset_val = 2;
+ val = snd_soc_read(codec, gain_reg);
+ val += offset_val;
+ snd_soc_write(codec, gain_reg, val);
+ }
+ msm_sdw_config_ear_spkr_gain(codec, event, gain_reg);
+ break;
+ };
+
+ return 0;
+}
+
+static int msm_sdw_config_ear_spkr_gain(struct snd_soc_codec *codec,
+ int event, int gain_reg)
+{
+ int comp_gain_offset, val;
+ struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec);
+
+ switch (msm_sdw->spkr_mode) {
+ /* Compander gain in SPKR_MODE1 case is 12 dB */
+ case SPKR_MODE_1:
+ comp_gain_offset = -12;
+ break;
+ /* Default case compander gain is 15 dB */
+ default:
+ comp_gain_offset = -15;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Apply ear spkr gain only if compander is enabled */
+ if (msm_sdw->comp_enabled[COMP1] &&
+ (gain_reg == MSM_SDW_RX7_RX_VOL_CTL) &&
+ (msm_sdw->ear_spkr_gain != 0)) {
+ /* For example, val is -8(-12+5-1) for 4dB of gain */
+ val = comp_gain_offset + msm_sdw->ear_spkr_gain - 1;
+ snd_soc_write(codec, gain_reg, val);
+
+ dev_dbg(codec->dev, "%s: RX4 Volume %d dB\n",
+ __func__, val);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * Reset RX4 volume to 0 dB if compander is enabled and
+ * ear_spkr_gain is non-zero.
+ */
+ if (msm_sdw->comp_enabled[COMP1] &&
+ (gain_reg == MSM_SDW_RX7_RX_VOL_CTL) &&
+ (msm_sdw->ear_spkr_gain != 0)) {
+ snd_soc_write(codec, gain_reg, 0x0);
+
+ dev_dbg(codec->dev, "%s: Reset RX4 Volume to 0 dB\n",
+ __func__);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int msm_sdw_codec_spk_boost_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 boost_path_ctl, boost_path_cfg1;
+ u16 reg;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ if (!strcmp(w->name, "RX INT4 CHAIN")) {
+ boost_path_ctl = MSM_SDW_BOOST0_BOOST_PATH_CTL;
+ boost_path_cfg1 = MSM_SDW_RX7_RX_PATH_CFG1;
+ reg = MSM_SDW_RX7_RX_PATH_CTL;
+ } else if (!strcmp(w->name, "RX INT5 CHAIN")) {
+ boost_path_ctl = MSM_SDW_BOOST1_BOOST_PATH_CTL;
+ boost_path_cfg1 = MSM_SDW_RX8_RX_PATH_CFG1;
+ reg = MSM_SDW_RX8_RX_PATH_CTL;
+ } else {
+ dev_err(codec->dev, "%s: boost reg not found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10);
+ snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01);
+ snd_soc_update_bits(codec, reg, 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00);
+ snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00);
+ break;
+ };
+
+ return 0;
+}
+
+static int msm_sdw_config_compander(struct snd_soc_codec *codec, int comp,
+ int event)
+{
+ struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec);
+ u16 comp_ctl0_reg, rx_path_cfg0_reg;
+
+ if (comp < COMP1 || comp >= COMP_MAX)
+ return 0;
+
+ dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n",
+ __func__, event, comp + 1, msm_sdw->comp_enabled[comp]);
+
+ if (!msm_sdw->comp_enabled[comp])
+ return 0;
+
+ comp_ctl0_reg = MSM_SDW_COMPANDER7_CTL0 + (comp * 0x20);
+ rx_path_cfg0_reg = MSM_SDW_RX7_RX_PATH_CFG0 + (comp * 0x1E0);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Compander Clock */
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00);
+ snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04);
+ snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00);
+ }
+
+ return 0;
+}
+
+static int msm_sdw_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int comp = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = msm_sdw->comp_enabled[comp];
+ return 0;
+}
+
+static int msm_sdw_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec);
+ int comp = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n",
+ __func__, comp + 1, msm_sdw->comp_enabled[comp], value);
+ msm_sdw->comp_enabled[comp] = value;
+
+ return 0;
+}
+
+static int msm_sdw_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = msm_sdw->ear_spkr_gain;
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_sdw_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec);
+
+ msm_sdw->ear_spkr_gain = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: gain = %d\n", __func__,
+ msm_sdw->ear_spkr_gain);
+
+ return 0;
+}
+
+static int msm_sdw_vi_feed_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = msm_sdw_p->vi_feed_value;
+
+ return 0;
+}
+
+static int msm_sdw_vi_feed_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec);
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n",
+ __func__, enable, port_id, dai_id);
+
+ msm_sdw_p->vi_feed_value = ucontrol->value.integer.value[0];
+
+ mutex_lock(&msm_sdw_p->codec_mutex);
+ if (enable) {
+ if (port_id == MSM_SDW_TX0 && !test_bit(VI_SENSE_1,
+ &msm_sdw_p->status_mask))
+ set_bit(VI_SENSE_1, &msm_sdw_p->status_mask);
+ if (port_id == MSM_SDW_TX1 && !test_bit(VI_SENSE_2,
+ &msm_sdw_p->status_mask))
+ set_bit(VI_SENSE_2, &msm_sdw_p->status_mask);
+ } else {
+ if (port_id == MSM_SDW_TX0 && test_bit(VI_SENSE_1,
+ &msm_sdw_p->status_mask))
+ clear_bit(VI_SENSE_1, &msm_sdw_p->status_mask);
+ if (port_id == MSM_SDW_TX1 && test_bit(VI_SENSE_2,
+ &msm_sdw_p->status_mask))
+ clear_bit(VI_SENSE_2, &msm_sdw_p->status_mask);
+ }
+ mutex_unlock(&msm_sdw_p->codec_mutex);
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL);
+
+ return 0;
+}
+
+static int msm_sdw_mclk_enable(struct msm_sdw_priv *msm_sdw,
+ int mclk_enable, bool dapm)
+{
+ dev_dbg(msm_sdw->dev, "%s: mclk_enable = %u, dapm = %d clk_users= %d\n",
+ __func__, mclk_enable, dapm, msm_sdw->sdw_mclk_users);
+ if (mclk_enable) {
+ msm_sdw->sdw_mclk_users++;
+ if (msm_sdw->sdw_mclk_users == 1) {
+ regmap_update_bits(msm_sdw->regmap,
+ MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL,
+ 0x01, 0x01);
+ regmap_update_bits(msm_sdw->regmap,
+ MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x01, 0x01);
+ /* 9.6MHz MCLK, set value 0x00 if other frequency */
+ regmap_update_bits(msm_sdw->regmap,
+ MSM_SDW_TOP_FREQ_MCLK, 0x01, 0x01);
+ }
+ } else {
+ msm_sdw->sdw_mclk_users--;
+ if (msm_sdw->sdw_mclk_users == 0) {
+ regmap_update_bits(msm_sdw->regmap,
+ MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL,
+ 0x01, 0x00);
+ regmap_update_bits(msm_sdw->regmap,
+ MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x01, 0x00);
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(msm_sdw_mclk_enable);
+
+static int msm_sdw_swrm_read(void *handle, int reg)
+{
+ struct msm_sdw_priv *msm_sdw;
+ unsigned short sdw_rd_addr_base;
+ unsigned short sdw_rd_data_base;
+ int val, ret;
+
+ if (!handle) {
+ pr_err("%s: NULL handle\n", __func__);
+ return -EINVAL;
+ }
+ msm_sdw = (struct msm_sdw_priv *)handle;
+
+ dev_dbg(msm_sdw->dev, "%s: Reading soundwire register, 0x%x\n",
+ __func__, reg);
+ sdw_rd_addr_base = MSM_SDW_AHB_BRIDGE_RD_ADDR_0;
+ sdw_rd_data_base = MSM_SDW_AHB_BRIDGE_RD_DATA_0;
+ /*
+ * Add sleep as SWR slave access read takes time.
+ * Allow for RD_DONE to complete for previous register if any.
+ */
+ usleep_range(100, 105);
+
+ /* read_lock */
+ mutex_lock(&msm_sdw->sdw_read_lock);
+ ret = regmap_bulk_write(msm_sdw->regmap, sdw_rd_addr_base,
+ (u8 *)&reg, 4);
+ if (ret < 0) {
+ dev_err(msm_sdw->dev, "%s: RD Addr Failure\n", __func__);
+ goto err;
+ }
+ /* Add sleep for SWR register read value to get updated. */
+ usleep_range(100, 105);
+ /* Check for RD value */
+ ret = regmap_bulk_read(msm_sdw->regmap, sdw_rd_data_base,
+ (u8 *)&val, 4);
+ if (ret < 0) {
+ dev_err(msm_sdw->dev, "%s: RD Data Failure\n", __func__);
+ goto err;
+ }
+ ret = val;
+err:
+ /* read_unlock */
+ mutex_unlock(&msm_sdw->sdw_read_lock);
+ return ret;
+}
+
+static int msm_sdw_bulk_write(struct msm_sdw_priv *msm_sdw,
+ struct msm_sdw_reg_val *bulk_reg,
+ size_t len)
+{
+ int i, ret = 0;
+ unsigned short sdw_wr_addr_base;
+ unsigned short sdw_wr_data_base;
+
+ sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0;
+ sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0;
+
+ for (i = 0; i < len; i += 2) {
+ /*
+ * Add sleep as SWR slave write takes time.
+ * Allow for any previous pending write to complete.
+ */
+ usleep_range(100, 105);
+ /* First Write the Data to register */
+ ret = regmap_bulk_write(msm_sdw->regmap,
+ sdw_wr_data_base, bulk_reg[i].buf, 4);
+ if (ret < 0) {
+ dev_err(msm_sdw->dev, "%s: WR Data Failure\n",
+ __func__);
+ break;
+ }
+ /* Next Write Address */
+ ret = regmap_bulk_write(msm_sdw->regmap,
+ sdw_wr_addr_base, bulk_reg[i+1].buf, 4);
+ if (ret < 0) {
+ dev_err(msm_sdw->dev,
+ "%s: WR Addr Failure: 0x%x\n",
+ __func__, (u32)(bulk_reg[i+1].buf[0]));
+ break;
+ }
+ }
+ return ret;
+}
+
+static int msm_sdw_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len)
+{
+ struct msm_sdw_priv *msm_sdw;
+ struct msm_sdw_reg_val *bulk_reg;
+ unsigned short sdw_wr_addr_base;
+ unsigned short sdw_wr_data_base;
+ int i, j, ret;
+
+ if (!handle) {
+ pr_err("%s: NULL handle\n", __func__);
+ return -EINVAL;
+ }
+
+ msm_sdw = (struct msm_sdw_priv *)handle;
+ if (len <= 0) {
+ dev_err(msm_sdw->dev,
+ "%s: Invalid size: %zu\n", __func__, len);
+ return -EINVAL;
+ }
+
+ sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0;
+ sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0;
+
+ bulk_reg = kzalloc((2 * len * sizeof(struct msm_sdw_reg_val)),
+ GFP_KERNEL);
+ if (!bulk_reg)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < (len * 2); i += 2, j++) {
+ bulk_reg[i].reg = sdw_wr_data_base;
+ bulk_reg[i].buf = (u8 *)(&val[j]);
+ bulk_reg[i].bytes = 4;
+ bulk_reg[i+1].reg = sdw_wr_addr_base;
+ bulk_reg[i+1].buf = (u8 *)(&reg[j]);
+ bulk_reg[i+1].bytes = 4;
+ }
+ mutex_lock(&msm_sdw->sdw_write_lock);
+
+ ret = msm_sdw_bulk_write(msm_sdw, bulk_reg, (len * 2));
+ if (ret)
+ dev_err(msm_sdw->dev, "%s: swrm bulk write failed, ret: %d\n",
+ __func__, ret);
+
+ mutex_unlock(&msm_sdw->sdw_write_lock);
+ kfree(bulk_reg);
+
+ return ret;
+}
+
+static int msm_sdw_swrm_write(void *handle, int reg, int val)
+{
+ struct msm_sdw_priv *msm_sdw;
+ unsigned short sdw_wr_addr_base;
+ unsigned short sdw_wr_data_base;
+ struct msm_sdw_reg_val bulk_reg[2];
+ int ret;
+
+ if (!handle) {
+ pr_err("%s: NULL handle\n", __func__);
+ return -EINVAL;
+ }
+ msm_sdw = (struct msm_sdw_priv *)handle;
+
+ sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0;
+ sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0;
+
+ /* First Write the Data to register */
+ bulk_reg[0].reg = sdw_wr_data_base;
+ bulk_reg[0].buf = (u8 *)(&val);
+ bulk_reg[0].bytes = 4;
+ bulk_reg[1].reg = sdw_wr_addr_base;
+ bulk_reg[1].buf = (u8 *)(&reg);
+ bulk_reg[1].bytes = 4;
+
+ mutex_lock(&msm_sdw->sdw_write_lock);
+
+ ret = msm_sdw_bulk_write(msm_sdw, bulk_reg, 2);
+ if (ret < 0)
+ dev_err(msm_sdw->dev, "%s: WR Data Failure\n", __func__);
+
+ mutex_unlock(&msm_sdw->sdw_write_lock);
+ return ret;
+}
+
+static int msm_sdw_swrm_clock(void *handle, bool enable)
+{
+ struct msm_sdw_priv *msm_sdw = (struct msm_sdw_priv *) handle;
+
+ mutex_lock(&msm_sdw->sdw_clk_lock);
+
+ dev_dbg(msm_sdw->dev, "%s: swrm clock %s\n",
+ __func__, (enable ? "enable" : "disable"));
+ if (enable) {
+ msm_sdw->sdw_clk_users++;
+ if (msm_sdw->sdw_clk_users == 1) {
+ msm_int_enable_sdw_cdc_clk(msm_sdw, 1, true);
+ msm_sdw_mclk_enable(msm_sdw, 1, true);
+ regmap_update_bits(msm_sdw->regmap,
+ MSM_SDW_CLK_RST_CTRL_SWR_CONTROL, 0x01, 0x01);
+ msm_enable_sdw_npl_clk(msm_sdw, true);
+ msm_cdc_pinctrl_select_active_state(
+ msm_sdw->sdw_gpio_p);
+ }
+ } else {
+ msm_sdw->sdw_clk_users--;
+ if (msm_sdw->sdw_clk_users == 0) {
+ regmap_update_bits(msm_sdw->regmap,
+ MSM_SDW_CLK_RST_CTRL_SWR_CONTROL,
+ 0x01, 0x00);
+ msm_sdw_mclk_enable(msm_sdw, 0, true);
+ msm_int_enable_sdw_cdc_clk(msm_sdw, 0, true);
+ msm_enable_sdw_npl_clk(msm_sdw, false);
+ msm_cdc_pinctrl_select_sleep_state(msm_sdw->sdw_gpio_p);
+ }
+ }
+ dev_dbg(msm_sdw->dev, "%s: swrm clock users %d\n",
+ __func__, msm_sdw->sdw_clk_users);
+ mutex_unlock(&msm_sdw->sdw_clk_lock);
+ return 0;
+}
+
+static int msm_sdw_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n",
+ __func__,
+ substream->name, substream->stream);
+ return 0;
+}
+
+static int msm_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ u8 clk_fs_rate, fs_rate;
+
+ dev_dbg(dai->codec->dev,
+ "%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n",
+ __func__, dai->name, dai->id, params_rate(params),
+ params_channels(params), params_format(params));
+
+ switch (params_rate(params)) {
+ case 8000:
+ clk_fs_rate = 0x00;
+ fs_rate = 0x00;
+ break;
+ case 16000:
+ clk_fs_rate = 0x01;
+ fs_rate = 0x01;
+ break;
+ case 32000:
+ clk_fs_rate = 0x02;
+ fs_rate = 0x03;
+ break;
+ case 48000:
+ clk_fs_rate = 0x03;
+ fs_rate = 0x04;
+ break;
+ case 96000:
+ clk_fs_rate = 0x04;
+ fs_rate = 0x05;
+ break;
+ case 192000:
+ clk_fs_rate = 0x05;
+ fs_rate = 0x06;
+ break;
+ default:
+ dev_err(dai->codec->dev,
+ "%s: Invalid sampling rate %d\n", __func__,
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ snd_soc_update_bits(dai->codec,
+ MSM_SDW_TOP_TX_I2S_CTL, 0x1C,
+ (clk_fs_rate << 2));
+ } else {
+ snd_soc_update_bits(dai->codec,
+ MSM_SDW_TOP_RX_I2S_CTL, 0x1C,
+ (clk_fs_rate << 2));
+ snd_soc_update_bits(dai->codec,
+ MSM_SDW_RX7_RX_PATH_CTL, 0x0F,
+ fs_rate);
+ snd_soc_update_bits(dai->codec,
+ MSM_SDW_RX8_RX_PATH_CTL, 0x0F,
+ fs_rate);
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ snd_soc_update_bits(dai->codec,
+ MSM_SDW_TOP_TX_I2S_CTL, 0x20, 0x20);
+ else
+ snd_soc_update_bits(dai->codec,
+ MSM_SDW_TOP_RX_I2S_CTL, 0x20, 0x20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ snd_soc_update_bits(dai->codec,
+ MSM_SDW_TOP_TX_I2S_CTL, 0x20, 0x00);
+ else
+ snd_soc_update_bits(dai->codec,
+ MSM_SDW_TOP_RX_I2S_CTL, 0x20, 0x00);
+ break;
+ default:
+ dev_err(dai->codec->dev, "%s: wrong format selected\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void msm_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ dev_dbg(dai->codec->dev,
+ "%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+}
+
+static ssize_t msm_sdw_codec_version_read(struct snd_info_entry *entry,
+ void *file_private_data,
+ struct file *file,
+ char __user *buf, size_t count,
+ loff_t pos)
+{
+ struct msm_sdw_priv *msm_sdw;
+ char buffer[MSM_SDW_VERSION_ENTRY_SIZE];
+ int len = 0;
+
+ msm_sdw = (struct msm_sdw_priv *) entry->private_data;
+ if (!msm_sdw) {
+ pr_err("%s: msm_sdw priv is null\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (msm_sdw->version) {
+ case MSM_SDW_VERSION_1_0:
+ len = snprintf(buffer, sizeof(buffer), "SDW-CDC_1_0\n");
+ break;
+ default:
+ len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n");
+ }
+
+ return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static struct snd_info_entry_ops msm_sdw_codec_info_ops = {
+ .read = msm_sdw_codec_version_read,
+};
+
+/*
+ * msm_sdw_codec_info_create_codec_entry - creates msm_sdw module
+ * @codec_root: The parent directory
+ * @codec: Codec instance
+ *
+ * Creates msm_sdw module and version entry under the given
+ * parent directory.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int msm_sdw_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+ struct snd_soc_codec *codec)
+{
+ struct snd_info_entry *version_entry;
+ struct msm_sdw_priv *msm_sdw;
+ struct snd_soc_card *card;
+
+ if (!codec_root || !codec)
+ return -EINVAL;
+
+ msm_sdw = snd_soc_codec_get_drvdata(codec);
+ card = codec->component.card;
+ msm_sdw->entry = snd_register_module_info(codec_root->module,
+ "152c1000.msm-sdw-codec",
+ codec_root);
+ if (!msm_sdw->entry) {
+ dev_err(codec->dev, "%s: failed to create msm_sdw entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry = snd_info_create_card_entry(card->snd_card,
+ "version",
+ msm_sdw->entry);
+ if (!version_entry) {
+ dev_err(codec->dev, "%s: failed to create msm_sdw version entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry->private_data = msm_sdw;
+ version_entry->size = MSM_SDW_VERSION_ENTRY_SIZE;
+ version_entry->content = SNDRV_INFO_CONTENT_DATA;
+ version_entry->c.ops = &msm_sdw_codec_info_ops;
+
+ if (snd_info_register(version_entry) < 0) {
+ snd_info_free_entry(version_entry);
+ return -ENOMEM;
+ }
+ msm_sdw->version_entry = version_entry;
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_sdw_codec_info_create_codec_entry);
+
+static struct snd_soc_dai_ops msm_sdw_dai_ops = {
+ .startup = msm_sdw_startup,
+ .shutdown = msm_sdw_shutdown,
+ .hw_params = msm_sdw_hw_params,
+};
+
+static struct snd_soc_dai_driver msm_sdw_dai[] = {
+ {
+ .name = "msm_sdw_i2s_rx1",
+ .id = AIF1_SDW_PB,
+ .playback = {
+ .stream_name = "AIF1_SDW Playback",
+ .rates = MSM_SDW_RATES,
+ .formats = MSM_SDW_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &msm_sdw_dai_ops,
+ },
+ {
+ .name = "msm_sdw_vifeedback",
+ .id = AIF1_SDW_VIFEED,
+ .capture = {
+ .stream_name = "VIfeed_SDW",
+ .rates = MSM_SDW_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 2,
+ .channels_max = 4,
+ },
+ .ops = &msm_sdw_dai_ops,
+ },
+};
+
+static const char * const rx_mix1_text[] = {
+ "ZERO", "RX4", "RX5"
+};
+
+static const char * const msm_sdw_ear_spkr_pa_gain_text[] = {
+ "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB",
+ "G_4_DB", "G_5_DB", "G_6_DB"
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(msm_sdw_ear_spkr_pa_gain_enum,
+ msm_sdw_ear_spkr_pa_gain_text);
+/* RX4 MIX1 */
+static const struct soc_enum rx4_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(MSM_SDW_TOP_RX7_PATH_INPUT0_MUX,
+ 0, 3, rx_mix1_text);
+
+static const struct soc_enum rx4_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(MSM_SDW_TOP_RX7_PATH_INPUT1_MUX,
+ 0, 3, rx_mix1_text);
+
+/* RX5 MIX1 */
+static const struct soc_enum rx5_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(MSM_SDW_TOP_RX8_PATH_INPUT0_MUX,
+ 0, 3, rx_mix1_text);
+
+static const struct soc_enum rx5_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(MSM_SDW_TOP_RX8_PATH_INPUT1_MUX,
+ 0, 3, rx_mix1_text);
+
+static const struct snd_kcontrol_new rx4_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX4 MIX1 INP1 Mux", rx4_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx4_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX4 MIX1 INP2 Mux", rx4_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx5_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX5 MIX1 INP1 Mux", rx5_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx5_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX5 MIX1 INP2 Mux", rx5_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new aif1_vi_mixer[] = {
+ SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, MSM_SDW_TX0, 1, 0,
+ msm_sdw_vi_feed_mixer_get, msm_sdw_vi_feed_mixer_put),
+ SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, MSM_SDW_TX1, 1, 0,
+ msm_sdw_vi_feed_mixer_get, msm_sdw_vi_feed_mixer_put),
+};
+
+static const struct snd_soc_dapm_widget msm_sdw_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("I2S RX4", "AIF1_SDW Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("I2S RX5", "AIF1_SDW Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF1_SDW VI", "VIfeed_SDW", 0, SND_SOC_NOPM,
+ AIF1_SDW_VIFEED, 0, msm_sdw_codec_enable_vi_feedback,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("AIF1_VI_SDW Mixer", SND_SOC_NOPM, AIF1_SDW_VIFEED,
+ 0, aif1_vi_mixer, ARRAY_SIZE(aif1_vi_mixer)),
+
+ SND_SOC_DAPM_MUX_E("RX4 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx4_mix1_inp1_mux, msm_sdw_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX4 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx4_mix1_inp2_mux, msm_sdw_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX5 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx5_mix1_inp1_mux, msm_sdw_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX5 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx5_mix1_inp2_mux, msm_sdw_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("RX4 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX5 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER_E("RX INT4 INTERP", SND_SOC_NOPM,
+ COMP1, 0, NULL, 0, msm_sdw_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX INT5 INTERP", SND_SOC_NOPM,
+ COMP2, 0, NULL, 0, msm_sdw_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("RX INT4 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, msm_sdw_codec_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX INT5 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, msm_sdw_codec_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("VIINPUT_SDW"),
+
+ SND_SOC_DAPM_OUTPUT("SPK1 OUT"),
+ SND_SOC_DAPM_OUTPUT("SPK2 OUT"),
+
+ SND_SOC_DAPM_SUPPLY_S("SDW_CONN", -1, MSM_SDW_TOP_I2S_CLK,
+ 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("INT_MCLK1", -2, SND_SOC_NOPM, 0, 0,
+ msm_int_mclk1_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("SDW_RX_I2S_CLK",
+ MSM_SDW_TOP_RX_I2S_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SDW_TX_I2S_CLK",
+ MSM_SDW_TOP_TX_I2S_CTL, 0, 0, NULL, 0),
+};
+
+static const struct snd_kcontrol_new msm_sdw_snd_controls[] = {
+ SOC_ENUM_EXT("EAR SPKR PA Gain", msm_sdw_ear_spkr_pa_gain_enum,
+ msm_sdw_ear_spkr_pa_gain_get,
+ msm_sdw_ear_spkr_pa_gain_put),
+ SOC_SINGLE_SX_TLV("RX4 Digital Volume", MSM_SDW_RX7_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX5 Digital Volume", MSM_SDW_RX8_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMP1, 1, 0,
+ msm_sdw_get_compander, msm_sdw_set_compander),
+ SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMP2, 1, 0,
+ msm_sdw_get_compander, msm_sdw_set_compander),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ {"AIF1_SDW VI", NULL, "SDW_TX_I2S_CLK"},
+ {"SDW_TX_I2S_CLK", NULL, "INT_MCLK1"},
+ {"SDW_TX_I2S_CLK", NULL, "SDW_CONN"},
+
+ /* VI Feedback */
+ {"AIF1_VI_SDW Mixer", "SPKR_VI_1", "VIINPUT_SDW"},
+ {"AIF1_VI_SDW Mixer", "SPKR_VI_2", "VIINPUT_SDW"},
+ {"AIF1_SDW VI", NULL, "AIF1_VI_SDW Mixer"},
+
+ {"SDW_RX_I2S_CLK", NULL, "INT_MCLK1"},
+ {"SDW_RX_I2S_CLK", NULL, "SDW_CONN"},
+ {"I2S RX4", NULL, "SDW_RX_I2S_CLK"},
+ {"I2S RX5", NULL, "SDW_RX_I2S_CLK"},
+
+ {"RX4 MIX1 INP1", "RX4", "I2S RX4"},
+ {"RX4 MIX1 INP1", "RX5", "I2S RX5"},
+ {"RX4 MIX1 INP2", "RX4", "I2S RX4"},
+ {"RX4 MIX1 INP2", "RX5", "I2S RX5"},
+ {"RX5 MIX1 INP1", "RX4", "I2S RX4"},
+ {"RX5 MIX1 INP1", "RX5", "I2S RX5"},
+ {"RX5 MIX1 INP2", "RX4", "I2S RX4"},
+ {"RX5 MIX1 INP2", "RX5", "I2S RX5"},
+
+ {"RX4 MIX1", NULL, "RX4 MIX1 INP1"},
+ {"RX4 MIX1", NULL, "RX4 MIX1 INP2"},
+ {"RX5 MIX1", NULL, "RX5 MIX1 INP1"},
+ {"RX5 MIX1", NULL, "RX5 MIX1 INP2"},
+
+ {"RX INT4 INTERP", NULL, "RX4 MIX1"},
+ {"RX INT4 CHAIN", NULL, "RX INT4 INTERP"},
+ {"SPK1 OUT", NULL, "RX INT4 CHAIN"},
+
+ {"RX INT5 INTERP", NULL, "RX5 MIX1"},
+ {"RX INT5 CHAIN", NULL, "RX INT5 INTERP"},
+ {"SPK2 OUT", NULL, "RX INT5 CHAIN"},
+};
+
+static const struct msm_sdw_reg_mask_val msm_sdw_reg_init[] = {
+ {MSM_SDW_BOOST0_BOOST_CFG1, 0x3F, 0x12},
+ {MSM_SDW_BOOST0_BOOST_CFG2, 0x1C, 0x08},
+ {MSM_SDW_COMPANDER7_CTL7, 0x1E, 0x18},
+ {MSM_SDW_BOOST1_BOOST_CFG1, 0x3F, 0x12},
+ {MSM_SDW_BOOST1_BOOST_CFG2, 0x1C, 0x08},
+ {MSM_SDW_COMPANDER8_CTL7, 0x1E, 0x18},
+ {MSM_SDW_BOOST0_BOOST_CTL, 0x70, 0x50},
+ {MSM_SDW_BOOST1_BOOST_CTL, 0x70, 0x50},
+ {MSM_SDW_RX7_RX_PATH_CFG1, 0x08, 0x08},
+ {MSM_SDW_RX8_RX_PATH_CFG1, 0x08, 0x08},
+ {MSM_SDW_TOP_TOP_CFG1, 0x02, 0x02},
+ {MSM_SDW_TOP_TOP_CFG1, 0x01, 0x01},
+ {MSM_SDW_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {MSM_SDW_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {MSM_SDW_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {MSM_SDW_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x80},
+ {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x80},
+ {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x01},
+ {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x01},
+ {MSM_SDW_RX7_RX_PATH_CFG0, 0x01, 0x01},
+ {MSM_SDW_RX8_RX_PATH_CFG0, 0x01, 0x01},
+ {MSM_SDW_RX7_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {MSM_SDW_RX8_RX_PATH_MIX_CFG, 0x01, 0x01},
+};
+
+static void msm_sdw_init_reg(struct snd_soc_codec *codec)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(msm_sdw_reg_init); i++)
+ snd_soc_update_bits(codec,
+ msm_sdw_reg_init[i].reg,
+ msm_sdw_reg_init[i].mask,
+ msm_sdw_reg_init[i].val);
+}
+
+static int msm_sdw_notifier_service_cb(struct notifier_block *nb,
+ unsigned long opcode, void *ptr)
+{
+ int i;
+ struct msm_sdw_priv *msm_sdw = container_of(nb,
+ struct msm_sdw_priv,
+ service_nb);
+ bool adsp_ready = false;
+ unsigned long timeout;
+ static bool initial_boot = true;
+
+ pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode);
+
+ mutex_lock(&msm_sdw->codec_mutex);
+ switch (opcode) {
+ case AUDIO_NOTIFIER_SERVICE_DOWN:
+ if (initial_boot) {
+ initial_boot = false;
+ break;
+ }
+ msm_sdw->int_mclk1_enabled = false;
+ msm_sdw->dev_up = false;
+ for (i = 0; i < msm_sdw->nr; i++)
+ swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev,
+ SWR_DEVICE_DOWN, NULL);
+ break;
+ case AUDIO_NOTIFIER_SERVICE_UP:
+ if (initial_boot)
+ initial_boot = false;
+ if (!q6core_is_adsp_ready()) {
+ dev_dbg(msm_sdw->dev, "ADSP isn't ready\n");
+ timeout = jiffies +
+ msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+ while (!time_after(jiffies, timeout)) {
+ if (!q6core_is_adsp_ready()) {
+ dev_dbg(msm_sdw->dev,
+ "ADSP isn't ready\n");
+ } else {
+ dev_dbg(msm_sdw->dev,
+ "ADSP is ready\n");
+ adsp_ready = true;
+ goto powerup;
+ }
+ }
+ } else {
+ adsp_ready = true;
+ dev_dbg(msm_sdw->dev, "%s: DSP is ready\n", __func__);
+ }
+powerup:
+ if (adsp_ready) {
+ msm_sdw->dev_up = true;
+ msm_sdw_init_reg(msm_sdw->codec);
+ regcache_mark_dirty(msm_sdw->regmap);
+ regcache_sync(msm_sdw->regmap);
+ msm_sdw_set_spkr_mode(msm_sdw->codec,
+ msm_sdw->spkr_mode);
+ }
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&msm_sdw->codec_mutex);
+ return NOTIFY_OK;
+}
+
+static int msm_sdw_codec_probe(struct snd_soc_codec *codec)
+{
+ struct msm_sdw_priv *msm_sdw;
+ int i, ret;
+
+ msm_sdw = snd_soc_codec_get_drvdata(codec);
+ if (!msm_sdw) {
+ pr_err("%s:SDW priv data null\n", __func__);
+ return -EINVAL;
+ }
+ msm_sdw->codec = codec;
+ for (i = 0; i < COMP_MAX; i++)
+ msm_sdw->comp_enabled[i] = 0;
+
+ msm_sdw->spkr_gain_offset = RX_GAIN_OFFSET_0_DB;
+ msm_sdw_init_reg(codec);
+ msm_sdw->version = MSM_SDW_VERSION_1_0;
+
+ msm_sdw->service_nb.notifier_call = msm_sdw_notifier_service_cb;
+ ret = audio_notifier_register("msm_sdw",
+ AUDIO_NOTIFIER_ADSP_DOMAIN,
+ &msm_sdw->service_nb);
+ if (ret < 0)
+ dev_err(msm_sdw->dev,
+ "%s: Audio notifier register failed ret = %d\n",
+ __func__, ret);
+ return 0;
+}
+
+static int msm_sdw_codec_remove(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static struct regmap *msm_sdw_get_regmap(struct device *dev)
+{
+ struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev);
+
+ return msm_sdw->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_msm_sdw = {
+ .probe = msm_sdw_codec_probe,
+ .remove = msm_sdw_codec_remove,
+ .controls = msm_sdw_snd_controls,
+ .num_controls = ARRAY_SIZE(msm_sdw_snd_controls),
+ .dapm_widgets = msm_sdw_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(msm_sdw_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .get_regmap = msm_sdw_get_regmap,
+};
+
+static void msm_sdw_add_child_devices(struct work_struct *work)
+{
+ struct msm_sdw_priv *msm_sdw;
+ struct platform_device *pdev;
+ struct device_node *node;
+ struct msm_sdw_ctrl_data *sdw_ctrl_data = NULL, *temp;
+ int ret, ctrl_num = 0;
+ struct wcd_sdw_ctrl_platform_data *platdata;
+ char plat_dev_name[MSM_SDW_STRING_LEN];
+
+ msm_sdw = container_of(work, struct msm_sdw_priv,
+ msm_sdw_add_child_devices_work);
+ if (!msm_sdw) {
+ pr_err("%s: Memory for msm_sdw does not exist\n",
+ __func__);
+ return;
+ }
+ if (!msm_sdw->dev->of_node) {
+ dev_err(msm_sdw->dev,
+ "%s: DT node for msm_sdw does not exist\n", __func__);
+ return;
+ }
+
+ platdata = &msm_sdw->sdw_plat_data;
+
+ for_each_available_child_of_node(msm_sdw->dev->of_node, node) {
+ if (!strcmp(node->name, "swr_master"))
+ strlcpy(plat_dev_name, "msm_sdw_swr_ctrl",
+ (MSM_SDW_STRING_LEN - 1));
+ else if (strnstr(node->name, "msm_cdc_pinctrl",
+ strlen("msm_cdc_pinctrl")) != NULL)
+ strlcpy(plat_dev_name, node->name,
+ (MSM_SDW_STRING_LEN - 1));
+ else
+ continue;
+
+ pdev = platform_device_alloc(plat_dev_name, -1);
+ if (!pdev) {
+ dev_err(msm_sdw->dev, "%s: pdev memory alloc failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err;
+ }
+ pdev->dev.parent = msm_sdw->dev;
+ pdev->dev.of_node = node;
+
+ if (!strcmp(node->name, "swr_master")) {
+ ret = platform_device_add_data(pdev, platdata,
+ sizeof(*platdata));
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: cannot add plat data ctrl:%d\n",
+ __func__, ctrl_num);
+ goto fail_pdev_add;
+ }
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: Cannot add platform device\n",
+ __func__);
+ goto fail_pdev_add;
+ }
+
+ if (!strcmp(node->name, "swr_master")) {
+ temp = krealloc(sdw_ctrl_data,
+ (ctrl_num + 1) * sizeof(
+ struct msm_sdw_ctrl_data),
+ GFP_KERNEL);
+ if (!temp) {
+ dev_err(&pdev->dev, "out of memory\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ sdw_ctrl_data = temp;
+ sdw_ctrl_data[ctrl_num].sdw_pdev = pdev;
+ ctrl_num++;
+ dev_dbg(&pdev->dev,
+ "%s: Added soundwire ctrl device(s)\n",
+ __func__);
+ msm_sdw->nr = ctrl_num;
+ msm_sdw->sdw_ctrl_data = sdw_ctrl_data;
+ }
+ }
+
+ return;
+fail_pdev_add:
+ platform_device_put(pdev);
+err:
+ return;
+}
+
+static int msm_sdw_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct msm_sdw_priv *msm_sdw;
+ int adsp_state;
+
+ adsp_state = apr_get_subsys_state();
+ if (adsp_state != APR_SUBSYS_LOADED ||
+ !q6core_is_adsp_ready()) {
+ dev_err(&pdev->dev, "Adsp is not loaded yet %d\n",
+ adsp_state);
+ return -EPROBE_DEFER;
+ }
+
+ msm_sdw = devm_kzalloc(&pdev->dev, sizeof(struct msm_sdw_priv),
+ GFP_KERNEL);
+ if (!msm_sdw)
+ return -ENOMEM;
+ dev_set_drvdata(&pdev->dev, msm_sdw);
+ msm_sdw->dev_up = true;
+
+ msm_sdw->dev = &pdev->dev;
+ INIT_WORK(&msm_sdw->msm_sdw_add_child_devices_work,
+ msm_sdw_add_child_devices);
+ msm_sdw->sdw_plat_data.handle = (void *) msm_sdw;
+ msm_sdw->sdw_plat_data.read = msm_sdw_swrm_read;
+ msm_sdw->sdw_plat_data.write = msm_sdw_swrm_write;
+ msm_sdw->sdw_plat_data.bulk_write = msm_sdw_swrm_bulk_write;
+ msm_sdw->sdw_plat_data.clk = msm_sdw_swrm_clock;
+ msm_sdw->sdw_plat_data.handle_irq = msm_sdwm_handle_irq;
+ ret = of_property_read_u32(pdev->dev.of_node, "reg",
+ &msm_sdw->sdw_base_addr);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: could not find %s entry in dt\n",
+ __func__, "reg");
+ goto err_sdw_cdc;
+ }
+
+ msm_sdw->sdw_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,cdc-sdw-gpios", 0);
+ msm_sdw->sdw_base = ioremap(msm_sdw->sdw_base_addr,
+ MSM_SDW_MAX_REGISTER);
+ msm_sdw->read_dev = __msm_sdw_reg_read;
+ msm_sdw->write_dev = __msm_sdw_reg_write;
+
+ msm_sdw->regmap = msm_sdw_regmap_init(msm_sdw->dev,
+ &msm_sdw_regmap_config);
+ msm_sdw->sdw_irq = platform_get_irq_byname(pdev, "swr_master_irq");
+ if (msm_sdw->sdw_irq < 0) {
+ dev_err(msm_sdw->dev, "%s() error getting irq handle: %d\n",
+ __func__, msm_sdw->sdw_irq);
+ ret = -ENODEV;
+ goto err_sdw_cdc;
+ }
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm_sdw,
+ msm_sdw_dai, ARRAY_SIZE(msm_sdw_dai));
+ if (ret) {
+ dev_err(&pdev->dev, "%s: Codec registration failed, ret = %d\n",
+ __func__, ret);
+ goto err_sdw_cdc;
+ }
+ /* initialize the int_mclk1 */
+ msm_sdw->sdw_cdc_core_clk.clk_set_minor_version =
+ AFE_API_VERSION_I2S_CONFIG;
+ msm_sdw->sdw_cdc_core_clk.clk_id =
+ Q6AFE_LPASS_CLK_ID_INT_MCLK_1;
+ msm_sdw->sdw_cdc_core_clk.clk_freq_in_hz =
+ INT_MCLK1_FREQ;
+ msm_sdw->sdw_cdc_core_clk.clk_attri =
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
+ msm_sdw->sdw_cdc_core_clk.clk_root =
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT;
+ msm_sdw->sdw_cdc_core_clk.enable = 0;
+
+ /* initialize the sdw_npl_clk */
+ msm_sdw->sdw_npl_clk.clk_set_minor_version =
+ AFE_API_VERSION_I2S_CONFIG;
+ msm_sdw->sdw_npl_clk.clk_id =
+ AFE_CLOCK_SET_CLOCK_ID_SWR_NPL_CLK;
+ msm_sdw->sdw_npl_clk.clk_freq_in_hz = SDW_NPL_FREQ;
+ msm_sdw->sdw_npl_clk.clk_attri =
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
+ msm_sdw->sdw_npl_clk.clk_root =
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT;
+ msm_sdw->sdw_npl_clk.enable = 0;
+
+ INIT_DELAYED_WORK(&msm_sdw->disable_int_mclk1_work,
+ msm_disable_int_mclk1);
+ mutex_init(&msm_sdw->cdc_int_mclk1_mutex);
+ mutex_init(&msm_sdw->sdw_npl_clk_mutex);
+ mutex_init(&msm_sdw->io_lock);
+ mutex_init(&msm_sdw->sdw_read_lock);
+ mutex_init(&msm_sdw->sdw_write_lock);
+ mutex_init(&msm_sdw->sdw_clk_lock);
+ mutex_init(&msm_sdw->codec_mutex);
+ schedule_work(&msm_sdw->msm_sdw_add_child_devices_work);
+
+ dev_dbg(&pdev->dev, "%s: msm_sdw driver probe done\n", __func__);
+ return ret;
+
+err_sdw_cdc:
+ devm_kfree(&pdev->dev, msm_sdw);
+ return ret;
+}
+
+static int msm_sdw_remove(struct platform_device *pdev)
+{
+ struct msm_sdw_priv *msm_sdw;
+
+ msm_sdw = dev_get_drvdata(&pdev->dev);
+
+ mutex_destroy(&msm_sdw->io_lock);
+ mutex_destroy(&msm_sdw->sdw_read_lock);
+ mutex_destroy(&msm_sdw->sdw_write_lock);
+ mutex_destroy(&msm_sdw->sdw_clk_lock);
+ mutex_destroy(&msm_sdw->codec_mutex);
+ mutex_destroy(&msm_sdw->cdc_int_mclk1_mutex);
+ devm_kfree(&pdev->dev, msm_sdw);
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_sdw_codec_dt_match[] = {
+ { .compatible = "qcom,msm-sdw-codec", },
+ {}
+};
+
+static struct platform_driver msm_sdw_codec_driver = {
+ .probe = msm_sdw_probe,
+ .remove = msm_sdw_remove,
+ .driver = {
+ .name = "msm_sdw_codec",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_sdw_codec_dt_match,
+ },
+};
+module_platform_driver(msm_sdw_codec_driver);
+
+MODULE_DESCRIPTION("MSM Soundwire Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_cdc_utils.c b/sound/soc/codecs/msm_sdw/msm_sdw_cdc_utils.c
new file mode 100644
index 000000000000..59d0cae3893e
--- /dev/null
+++ b/sound/soc/codecs/msm_sdw/msm_sdw_cdc_utils.c
@@ -0,0 +1,211 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include "msm_sdw.h"
+
+#define REG_BYTES 2
+#define VAL_BYTES 1
+/*
+ * Page Register Address that APP Proc uses to
+ * access WCD9335 Codec registers is identified
+ * as 0x00
+ */
+#define PAGE_REG_ADDR 0x00
+
+/*
+ * msm_sdw_page_write:
+ * Retrieve page number from register and
+ * write that page number to the page address.
+ * Called under io_lock acquisition.
+ *
+ * @msm_sdw: pointer to msm_sdw
+ * @reg: Register address from which page number is retrieved
+ *
+ * Returns 0 for success and negative error code for failure.
+ */
+int msm_sdw_page_write(struct msm_sdw_priv *msm_sdw, unsigned short reg)
+{
+ int ret = 0;
+ u8 pg_num, prev_pg_num;
+
+ pg_num = msm_sdw_page_map[reg];
+ if (msm_sdw->prev_pg_valid) {
+ prev_pg_num = msm_sdw->prev_pg;
+ if (prev_pg_num != pg_num) {
+ ret = msm_sdw->write_dev(msm_sdw, PAGE_REG_ADDR, 1,
+ (void *) &pg_num);
+ if (ret < 0) {
+ dev_err(msm_sdw->dev,
+ "page write error, pg_num: 0x%x\n",
+ pg_num);
+ } else {
+ msm_sdw->prev_pg = pg_num;
+ dev_dbg(msm_sdw->dev,
+ "%s: Page 0x%x Write to 0x00\n",
+ __func__, pg_num);
+ }
+ }
+ } else {
+ ret = msm_sdw->write_dev(msm_sdw, PAGE_REG_ADDR, 1,
+ (void *) &pg_num);
+ if (ret < 0) {
+ dev_err(msm_sdw->dev,
+ "page write error, pg_num: 0x%x\n", pg_num);
+ } else {
+ msm_sdw->prev_pg = pg_num;
+ msm_sdw->prev_pg_valid = true;
+ dev_dbg(msm_sdw->dev, "%s: Page 0x%x Write to 0x00\n",
+ __func__, pg_num);
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL(msm_sdw_page_write);
+
+static int regmap_bus_read(void *context, const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct device *dev = context;
+ struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev);
+ unsigned short c_reg;
+ int ret, i;
+
+ if (!msm_sdw) {
+ dev_err(dev, "%s: msm_sdw is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (!reg || !val) {
+ dev_err(dev, "%s: reg or val is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (reg_size != REG_BYTES) {
+ dev_err(dev, "%s: register size %zd bytes, not supported\n",
+ __func__, reg_size);
+ return -EINVAL;
+ }
+ if (!msm_sdw->dev_up) {
+ dev_dbg_ratelimited(dev, "%s: No read allowed. dev_up = %d\n",
+ __func__, msm_sdw->dev_up);
+ return 0;
+ }
+
+ mutex_lock(&msm_sdw->io_lock);
+ c_reg = *(u16 *)reg;
+ ret = msm_sdw_page_write(msm_sdw, c_reg);
+ if (ret)
+ goto err;
+ ret = msm_sdw->read_dev(msm_sdw, c_reg, val_size, val);
+ if (ret < 0)
+ dev_err(dev, "%s: Codec read failed (%d), reg: 0x%x, size:%zd\n",
+ __func__, ret, c_reg, val_size);
+ else {
+ for (i = 0; i < val_size; i++)
+ dev_dbg(dev, "%s: Read 0x%02x from 0x%x\n",
+ __func__, ((u8 *)val)[i], c_reg + i);
+ }
+err:
+ mutex_unlock(&msm_sdw->io_lock);
+
+ return ret;
+}
+
+static int regmap_bus_gather_write(void *context,
+ const void *reg, size_t reg_size,
+ const void *val, size_t val_size)
+{
+ struct device *dev = context;
+ struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev);
+ unsigned short c_reg;
+ int ret, i;
+
+ if (!msm_sdw) {
+ dev_err(dev, "%s: msm_sdw is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (!reg || !val) {
+ dev_err(dev, "%s: reg or val is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (reg_size != REG_BYTES) {
+ dev_err(dev, "%s: register size %zd bytes, not supported\n",
+ __func__, reg_size);
+ return -EINVAL;
+ }
+ if (!msm_sdw->dev_up) {
+ dev_dbg_ratelimited(dev, "%s: No write allowed. dev_up = %d\n",
+ __func__, msm_sdw->dev_up);
+ return 0;
+ }
+
+ mutex_lock(&msm_sdw->io_lock);
+ c_reg = *(u16 *)reg;
+ ret = msm_sdw_page_write(msm_sdw, c_reg);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < val_size; i++)
+ dev_dbg(dev, "Write %02x to 0x%x\n", ((u8 *)val)[i],
+ c_reg + i*4);
+
+ ret = msm_sdw->write_dev(msm_sdw, c_reg, val_size, (void *) val);
+ if (ret < 0)
+ dev_err(dev,
+ "%s: Codec write failed (%d), reg:0x%x, size:%zd\n",
+ __func__, ret, c_reg, val_size);
+
+err:
+ mutex_unlock(&msm_sdw->io_lock);
+ return ret;
+}
+
+static int regmap_bus_write(void *context, const void *data, size_t count)
+{
+ struct device *dev = context;
+ struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev);
+
+ if (!msm_sdw)
+ return -EINVAL;
+
+ WARN_ON(count < REG_BYTES);
+
+ return regmap_bus_gather_write(context, data, REG_BYTES,
+ data + REG_BYTES,
+ count - REG_BYTES);
+
+}
+
+static struct regmap_bus regmap_bus_config = {
+ .write = regmap_bus_write,
+ .gather_write = regmap_bus_gather_write,
+ .read = regmap_bus_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+};
+
+/*
+ * msm_sdw_regmap_init:
+ * Initialize msm_sdw register map
+ *
+ * @dev: pointer to wcd device
+ * @config: pointer to register map config
+ *
+ * Returns pointer to regmap structure for success
+ * or NULL in case of failure.
+ */
+struct regmap *msm_sdw_regmap_init(struct device *dev,
+ const struct regmap_config *config)
+{
+ return devm_regmap_init(dev, &regmap_bus_config, dev, config);
+}
+EXPORT_SYMBOL(msm_sdw_regmap_init);
diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_registers.h b/sound/soc/codecs/msm_sdw/msm_sdw_registers.h
new file mode 100644
index 000000000000..f2302ef21e13
--- /dev/null
+++ b/sound/soc/codecs/msm_sdw/msm_sdw_registers.h
@@ -0,0 +1,126 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef MSM_SDW_REGISTERS_H
+#define MSM_SDW_REGISTERS_H
+
+#define MSM_SDW_PAGE_REGISTER 0x0000
+
+/* Page-A Registers */
+#define MSM_SDW_TX9_SPKR_PROT_PATH_CTL 0x0308
+#define MSM_SDW_TX9_SPKR_PROT_PATH_CFG0 0x030c
+#define MSM_SDW_TX10_SPKR_PROT_PATH_CTL 0x0318
+#define MSM_SDW_TX10_SPKR_PROT_PATH_CFG0 0x031c
+#define MSM_SDW_TX11_SPKR_PROT_PATH_CTL 0x0328
+#define MSM_SDW_TX11_SPKR_PROT_PATH_CFG0 0x032c
+#define MSM_SDW_TX12_SPKR_PROT_PATH_CTL 0x0338
+#define MSM_SDW_TX12_SPKR_PROT_PATH_CFG0 0x033c
+
+/* Page-B Registers */
+#define MSM_SDW_COMPANDER7_CTL0 0x0024
+#define MSM_SDW_COMPANDER7_CTL1 0x0028
+#define MSM_SDW_COMPANDER7_CTL2 0x002c
+#define MSM_SDW_COMPANDER7_CTL3 0x0030
+#define MSM_SDW_COMPANDER7_CTL4 0x0034
+#define MSM_SDW_COMPANDER7_CTL5 0x0038
+#define MSM_SDW_COMPANDER7_CTL6 0x003c
+#define MSM_SDW_COMPANDER7_CTL7 0x0040
+#define MSM_SDW_COMPANDER8_CTL0 0x0044
+#define MSM_SDW_COMPANDER8_CTL1 0x0048
+#define MSM_SDW_COMPANDER8_CTL2 0x004c
+#define MSM_SDW_COMPANDER8_CTL3 0x0050
+#define MSM_SDW_COMPANDER8_CTL4 0x0054
+#define MSM_SDW_COMPANDER8_CTL5 0x0058
+#define MSM_SDW_COMPANDER8_CTL6 0x005c
+#define MSM_SDW_COMPANDER8_CTL7 0x0060
+#define MSM_SDW_RX7_RX_PATH_CTL 0x01a4
+#define MSM_SDW_RX7_RX_PATH_CFG0 0x01a8
+#define MSM_SDW_RX7_RX_PATH_CFG1 0x01ac
+#define MSM_SDW_RX7_RX_PATH_CFG2 0x01b0
+#define MSM_SDW_RX7_RX_VOL_CTL 0x01b4
+#define MSM_SDW_RX7_RX_PATH_MIX_CTL 0x01b8
+#define MSM_SDW_RX7_RX_PATH_MIX_CFG 0x01bc
+#define MSM_SDW_RX7_RX_VOL_MIX_CTL 0x01c0
+#define MSM_SDW_RX7_RX_PATH_SEC0 0x01c4
+#define MSM_SDW_RX7_RX_PATH_SEC1 0x01c8
+#define MSM_SDW_RX7_RX_PATH_SEC2 0x01cc
+#define MSM_SDW_RX7_RX_PATH_SEC3 0x01d0
+#define MSM_SDW_RX7_RX_PATH_SEC5 0x01d8
+#define MSM_SDW_RX7_RX_PATH_SEC6 0x01dc
+#define MSM_SDW_RX7_RX_PATH_SEC7 0x01e0
+#define MSM_SDW_RX7_RX_PATH_MIX_SEC0 0x01e4
+#define MSM_SDW_RX7_RX_PATH_MIX_SEC1 0x01e8
+#define MSM_SDW_RX8_RX_PATH_CTL 0x0384
+#define MSM_SDW_RX8_RX_PATH_CFG0 0x0388
+#define MSM_SDW_RX8_RX_PATH_CFG1 0x038c
+#define MSM_SDW_RX8_RX_PATH_CFG2 0x0390
+#define MSM_SDW_RX8_RX_VOL_CTL 0x0394
+#define MSM_SDW_RX8_RX_PATH_MIX_CTL 0x0398
+#define MSM_SDW_RX8_RX_PATH_MIX_CFG 0x039c
+#define MSM_SDW_RX8_RX_VOL_MIX_CTL 0x03a0
+#define MSM_SDW_RX8_RX_PATH_SEC0 0x03a4
+#define MSM_SDW_RX8_RX_PATH_SEC1 0x03a8
+#define MSM_SDW_RX8_RX_PATH_SEC2 0x03ac
+#define MSM_SDW_RX8_RX_PATH_SEC3 0x03b0
+#define MSM_SDW_RX8_RX_PATH_SEC5 0x03b8
+#define MSM_SDW_RX8_RX_PATH_SEC6 0x03bc
+#define MSM_SDW_RX8_RX_PATH_SEC7 0x03c0
+#define MSM_SDW_RX8_RX_PATH_MIX_SEC0 0x03c4
+#define MSM_SDW_RX8_RX_PATH_MIX_SEC1 0x03c8
+
+/* Page-C Registers */
+#define MSM_SDW_BOOST0_BOOST_PATH_CTL 0x0064
+#define MSM_SDW_BOOST0_BOOST_CTL 0x0068
+#define MSM_SDW_BOOST0_BOOST_CFG1 0x006c
+#define MSM_SDW_BOOST0_BOOST_CFG2 0x0070
+#define MSM_SDW_BOOST1_BOOST_PATH_CTL 0x0084
+#define MSM_SDW_BOOST1_BOOST_CTL 0x0088
+#define MSM_SDW_BOOST1_BOOST_CFG1 0x008c
+#define MSM_SDW_BOOST1_BOOST_CFG2 0x0090
+#define MSM_SDW_AHB_BRIDGE_WR_DATA_0 0x00a4
+#define MSM_SDW_AHB_BRIDGE_WR_DATA_1 0x00a8
+#define MSM_SDW_AHB_BRIDGE_WR_DATA_2 0x00ac
+#define MSM_SDW_AHB_BRIDGE_WR_DATA_3 0x00b0
+#define MSM_SDW_AHB_BRIDGE_WR_ADDR_0 0x00b4
+#define MSM_SDW_AHB_BRIDGE_WR_ADDR_1 0x00b8
+#define MSM_SDW_AHB_BRIDGE_WR_ADDR_2 0x00bc
+#define MSM_SDW_AHB_BRIDGE_WR_ADDR_3 0x00c0
+#define MSM_SDW_AHB_BRIDGE_RD_ADDR_0 0x00c4
+#define MSM_SDW_AHB_BRIDGE_RD_ADDR_1 0x00c8
+#define MSM_SDW_AHB_BRIDGE_RD_ADDR_2 0x00cc
+#define MSM_SDW_AHB_BRIDGE_RD_ADDR_3 0x00d0
+#define MSM_SDW_AHB_BRIDGE_RD_DATA_0 0x00d4
+#define MSM_SDW_AHB_BRIDGE_RD_DATA_1 0x00d8
+#define MSM_SDW_AHB_BRIDGE_RD_DATA_2 0x00dc
+#define MSM_SDW_AHB_BRIDGE_RD_DATA_3 0x00e0
+#define MSM_SDW_AHB_BRIDGE_ACCESS_CFG 0x00e4
+#define MSM_SDW_AHB_BRIDGE_ACCESS_STATUS 0x00e8
+
+/* Page-D Registers */
+#define MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL 0x0104
+#define MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL 0x0108
+#define MSM_SDW_CLK_RST_CTRL_SWR_CONTROL 0x010c
+#define MSM_SDW_TOP_TOP_CFG0 0x0204
+#define MSM_SDW_TOP_TOP_CFG1 0x0208
+#define MSM_SDW_TOP_RX_I2S_CTL 0x020c
+#define MSM_SDW_TOP_TX_I2S_CTL 0x0210
+#define MSM_SDW_TOP_I2S_CLK 0x0214
+#define MSM_SDW_TOP_RX7_PATH_INPUT0_MUX 0x0218
+#define MSM_SDW_TOP_RX7_PATH_INPUT1_MUX 0x021c
+#define MSM_SDW_TOP_RX8_PATH_INPUT0_MUX 0x0220
+#define MSM_SDW_TOP_RX8_PATH_INPUT1_MUX 0x0224
+#define MSM_SDW_TOP_FREQ_MCLK 0x0228
+#define MSM_SDW_TOP_DEBUG_BUS_SEL 0x022c
+#define MSM_SDW_TOP_DEBUG_EN 0x0230
+#define MSM_SDW_TOP_I2S_RESET 0x0234
+#define MSM_SDW_TOP_BLOCKS_RESET 0x0238
+
+#endif
diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_regmap.c b/sound/soc/codecs/msm_sdw/msm_sdw_regmap.c
new file mode 100644
index 000000000000..22663384ec35
--- /dev/null
+++ b/sound/soc/codecs/msm_sdw/msm_sdw_regmap.c
@@ -0,0 +1,161 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include "msm_sdw.h"
+
+static const struct reg_default msm_sdw_defaults[] = {
+ /* Page #10 registers */
+ { MSM_SDW_PAGE_REGISTER, 0x00 },
+ { MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x02 },
+ { MSM_SDW_TX9_SPKR_PROT_PATH_CFG0, 0x00 },
+ { MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x02 },
+ { MSM_SDW_TX10_SPKR_PROT_PATH_CFG0, 0x00 },
+ { MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x02 },
+ { MSM_SDW_TX11_SPKR_PROT_PATH_CFG0, 0x00 },
+ { MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x02 },
+ { MSM_SDW_TX12_SPKR_PROT_PATH_CFG0, 0x00 },
+ /* Page #11 registers */
+ { MSM_SDW_COMPANDER7_CTL0, 0x60 },
+ { MSM_SDW_COMPANDER7_CTL1, 0xdb },
+ { MSM_SDW_COMPANDER7_CTL2, 0xff },
+ { MSM_SDW_COMPANDER7_CTL3, 0x35 },
+ { MSM_SDW_COMPANDER7_CTL4, 0xff },
+ { MSM_SDW_COMPANDER7_CTL5, 0x00 },
+ { MSM_SDW_COMPANDER7_CTL6, 0x01 },
+ { MSM_SDW_COMPANDER8_CTL0, 0x60 },
+ { MSM_SDW_COMPANDER8_CTL1, 0xdb },
+ { MSM_SDW_COMPANDER8_CTL2, 0xff },
+ { MSM_SDW_COMPANDER8_CTL3, 0x35 },
+ { MSM_SDW_COMPANDER8_CTL4, 0xff },
+ { MSM_SDW_COMPANDER8_CTL5, 0x00 },
+ { MSM_SDW_COMPANDER8_CTL6, 0x01 },
+ { MSM_SDW_RX7_RX_PATH_CTL, 0x04 },
+ { MSM_SDW_RX7_RX_PATH_CFG0, 0x00 },
+ { MSM_SDW_RX7_RX_PATH_CFG2, 0x8f },
+ { MSM_SDW_RX7_RX_VOL_CTL, 0x00 },
+ { MSM_SDW_RX7_RX_PATH_MIX_CTL, 0x04 },
+ { MSM_SDW_RX7_RX_VOL_MIX_CTL, 0x00 },
+ { MSM_SDW_RX7_RX_PATH_SEC2, 0x00 },
+ { MSM_SDW_RX7_RX_PATH_SEC3, 0x00 },
+ { MSM_SDW_RX7_RX_PATH_SEC5, 0x00 },
+ { MSM_SDW_RX7_RX_PATH_SEC6, 0x00 },
+ { MSM_SDW_RX7_RX_PATH_SEC7, 0x00 },
+ { MSM_SDW_RX7_RX_PATH_MIX_SEC1, 0x00 },
+ { MSM_SDW_RX8_RX_PATH_CTL, 0x04 },
+ { MSM_SDW_RX8_RX_PATH_CFG0, 0x00 },
+ { MSM_SDW_RX8_RX_PATH_CFG2, 0x8f },
+ { MSM_SDW_RX8_RX_VOL_CTL, 0x00 },
+ { MSM_SDW_RX8_RX_PATH_MIX_CTL, 0x04 },
+ { MSM_SDW_RX8_RX_VOL_MIX_CTL, 0x00 },
+ { MSM_SDW_RX8_RX_PATH_SEC2, 0x00 },
+ { MSM_SDW_RX8_RX_PATH_SEC3, 0x00 },
+ { MSM_SDW_RX8_RX_PATH_SEC5, 0x00 },
+ { MSM_SDW_RX8_RX_PATH_SEC6, 0x00 },
+ { MSM_SDW_RX8_RX_PATH_SEC7, 0x00 },
+ { MSM_SDW_RX8_RX_PATH_MIX_SEC1, 0x00 },
+ /* Page #12 registers */
+ { MSM_SDW_BOOST0_BOOST_PATH_CTL, 0x00 },
+ { MSM_SDW_BOOST0_BOOST_CTL, 0xb2 },
+ { MSM_SDW_BOOST0_BOOST_CFG1, 0x00 },
+ { MSM_SDW_BOOST0_BOOST_CFG2, 0x00 },
+ { MSM_SDW_BOOST1_BOOST_PATH_CTL, 0x00 },
+ { MSM_SDW_BOOST1_BOOST_CTL, 0xb2 },
+ { MSM_SDW_BOOST1_BOOST_CFG1, 0x00 },
+ { MSM_SDW_BOOST1_BOOST_CFG2, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_WR_DATA_0, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_WR_DATA_1, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_WR_DATA_2, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_WR_DATA_3, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_WR_ADDR_0, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_WR_ADDR_1, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_WR_ADDR_2, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_WR_ADDR_3, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_RD_ADDR_0, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_RD_ADDR_1, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_RD_ADDR_2, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_RD_ADDR_3, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_RD_DATA_0, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_RD_DATA_1, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_RD_DATA_2, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_RD_DATA_3, 0x00 },
+ { MSM_SDW_AHB_BRIDGE_ACCESS_CFG, 0x0f },
+ { MSM_SDW_AHB_BRIDGE_ACCESS_STATUS, 0x03 },
+ /* Page #13 registers */
+ { MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL, 0x00 },
+ { MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 },
+ { MSM_SDW_CLK_RST_CTRL_SWR_CONTROL, 0x00 },
+ { MSM_SDW_TOP_TOP_CFG0, 0x00 },
+ { MSM_SDW_TOP_TOP_CFG1, 0x00 },
+ { MSM_SDW_TOP_RX_I2S_CTL, 0x0C },
+ { MSM_SDW_TOP_TX_I2S_CTL, 0x00 },
+ { MSM_SDW_TOP_I2S_CLK, 0x00 },
+ { MSM_SDW_TOP_RX7_PATH_INPUT0_MUX, 0x00 },
+ { MSM_SDW_TOP_RX7_PATH_INPUT1_MUX, 0x00 },
+ { MSM_SDW_TOP_RX8_PATH_INPUT0_MUX, 0x00 },
+ { MSM_SDW_TOP_RX8_PATH_INPUT1_MUX, 0x00 },
+ { MSM_SDW_TOP_FREQ_MCLK, 0x00 },
+ { MSM_SDW_TOP_DEBUG_BUS_SEL, 0x00 },
+ { MSM_SDW_TOP_DEBUG_EN, 0x00 },
+ { MSM_SDW_TOP_I2S_RESET, 0x00 },
+ { MSM_SDW_TOP_BLOCKS_RESET, 0x00 },
+};
+
+static bool msm_sdw_is_readable_register(struct device *dev, unsigned int reg)
+{
+ return msm_sdw_reg_readable[reg];
+}
+
+static bool msm_sdw_is_writeable_register(struct device *dev, unsigned int reg)
+{
+ return msm_sdw_reg_writeable[reg];
+}
+
+static bool msm_sdw_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MSM_SDW_AHB_BRIDGE_WR_DATA_0:
+ case MSM_SDW_AHB_BRIDGE_WR_DATA_1:
+ case MSM_SDW_AHB_BRIDGE_WR_DATA_2:
+ case MSM_SDW_AHB_BRIDGE_WR_DATA_3:
+ case MSM_SDW_AHB_BRIDGE_WR_ADDR_0:
+ case MSM_SDW_AHB_BRIDGE_WR_ADDR_1:
+ case MSM_SDW_AHB_BRIDGE_WR_ADDR_2:
+ case MSM_SDW_AHB_BRIDGE_WR_ADDR_3:
+ case MSM_SDW_AHB_BRIDGE_RD_DATA_0:
+ case MSM_SDW_AHB_BRIDGE_RD_DATA_1:
+ case MSM_SDW_AHB_BRIDGE_RD_DATA_2:
+ case MSM_SDW_AHB_BRIDGE_RD_DATA_3:
+ case MSM_SDW_AHB_BRIDGE_RD_ADDR_0:
+ case MSM_SDW_AHB_BRIDGE_RD_ADDR_1:
+ case MSM_SDW_AHB_BRIDGE_RD_ADDR_2:
+ case MSM_SDW_AHB_BRIDGE_RD_ADDR_3:
+ case MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL:
+ case MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const struct regmap_config msm_sdw_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .reg_stride = 4,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = msm_sdw_defaults,
+ .num_reg_defaults = ARRAY_SIZE(msm_sdw_defaults),
+ .max_register = MSM_SDW_MAX_REGISTER,
+ .writeable_reg = msm_sdw_is_writeable_register,
+ .volatile_reg = msm_sdw_is_volatile_register,
+ .readable_reg = msm_sdw_is_readable_register,
+};
diff --git a/sound/soc/codecs/msm_stub.c b/sound/soc/codecs/msm_stub.c
new file mode 100644
index 000000000000..ffb3f84c0927
--- /dev/null
+++ b/sound/soc/codecs/msm_stub.c
@@ -0,0 +1,94 @@
+/* Copyright (c) 2011-2014, 2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+/* A dummy driver useful only to advertise hardware parameters */
+static struct snd_soc_dai_driver msm_stub_dais[] = {
+ {
+ .name = "msm-stub-rx",
+ .playback = { /* Support maximum range */
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ },
+ },
+ {
+ .name = "msm-stub-tx",
+ .capture = { /* Support maximum range */
+ .stream_name = "Record",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ },
+ },
+};
+
+static struct snd_soc_codec_driver soc_msm_stub = {};
+
+static int msm_stub_dev_probe(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_msm_stub, msm_stub_dais, ARRAY_SIZE(msm_stub_dais));
+}
+
+static int msm_stub_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+static const struct of_device_id msm_stub_codec_dt_match[] = {
+ { .compatible = "qcom,msm-stub-codec", },
+ {}
+};
+
+static struct platform_driver msm_stub_driver = {
+ .driver = {
+ .name = "msm-stub-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_stub_codec_dt_match,
+ },
+ .probe = msm_stub_dev_probe,
+ .remove = msm_stub_dev_remove,
+};
+
+static int __init msm_stub_init(void)
+{
+ return platform_driver_register(&msm_stub_driver);
+}
+module_init(msm_stub_init);
+
+static void __exit msm_stub_exit(void)
+{
+ platform_driver_unregister(&msm_stub_driver);
+}
+module_exit(msm_stub_exit);
+
+MODULE_DESCRIPTION("Generic MSM CODEC driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sdm660_cdc/Kconfig b/sound/soc/codecs/sdm660_cdc/Kconfig
new file mode 100644
index 000000000000..d370da3d2ad5
--- /dev/null
+++ b/sound/soc/codecs/sdm660_cdc/Kconfig
@@ -0,0 +1,3 @@
+
+config SND_SOC_SDM660_CDC
+ tristate "MSM Internal PMIC based codec"
diff --git a/sound/soc/codecs/sdm660_cdc/Makefile b/sound/soc/codecs/sdm660_cdc/Makefile
new file mode 100644
index 000000000000..d846fae26054
--- /dev/null
+++ b/sound/soc/codecs/sdm660_cdc/Makefile
@@ -0,0 +1,2 @@
+snd-soc-sdm660-cdc-objs := msm-analog-cdc.o msm-digital-cdc.o sdm660-regmap.o
+obj-$(CONFIG_SND_SOC_SDM660_CDC) += snd-soc-sdm660-cdc.o sdm660-cdc-irq.o
diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c
new file mode 100644
index 000000000000..eb093cf71b41
--- /dev/null
+++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c
@@ -0,0 +1,4687 @@
+/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/workqueue.h>
+#include <linux/regmap.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+#include <sound/q6afe-v2.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/q6core.h>
+#include "msm-analog-cdc.h"
+#include "sdm660-cdc-irq.h"
+#include "sdm660-cdc-registers.h"
+#include "msm-cdc-common.h"
+#include "../../msm/sdm660-common.h"
+#include "../wcd-mbhc-v2.h"
+
+#define DRV_NAME "pmic_analog_codec"
+#define SDM660_CDC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+#define SDM660_CDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE)
+#define MSM_DIG_CDC_STRING_LEN 80
+#define MSM_ANLG_CDC_VERSION_ENTRY_SIZE 32
+
+#define CODEC_DT_MAX_PROP_SIZE 40
+#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64
+#define BUS_DOWN 1
+
+/*
+ * 200 Milliseconds sufficient for DSP bring up in the lpass
+ * after Sub System Restart
+ */
+#define ADSP_STATE_READY_TIMEOUT_MS 200
+
+#define EAR_PMD 0
+#define EAR_PMU 1
+#define SPK_PMD 2
+#define SPK_PMU 3
+
+#define MICBIAS_DEFAULT_VAL 1800000
+#define MICBIAS_MIN_VAL 1600000
+#define MICBIAS_STEP_SIZE 50000
+
+#define DEFAULT_BOOST_VOLTAGE 5000
+#define MIN_BOOST_VOLTAGE 4000
+#define MAX_BOOST_VOLTAGE 5550
+#define BOOST_VOLTAGE_STEP 50
+
+#define SDM660_CDC_MBHC_BTN_COARSE_ADJ 100 /* in mV */
+#define SDM660_CDC_MBHC_BTN_FINE_ADJ 12 /* in mV */
+
+#define VOLTAGE_CONVERTER(value, min_value, step_size)\
+ ((value - min_value)/step_size)
+
+enum {
+ BOOST_SWITCH = 0,
+ BOOST_ALWAYS,
+ BYPASS_ALWAYS,
+ BOOST_ON_FOREVER,
+};
+
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+static struct snd_soc_dai_driver msm_anlg_cdc_i2s_dai[];
+/* By default enable the internal speaker boost */
+static bool spkr_boost_en = true;
+
+static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = {
+ "cdc-vdd-mic-bias",
+};
+
+static struct wcd_mbhc_register
+ wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN",
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x80, 7, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN",
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x40, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE",
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x20, 5, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL",
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x18, 3, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE",
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x01, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL",
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0xC0, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL",
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x20, 5, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE",
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x10, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE",
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08, 3, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND",
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x01, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC",
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x06, 1, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN",
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x80, 7, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC",
+ MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0xF0, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC",
+ MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x0C, 2, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF",
+ MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x03, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT",
+ MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x01,
+ 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT",
+ MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x02,
+ 1, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT",
+ MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x08,
+ 3, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT",
+ MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x04,
+ 2, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN",
+ MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0x10, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT",
+ MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0xFF, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL",
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x70, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT",
+ MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0xFF,
+ 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL",
+ MSM89XX_PMIC_ANALOG_MICB_2_EN, 0xC0, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME",
+ MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFC, 2, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN",
+ MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x10, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN",
+ MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x20, 5, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN",
+ MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x30, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE",
+ MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT,
+ 0x10, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL",
+ MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20, 5, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", 0, 0, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", 0, 0, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", 0, 0, 0, 0),
+};
+
+/* Multiply gain_adj and offset by 1000 and 100 to avoid float arithmetic */
+static const struct wcd_imped_i_ref imped_i_ref[] = {
+ {I_h4_UA, 8, 800, 9000, 10000},
+ {I_pt5_UA, 10, 100, 990, 4600},
+ {I_14_UA, 17, 14, 1050, 700},
+ {I_l4_UA, 10, 4, 1165, 110},
+ {I_1_UA, 0, 1, 1200, 65},
+};
+
+static const struct wcd_mbhc_intr intr_ids = {
+ .mbhc_sw_intr = MSM89XX_IRQ_MBHC_HS_DET,
+ .mbhc_btn_press_intr = MSM89XX_IRQ_MBHC_PRESS,
+ .mbhc_btn_release_intr = MSM89XX_IRQ_MBHC_RELEASE,
+ .mbhc_hs_ins_intr = MSM89XX_IRQ_MBHC_INSREM_DET1,
+ .mbhc_hs_rem_intr = MSM89XX_IRQ_MBHC_INSREM_DET,
+ .hph_left_ocp = MSM89XX_IRQ_HPHL_OCP,
+ .hph_right_ocp = MSM89XX_IRQ_HPHR_OCP,
+};
+
+static int msm_anlg_cdc_dt_parse_vreg_info(struct device *dev,
+ struct sdm660_cdc_regulator *vreg,
+ const char *vreg_name,
+ bool ondemand);
+static struct sdm660_cdc_pdata *msm_anlg_cdc_populate_dt_pdata(
+ struct device *dev);
+static int msm_anlg_cdc_enable_ext_mb_source(struct wcd_mbhc *wcd_mbhc,
+ bool turn_on);
+static void msm_anlg_cdc_trim_btn_reg(struct snd_soc_codec *codec);
+static void msm_anlg_cdc_set_micb_v(struct snd_soc_codec *codec);
+static void msm_anlg_cdc_set_boost_v(struct snd_soc_codec *codec);
+static void msm_anlg_cdc_set_auto_zeroing(struct snd_soc_codec *codec,
+ bool enable);
+static void msm_anlg_cdc_configure_cap(struct snd_soc_codec *codec,
+ bool micbias1, bool micbias2);
+static bool msm_anlg_cdc_use_mb(struct snd_soc_codec *codec);
+
+static int get_codec_version(struct sdm660_cdc_priv *sdm660_cdc)
+{
+ if (sdm660_cdc->codec_version == DRAX_CDC)
+ return DRAX_CDC;
+ else if (sdm660_cdc->codec_version == DIANGU)
+ return DIANGU;
+ else if (sdm660_cdc->codec_version == CAJON_2_0)
+ return CAJON_2_0;
+ else if (sdm660_cdc->codec_version == CAJON)
+ return CAJON;
+ else if (sdm660_cdc->codec_version == CONGA)
+ return CONGA;
+ else if (sdm660_cdc->pmic_rev == TOMBAK_2_0)
+ return TOMBAK_2_0;
+ else if (sdm660_cdc->pmic_rev == TOMBAK_1_0)
+ return TOMBAK_1_0;
+
+ pr_err("%s: unsupported codec version\n", __func__);
+ return UNSUPPORTED;
+}
+
+static void wcd_mbhc_meas_imped(struct snd_soc_codec *codec,
+ s16 *impedance_l, s16 *impedance_r)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if ((sdm660_cdc->imped_det_pin == WCD_MBHC_DET_BOTH) ||
+ (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHL)) {
+ /* Enable ZDET_L_MEAS_EN */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x08, 0x08);
+ /* Wait for 2ms for measurement to complete */
+ usleep_range(2000, 2100);
+ /* Read Left impedance value from Result1 */
+ *impedance_l = snd_soc_read(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT);
+ /* Enable ZDET_R_MEAS_EN */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x08, 0x00);
+ }
+ if ((sdm660_cdc->imped_det_pin == WCD_MBHC_DET_BOTH) ||
+ (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR)) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x04, 0x04);
+ /* Wait for 2ms for measurement to complete */
+ usleep_range(2000, 2100);
+ /* Read Right impedance value from Result1 */
+ *impedance_r = snd_soc_read(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x04, 0x00);
+ }
+}
+
+static void msm_anlg_cdc_set_ref_current(struct snd_soc_codec *codec,
+ enum wcd_curr_ref curr_ref)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: curr_ref: %d\n", __func__, curr_ref);
+
+ if (get_codec_version(sdm660_cdc) < CAJON)
+ dev_dbg(codec->dev, "%s: Setting ref current not required\n",
+ __func__);
+
+ sdm660_cdc->imped_i_ref = imped_i_ref[curr_ref];
+
+ switch (curr_ref) {
+ case I_h4_UA:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_2_EN,
+ 0x07, 0x01);
+ break;
+ case I_pt5_UA:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_2_EN,
+ 0x07, 0x04);
+ break;
+ case I_14_UA:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_2_EN,
+ 0x07, 0x03);
+ break;
+ case I_l4_UA:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_2_EN,
+ 0x07, 0x01);
+ break;
+ case I_1_UA:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_2_EN,
+ 0x07, 0x00);
+ break;
+ default:
+ pr_debug("%s: No ref current set\n", __func__);
+ break;
+ }
+}
+
+static bool msm_anlg_cdc_adj_ref_current(struct snd_soc_codec *codec,
+ s16 *impedance_l, s16 *impedance_r)
+{
+ int i = 2;
+ s16 compare_imp = 0;
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR)
+ compare_imp = *impedance_r;
+ else
+ compare_imp = *impedance_l;
+
+ if (get_codec_version(sdm660_cdc) < CAJON) {
+ dev_dbg(codec->dev,
+ "%s: Reference current adjustment not required\n",
+ __func__);
+ return false;
+ }
+
+ while (compare_imp < imped_i_ref[i].min_val) {
+ msm_anlg_cdc_set_ref_current(codec, imped_i_ref[++i].curr_ref);
+ wcd_mbhc_meas_imped(codec, impedance_l, impedance_r);
+ compare_imp = (sdm660_cdc->imped_det_pin ==
+ WCD_MBHC_DET_HPHR) ? *impedance_r : *impedance_l;
+ if (i >= I_1_UA)
+ break;
+ }
+ return true;
+}
+
+void msm_anlg_cdc_spk_ext_pa_cb(
+ int (*codec_spk_ext_pa)(struct snd_soc_codec *codec,
+ int enable), struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc;
+
+ if (!codec) {
+ pr_err("%s: NULL codec pointer!\n", __func__);
+ return;
+ }
+
+ sdm660_cdc = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: Enter\n", __func__);
+ sdm660_cdc->codec_spk_ext_pa_cb = codec_spk_ext_pa;
+}
+
+static void msm_anlg_cdc_compute_impedance(struct snd_soc_codec *codec, s16 l,
+ s16 r, uint32_t *zl, uint32_t *zr,
+ bool high)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+ uint32_t rl = 0, rr = 0;
+ struct wcd_imped_i_ref R = sdm660_cdc->imped_i_ref;
+ int codec_ver = get_codec_version(sdm660_cdc);
+
+ switch (codec_ver) {
+ case TOMBAK_1_0:
+ case TOMBAK_2_0:
+ case CONGA:
+ if (high) {
+ dev_dbg(codec->dev,
+ "%s: This plug has high range impedance\n",
+ __func__);
+ rl = (uint32_t)(((100 * (l * 400 - 200))/96) - 230);
+ rr = (uint32_t)(((100 * (r * 400 - 200))/96) - 230);
+ } else {
+ dev_dbg(codec->dev,
+ "%s: This plug has low range impedance\n",
+ __func__);
+ rl = (uint32_t)(((1000 * (l * 2 - 1))/1165) - (13/10));
+ rr = (uint32_t)(((1000 * (r * 2 - 1))/1165) - (13/10));
+ }
+ break;
+ case CAJON:
+ case CAJON_2_0:
+ case DIANGU:
+ case DRAX_CDC:
+ if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHL) {
+ rr = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * r - 5)) -
+ (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN);
+ rl = (uint32_t)(((10000 * (R.multiplier * (10 * l - 5)))
+ - R.offset * R.gain_adj)/(R.gain_adj * 100));
+ } else if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR) {
+ rr = (uint32_t)(((10000 * (R.multiplier * (10 * r - 5)))
+ - R.offset * R.gain_adj)/(R.gain_adj * 100));
+ rl = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * l - 5))-
+ (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN);
+ } else if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_NONE) {
+ rr = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * r - 5)) -
+ (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN);
+ rl = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * l - 5))-
+ (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN);
+ } else {
+ rr = (uint32_t)(((10000 * (R.multiplier * (10 * r - 5)))
+ - R.offset * R.gain_adj)/(R.gain_adj * 100));
+ rl = (uint32_t)(((10000 * (R.multiplier * (10 * l - 5)))
+ - R.offset * R.gain_adj)/(R.gain_adj * 100));
+ }
+ break;
+ default:
+ dev_dbg(codec->dev, "%s: No codec mentioned\n", __func__);
+ break;
+ }
+ *zl = rl;
+ *zr = rr;
+}
+
+static struct firmware_cal *msm_anlg_cdc_get_hwdep_fw_cal(
+ struct wcd_mbhc *wcd_mbhc,
+ enum wcd_cal_type type)
+{
+ struct sdm660_cdc_priv *sdm660_cdc;
+ struct firmware_cal *hwdep_cal;
+ struct snd_soc_codec *codec = wcd_mbhc->codec;
+
+ if (!codec) {
+ pr_err("%s: NULL codec pointer\n", __func__);
+ return NULL;
+ }
+ sdm660_cdc = snd_soc_codec_get_drvdata(codec);
+ hwdep_cal = wcdcal_get_fw_cal(sdm660_cdc->fw_data, type);
+ if (!hwdep_cal) {
+ dev_err(codec->dev, "%s: cal not sent by %d\n",
+ __func__, type);
+ return NULL;
+ }
+ return hwdep_cal;
+}
+
+static void wcd9xxx_spmi_irq_control(struct snd_soc_codec *codec,
+ int irq, bool enable)
+{
+ if (enable)
+ wcd9xxx_spmi_enable_irq(irq);
+ else
+ wcd9xxx_spmi_disable_irq(irq);
+}
+
+static void msm_anlg_cdc_mbhc_clk_setup(struct snd_soc_codec *codec,
+ bool enable)
+{
+ if (enable)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x08, 0x08);
+ else
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x08, 0x00);
+}
+
+static int msm_anlg_cdc_mbhc_map_btn_code_to_num(struct snd_soc_codec *codec)
+{
+ int btn_code;
+ int btn;
+
+ btn_code = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT);
+
+ switch (btn_code) {
+ case 0:
+ btn = 0;
+ break;
+ case 1:
+ btn = 1;
+ break;
+ case 3:
+ btn = 2;
+ break;
+ case 7:
+ btn = 3;
+ break;
+ case 15:
+ btn = 4;
+ break;
+ default:
+ btn = -EINVAL;
+ break;
+ };
+
+ return btn;
+}
+
+static bool msm_anlg_cdc_spmi_lock_sleep(struct wcd_mbhc *mbhc, bool lock)
+{
+ if (lock)
+ return wcd9xxx_spmi_lock_sleep();
+ wcd9xxx_spmi_unlock_sleep();
+ return 0;
+}
+
+static bool msm_anlg_cdc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num)
+{
+ if (micb_num == MIC_BIAS_1)
+ return (snd_soc_read(mbhc->codec,
+ MSM89XX_PMIC_ANALOG_MICB_1_EN) &
+ 0x80);
+ if (micb_num == MIC_BIAS_2)
+ return (snd_soc_read(mbhc->codec,
+ MSM89XX_PMIC_ANALOG_MICB_2_EN) &
+ 0x80);
+ return false;
+}
+
+static void msm_anlg_cdc_enable_master_bias(struct snd_soc_codec *codec,
+ bool enable)
+{
+ if (enable)
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL,
+ 0x30, 0x30);
+ else
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL,
+ 0x30, 0x00);
+}
+
+static void msm_anlg_cdc_mbhc_common_micb_ctrl(struct snd_soc_codec *codec,
+ int event, bool enable)
+{
+ u16 reg;
+ u8 mask;
+ u8 val;
+
+ switch (event) {
+ case MBHC_COMMON_MICB_PRECHARGE:
+ reg = MSM89XX_PMIC_ANALOG_MICB_1_CTL;
+ mask = 0x60;
+ val = (enable ? 0x60 : 0x00);
+ break;
+ case MBHC_COMMON_MICB_SET_VAL:
+ reg = MSM89XX_PMIC_ANALOG_MICB_1_VAL;
+ mask = 0xFF;
+ val = (enable ? 0xC0 : 0x00);
+ break;
+ case MBHC_COMMON_MICB_TAIL_CURR:
+ reg = MSM89XX_PMIC_ANALOG_MICB_1_EN;
+ mask = 0x04;
+ val = (enable ? 0x04 : 0x00);
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: Invalid event received\n", __func__);
+ return;
+ };
+ snd_soc_update_bits(codec, reg, mask, val);
+}
+
+static void msm_anlg_cdc_mbhc_internal_micbias_ctrl(struct snd_soc_codec *codec,
+ int micbias_num,
+ bool enable)
+{
+ if (micbias_num == 1) {
+ if (enable)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS,
+ 0x10, 0x10);
+ else
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS,
+ 0x10, 0x00);
+ }
+}
+
+static bool msm_anlg_cdc_mbhc_hph_pa_on_status(struct snd_soc_codec *codec)
+{
+ return (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN) &
+ 0x30) ? true : false;
+}
+
+static void msm_anlg_cdc_mbhc_program_btn_thr(struct snd_soc_codec *codec,
+ s16 *btn_low, s16 *btn_high,
+ int num_btn, bool is_micbias)
+{
+ int i;
+ u32 course, fine, reg_val;
+ u16 reg_addr = MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL;
+ s16 *btn_voltage;
+
+ btn_voltage = ((is_micbias) ? btn_high : btn_low);
+
+ for (i = 0; i < num_btn; i++) {
+ course = (btn_voltage[i] / SDM660_CDC_MBHC_BTN_COARSE_ADJ);
+ fine = ((btn_voltage[i] % SDM660_CDC_MBHC_BTN_COARSE_ADJ) /
+ SDM660_CDC_MBHC_BTN_FINE_ADJ);
+
+ reg_val = (course << 5) | (fine << 2);
+ snd_soc_update_bits(codec, reg_addr, 0xFC, reg_val);
+ dev_dbg(codec->dev,
+ "%s: course: %d fine: %d reg_addr: %x reg_val: %x\n",
+ __func__, course, fine, reg_addr, reg_val);
+ reg_addr++;
+ }
+}
+
+static void msm_anlg_cdc_mbhc_calc_impedance(struct wcd_mbhc *mbhc,
+ uint32_t *zl, uint32_t *zr)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+ s16 impedance_l, impedance_r;
+ s16 impedance_l_fixed;
+ s16 reg0, reg1, reg2, reg3, reg4;
+ bool high = false;
+ bool min_range_used = false;
+
+ WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+ reg0 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER);
+ reg1 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL);
+ reg2 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2);
+ reg3 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MICB_2_EN);
+ reg4 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL);
+
+ sdm660_cdc->imped_det_pin = WCD_MBHC_DET_BOTH;
+ mbhc->hph_type = WCD_MBHC_HPH_NONE;
+
+ /* disable FSM and micbias and enable pullup*/
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x80, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_2_EN,
+ 0xA5, 0x25);
+ /*
+ * Enable legacy electrical detection current sources
+ * and disable fast ramp and enable manual switching
+ * of extra capacitance
+ */
+ dev_dbg(codec->dev, "%s: Setup for impedance det\n", __func__);
+
+ msm_anlg_cdc_set_ref_current(codec, I_h4_UA);
+
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2,
+ 0x06, 0x02);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL,
+ 0x02, 0x00);
+
+ dev_dbg(codec->dev, "%s: Start performing impedance detection\n",
+ __func__);
+
+ wcd_mbhc_meas_imped(codec, &impedance_l, &impedance_r);
+
+ if (impedance_l > 2 || impedance_r > 2) {
+ high = true;
+ if (!mbhc->mbhc_cfg->mono_stero_detection) {
+ /* Set ZDET_CHG to 0 to discharge ramp */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x02, 0x00);
+ /* wait 40ms for the discharge ramp to complete */
+ usleep_range(40000, 40100);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+ 0x03, 0x00);
+ sdm660_cdc->imped_det_pin = (impedance_l > 2 &&
+ impedance_r > 2) ?
+ WCD_MBHC_DET_NONE :
+ ((impedance_l > 2) ?
+ WCD_MBHC_DET_HPHR :
+ WCD_MBHC_DET_HPHL);
+ if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_NONE)
+ goto exit;
+ } else {
+ if (get_codec_version(sdm660_cdc) >= CAJON) {
+ if (impedance_l == 63 && impedance_r == 63) {
+ dev_dbg(codec->dev,
+ "%s: HPHL and HPHR are floating\n",
+ __func__);
+ sdm660_cdc->imped_det_pin =
+ WCD_MBHC_DET_NONE;
+ mbhc->hph_type = WCD_MBHC_HPH_NONE;
+ } else if (impedance_l == 63
+ && impedance_r < 63) {
+ dev_dbg(codec->dev,
+ "%s: Mono HS with HPHL floating\n",
+ __func__);
+ sdm660_cdc->imped_det_pin =
+ WCD_MBHC_DET_HPHR;
+ mbhc->hph_type = WCD_MBHC_HPH_MONO;
+ } else if (impedance_r == 63 &&
+ impedance_l < 63) {
+ dev_dbg(codec->dev,
+ "%s: Mono HS with HPHR floating\n",
+ __func__);
+ sdm660_cdc->imped_det_pin =
+ WCD_MBHC_DET_HPHL;
+ mbhc->hph_type = WCD_MBHC_HPH_MONO;
+ } else if (impedance_l > 3 && impedance_r > 3 &&
+ (impedance_l == impedance_r)) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2,
+ 0x06, 0x06);
+ wcd_mbhc_meas_imped(codec, &impedance_l,
+ &impedance_r);
+ if (impedance_r == impedance_l)
+ dev_dbg(codec->dev,
+ "%s: Mono Headset\n",
+ __func__);
+ sdm660_cdc->imped_det_pin =
+ WCD_MBHC_DET_NONE;
+ mbhc->hph_type =
+ WCD_MBHC_HPH_MONO;
+ } else {
+ dev_dbg(codec->dev,
+ "%s: STEREO headset is found\n",
+ __func__);
+ sdm660_cdc->imped_det_pin =
+ WCD_MBHC_DET_BOTH;
+ mbhc->hph_type = WCD_MBHC_HPH_STEREO;
+ }
+ }
+ }
+ }
+
+ msm_anlg_cdc_set_ref_current(codec, I_pt5_UA);
+ msm_anlg_cdc_set_ref_current(codec, I_14_UA);
+
+ /* Enable RAMP_L , RAMP_R & ZDET_CHG*/
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+ 0x03, 0x03);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x02, 0x02);
+ /* wait for 50msec for the HW to apply ramp on HPHL and HPHR */
+ usleep_range(50000, 50100);
+ /* Enable ZDET_DISCHG_CAP_CTL to add extra capacitance */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x01, 0x01);
+ /* wait for 5msec for the voltage to get stable */
+ usleep_range(5000, 5100);
+
+ wcd_mbhc_meas_imped(codec, &impedance_l, &impedance_r);
+
+ min_range_used = msm_anlg_cdc_adj_ref_current(codec,
+ &impedance_l, &impedance_r);
+ if (!mbhc->mbhc_cfg->mono_stero_detection) {
+ /* Set ZDET_CHG to 0 to discharge ramp */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x02, 0x00);
+ /* wait for 40msec for the capacitor to discharge */
+ usleep_range(40000, 40100);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+ 0x03, 0x00);
+ goto exit;
+ }
+
+ /* we are setting ref current to the minimun range or the measured
+ * value larger than the minimum value, so min_range_used is true.
+ * If the headset is mono headset with either HPHL or HPHR floating
+ * then we have already done the mono stereo detection and do not
+ * need to continue further.
+ */
+
+ if (!min_range_used ||
+ sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHL ||
+ sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR)
+ goto exit;
+
+
+ /* Disable Set ZDET_CONN_RAMP_L and enable ZDET_CONN_FIXED_L */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL,
+ 0x02, 0x02);
+ /* Set ZDET_CHG to 0 */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x02, 0x00);
+ /* wait for 40msec for the capacitor to discharge */
+ usleep_range(40000, 40100);
+
+ /* Set ZDET_CONN_RAMP_R to 0 */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+ 0x01, 0x00);
+ /* Enable ZDET_L_MEAS_EN */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x08, 0x08);
+ /* wait for 2msec for the HW to compute left inpedance value */
+ usleep_range(2000, 2100);
+ /* Read Left impedance value from Result1 */
+ impedance_l_fixed = snd_soc_read(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT);
+ /* Disable ZDET_L_MEAS_EN */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x08, 0x00);
+ /*
+ * Assume impedance_l is L1, impedance_l_fixed is L2.
+ * If the following condition is met, we can take this
+ * headset as mono one with impedance of L2.
+ * Otherwise, take it as stereo with impedance of L1.
+ * Condition:
+ * abs[(L2-0.5L1)/(L2+0.5L1)] < abs [(L2-L1)/(L2+L1)]
+ */
+ if ((abs(impedance_l_fixed - impedance_l/2) *
+ (impedance_l_fixed + impedance_l)) >=
+ (abs(impedance_l_fixed - impedance_l) *
+ (impedance_l_fixed + impedance_l/2))) {
+ dev_dbg(codec->dev,
+ "%s: STEREO plug type detected\n",
+ __func__);
+ mbhc->hph_type = WCD_MBHC_HPH_STEREO;
+ } else {
+ dev_dbg(codec->dev,
+ "%s: MONO plug type detected\n",
+ __func__);
+ mbhc->hph_type = WCD_MBHC_HPH_MONO;
+ impedance_l = impedance_l_fixed;
+ }
+ /* Enable ZDET_CHG */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x02, 0x02);
+ /* wait for 10msec for the capacitor to charge */
+ usleep_range(10000, 10100);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL,
+ 0x02, 0x00);
+ /* Set ZDET_CHG to 0 to discharge HPHL */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+ 0x02, 0x00);
+ /* wait for 40msec for the capacitor to discharge */
+ usleep_range(40000, 40100);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+ 0x02, 0x00);
+
+exit:
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, reg4);
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MICB_2_EN, reg3);
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, reg1);
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, reg0);
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, reg2);
+ msm_anlg_cdc_compute_impedance(codec, impedance_l, impedance_r,
+ zl, zr, high);
+
+ dev_dbg(codec->dev, "%s: RL %d ohm, RR %d ohm\n", __func__, *zl, *zr);
+ dev_dbg(codec->dev, "%s: Impedance detection completed\n", __func__);
+}
+
+static int msm_anlg_cdc_dig_register_notifier(void *handle,
+ struct notifier_block *nblock,
+ bool enable)
+{
+ struct sdm660_cdc_priv *handle_cdc = handle;
+
+ if (enable)
+ return blocking_notifier_chain_register(&handle_cdc->notifier,
+ nblock);
+
+ return blocking_notifier_chain_unregister(&handle_cdc->notifier,
+ nblock);
+}
+
+static int msm_anlg_cdc_mbhc_register_notifier(struct wcd_mbhc *wcd_mbhc,
+ struct notifier_block *nblock,
+ bool enable)
+{
+ struct snd_soc_codec *codec = wcd_mbhc->codec;
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (enable)
+ return blocking_notifier_chain_register(
+ &sdm660_cdc->notifier_mbhc,
+ nblock);
+
+ return blocking_notifier_chain_unregister(&sdm660_cdc->notifier_mbhc,
+ nblock);
+}
+
+static int msm_anlg_cdc_request_irq(struct snd_soc_codec *codec,
+ int irq, irq_handler_t handler,
+ const char *name, void *data)
+{
+ return wcd9xxx_spmi_request_irq(irq, handler, name, data);
+}
+
+static int msm_anlg_cdc_free_irq(struct snd_soc_codec *codec,
+ int irq, void *data)
+{
+ return wcd9xxx_spmi_free_irq(irq, data);
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .enable_mb_source = msm_anlg_cdc_enable_ext_mb_source,
+ .trim_btn_reg = msm_anlg_cdc_trim_btn_reg,
+ .compute_impedance = msm_anlg_cdc_mbhc_calc_impedance,
+ .set_micbias_value = msm_anlg_cdc_set_micb_v,
+ .set_auto_zeroing = msm_anlg_cdc_set_auto_zeroing,
+ .get_hwdep_fw_cal = msm_anlg_cdc_get_hwdep_fw_cal,
+ .set_cap_mode = msm_anlg_cdc_configure_cap,
+ .register_notifier = msm_anlg_cdc_mbhc_register_notifier,
+ .request_irq = msm_anlg_cdc_request_irq,
+ .irq_control = wcd9xxx_spmi_irq_control,
+ .free_irq = msm_anlg_cdc_free_irq,
+ .clk_setup = msm_anlg_cdc_mbhc_clk_setup,
+ .map_btn_code_to_num = msm_anlg_cdc_mbhc_map_btn_code_to_num,
+ .lock_sleep = msm_anlg_cdc_spmi_lock_sleep,
+ .micbias_enable_status = msm_anlg_cdc_micb_en_status,
+ .mbhc_bias = msm_anlg_cdc_enable_master_bias,
+ .mbhc_common_micb_ctrl = msm_anlg_cdc_mbhc_common_micb_ctrl,
+ .micb_internal = msm_anlg_cdc_mbhc_internal_micbias_ctrl,
+ .hph_pa_on_status = msm_anlg_cdc_mbhc_hph_pa_on_status,
+ .set_btn_thr = msm_anlg_cdc_mbhc_program_btn_thr,
+ .extn_use_mb = msm_anlg_cdc_use_mb,
+};
+
+static const uint32_t wcd_imped_val[] = {4, 8, 12, 13, 16,
+ 20, 24, 28, 32,
+ 36, 40, 44, 48};
+
+static void msm_anlg_cdc_dig_notifier_call(struct snd_soc_codec *codec,
+ const enum dig_cdc_notify_event event)
+{
+ struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: notifier call event %d\n", __func__, event);
+ blocking_notifier_call_chain(&sdm660_cdc->notifier,
+ event, NULL);
+}
+
+static void msm_anlg_cdc_notifier_call(struct snd_soc_codec *codec,
+ const enum wcd_notify_event event)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: notifier call event %d\n", __func__, event);
+ blocking_notifier_call_chain(&sdm660_cdc->notifier_mbhc, event,
+ &sdm660_cdc->mbhc);
+}
+
+static void msm_anlg_cdc_boost_on(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F, 0x0F);
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5);
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F);
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30);
+ if (get_codec_version(sdm660_cdc) < CAJON_2_0)
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82);
+ else
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0xA2);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+ 0x69, 0x69);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO,
+ 0x88, 0x88);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL,
+ 0x03, 0x03);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL,
+ 0xE1, 0xE1);
+ if (get_codec_version(sdm660_cdc) < CAJON_2_0) {
+ snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x20, 0x20);
+ /* Wait for 1ms after clock ctl enable */
+ usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+ 0xDF, 0xDF);
+ usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+ } else {
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+ 0x40, 0x00);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x20, 0x20);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+ 0x80, 0x80);
+ /* Wait for 500us after BOOST_EN to happen */
+ usleep_range(500, 510);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+ 0x40, 0x40);
+ /* Wait for 500us after BOOST pulse_skip */
+ usleep_range(500, 510);
+ }
+}
+
+static void msm_anlg_cdc_boost_off(struct snd_soc_codec *codec)
+{
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+ 0xDF, 0x5F);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x20, 0x00);
+}
+
+static void msm_anlg_cdc_bypass_on(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (get_codec_version(sdm660_cdc) < CAJON_2_0) {
+ snd_soc_write(codec,
+ MSM89XX_PMIC_ANALOG_SEC_ACCESS,
+ 0xA5);
+ snd_soc_write(codec,
+ MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3,
+ 0x07);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+ 0x40, 0x40);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+ 0xDF, 0xDF);
+ } else {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x20, 0x20);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+ 0x20, 0x20);
+ }
+}
+
+static void msm_anlg_cdc_bypass_off(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (get_codec_version(sdm660_cdc) < CAJON_2_0) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+ 0x80, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+ 0x80, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+ 0x40, 0x00);
+ } else {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+ 0x20, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x20, 0x00);
+ }
+}
+
+static void msm_anlg_cdc_boost_mode_sequence(struct snd_soc_codec *codec,
+ int flag)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (flag == EAR_PMU) {
+ switch (sdm660_cdc->boost_option) {
+ case BOOST_SWITCH:
+ if (sdm660_cdc->ear_pa_boost_set) {
+ msm_anlg_cdc_boost_off(codec);
+ msm_anlg_cdc_bypass_on(codec);
+ }
+ break;
+ case BOOST_ALWAYS:
+ msm_anlg_cdc_boost_on(codec);
+ break;
+ case BYPASS_ALWAYS:
+ msm_anlg_cdc_bypass_on(codec);
+ break;
+ case BOOST_ON_FOREVER:
+ msm_anlg_cdc_boost_on(codec);
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: invalid boost option: %d\n", __func__,
+ sdm660_cdc->boost_option);
+ break;
+ }
+ } else if (flag == EAR_PMD) {
+ switch (sdm660_cdc->boost_option) {
+ case BOOST_SWITCH:
+ if (sdm660_cdc->ear_pa_boost_set)
+ msm_anlg_cdc_bypass_off(codec);
+ break;
+ case BOOST_ALWAYS:
+ msm_anlg_cdc_boost_off(codec);
+ /* 80ms for EAR boost to settle down */
+ msleep(80);
+ break;
+ case BYPASS_ALWAYS:
+ /* nothing to do as bypass on always */
+ break;
+ case BOOST_ON_FOREVER:
+ /* nothing to do as boost on forever */
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: invalid boost option: %d\n", __func__,
+ sdm660_cdc->boost_option);
+ break;
+ }
+ } else if (flag == SPK_PMU) {
+ switch (sdm660_cdc->boost_option) {
+ case BOOST_SWITCH:
+ if (sdm660_cdc->spk_boost_set) {
+ msm_anlg_cdc_bypass_off(codec);
+ msm_anlg_cdc_boost_on(codec);
+ }
+ break;
+ case BOOST_ALWAYS:
+ msm_anlg_cdc_boost_on(codec);
+ break;
+ case BYPASS_ALWAYS:
+ msm_anlg_cdc_bypass_on(codec);
+ break;
+ case BOOST_ON_FOREVER:
+ msm_anlg_cdc_boost_on(codec);
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: invalid boost option: %d\n", __func__,
+ sdm660_cdc->boost_option);
+ break;
+ }
+ } else if (flag == SPK_PMD) {
+ switch (sdm660_cdc->boost_option) {
+ case BOOST_SWITCH:
+ if (sdm660_cdc->spk_boost_set) {
+ msm_anlg_cdc_boost_off(codec);
+ /*
+ * Add 40 ms sleep for the spk
+ * boost to settle down
+ */
+ msleep(40);
+ }
+ break;
+ case BOOST_ALWAYS:
+ msm_anlg_cdc_boost_off(codec);
+ /*
+ * Add 40 ms sleep for the spk
+ * boost to settle down
+ */
+ msleep(40);
+ break;
+ case BYPASS_ALWAYS:
+ /* nothing to do as bypass on always */
+ break;
+ case BOOST_ON_FOREVER:
+ /* nothing to do as boost on forever */
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: invalid boost option: %d\n", __func__,
+ sdm660_cdc->boost_option);
+ break;
+ }
+ }
+}
+
+static int msm_anlg_cdc_dt_parse_vreg_info(struct device *dev,
+ struct sdm660_cdc_regulator *vreg, const char *vreg_name,
+ bool ondemand)
+{
+ int len, ret = 0;
+ const __be32 *prop;
+ char prop_name[CODEC_DT_MAX_PROP_SIZE];
+ struct device_node *regnode = NULL;
+ u32 prop_val;
+
+ snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply",
+ vreg_name);
+ regnode = of_parse_phandle(dev->of_node, prop_name, 0);
+
+ if (!regnode) {
+ dev_err(dev, "Looking up %s property in node %s failed\n",
+ prop_name, dev->of_node->full_name);
+ return -ENODEV;
+ }
+
+ dev_dbg(dev, "Looking up %s property in node %s\n",
+ prop_name, dev->of_node->full_name);
+
+ vreg->name = vreg_name;
+ vreg->ondemand = ondemand;
+
+ snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
+ "qcom,%s-voltage", vreg_name);
+ prop = of_get_property(dev->of_node, prop_name, &len);
+
+ if (!prop || (len != (2 * sizeof(__be32)))) {
+ dev_err(dev, "%s %s property\n",
+ prop ? "invalid format" : "no", prop_name);
+ return -EINVAL;
+ }
+ vreg->min_uv = be32_to_cpup(&prop[0]);
+ vreg->max_uv = be32_to_cpup(&prop[1]);
+
+ snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
+ "qcom,%s-current", vreg_name);
+
+ ret = of_property_read_u32(dev->of_node, prop_name, &prop_val);
+ if (ret) {
+ dev_err(dev, "Looking up %s property in node %s failed",
+ prop_name, dev->of_node->full_name);
+ return -EFAULT;
+ }
+ vreg->optimum_ua = prop_val;
+
+ dev_dbg(dev, "%s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n\n", vreg->name,
+ vreg->min_uv, vreg->max_uv, vreg->optimum_ua, vreg->ondemand);
+ return 0;
+}
+
+static void msm_anlg_cdc_dt_parse_boost_info(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc_priv =
+ snd_soc_codec_get_drvdata(codec);
+ const char *prop_name = "qcom,cdc-boost-voltage";
+ int boost_voltage, ret;
+
+ ret = of_property_read_u32(codec->dev->of_node, prop_name,
+ &boost_voltage);
+ if (ret) {
+ dev_dbg(codec->dev, "Looking up %s property in node %s failed\n",
+ prop_name, codec->dev->of_node->full_name);
+ boost_voltage = DEFAULT_BOOST_VOLTAGE;
+ }
+ if (boost_voltage < MIN_BOOST_VOLTAGE ||
+ boost_voltage > MAX_BOOST_VOLTAGE) {
+ dev_err(codec->dev,
+ "Incorrect boost voltage. Reverting to default\n");
+ boost_voltage = DEFAULT_BOOST_VOLTAGE;
+ }
+
+ sdm660_cdc_priv->boost_voltage =
+ VOLTAGE_CONVERTER(boost_voltage, MIN_BOOST_VOLTAGE,
+ BOOST_VOLTAGE_STEP);
+ dev_dbg(codec->dev, "Boost voltage value is: %d\n",
+ boost_voltage);
+}
+
+static void msm_anlg_cdc_dt_parse_micbias_info(struct device *dev,
+ struct wcd_micbias_setting *micbias)
+{
+ const char *prop_name = "qcom,cdc-micbias-cfilt-mv";
+ int ret;
+
+ ret = of_property_read_u32(dev->of_node, prop_name,
+ &micbias->cfilt1_mv);
+ if (ret) {
+ dev_dbg(dev, "Looking up %s property in node %s failed",
+ prop_name, dev->of_node->full_name);
+ micbias->cfilt1_mv = MICBIAS_DEFAULT_VAL;
+ }
+}
+
+static struct sdm660_cdc_pdata *msm_anlg_cdc_populate_dt_pdata(
+ struct device *dev)
+{
+ struct sdm660_cdc_pdata *pdata;
+ int ret, static_cnt, ond_cnt, idx, i;
+ const char *name = NULL;
+ const char *static_prop_name = "qcom,cdc-static-supplies";
+ const char *ond_prop_name = "qcom,cdc-on-demand-supplies";
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ static_cnt = of_property_count_strings(dev->of_node, static_prop_name);
+ if (IS_ERR_VALUE(static_cnt)) {
+ dev_err(dev, "%s: Failed to get static supplies %d\n", __func__,
+ static_cnt);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* On-demand supply list is an optional property */
+ ond_cnt = of_property_count_strings(dev->of_node, ond_prop_name);
+ if (IS_ERR_VALUE(ond_cnt))
+ ond_cnt = 0;
+
+ WARN_ON(static_cnt <= 0 || ond_cnt < 0);
+ if ((static_cnt + ond_cnt) > ARRAY_SIZE(pdata->regulator)) {
+ dev_err(dev, "%s: Num of supplies %u > max supported %zd\n",
+ __func__, (static_cnt + ond_cnt),
+ ARRAY_SIZE(pdata->regulator));
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (idx = 0; idx < static_cnt; idx++) {
+ ret = of_property_read_string_index(dev->of_node,
+ static_prop_name, idx,
+ &name);
+ if (ret) {
+ dev_err(dev, "%s: of read string %s idx %d error %d\n",
+ __func__, static_prop_name, idx, ret);
+ goto err;
+ }
+
+ dev_dbg(dev, "%s: Found static cdc supply %s\n", __func__,
+ name);
+ ret = msm_anlg_cdc_dt_parse_vreg_info(dev,
+ &pdata->regulator[idx],
+ name, false);
+ if (ret) {
+ dev_err(dev, "%s:err parsing vreg for %s idx %d\n",
+ __func__, name, idx);
+ goto err;
+ }
+ }
+
+ for (i = 0; i < ond_cnt; i++, idx++) {
+ ret = of_property_read_string_index(dev->of_node, ond_prop_name,
+ i, &name);
+ if (ret) {
+ dev_err(dev, "%s: err parsing on_demand for %s idx %d\n",
+ __func__, ond_prop_name, i);
+ goto err;
+ }
+
+ dev_dbg(dev, "%s: Found on-demand cdc supply %s\n", __func__,
+ name);
+ ret = msm_anlg_cdc_dt_parse_vreg_info(dev,
+ &pdata->regulator[idx],
+ name, true);
+ if (ret) {
+ dev_err(dev, "%s: err parsing vreg on_demand for %s idx %d\n",
+ __func__, name, idx);
+ goto err;
+ }
+ }
+ msm_anlg_cdc_dt_parse_micbias_info(dev, &pdata->micbias);
+
+ return pdata;
+err:
+ devm_kfree(dev, pdata);
+ dev_err(dev, "%s: Failed to populate DT data ret = %d\n",
+ __func__, ret);
+ return NULL;
+}
+
+static int msm_anlg_cdc_codec_enable_on_demand_supply(
+ struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+ struct on_demand_supply *supply;
+
+ if (w->shift >= ON_DEMAND_SUPPLIES_MAX) {
+ dev_err(codec->dev, "%s: error index > MAX Demand supplies",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+ dev_dbg(codec->dev, "%s: supply: %s event: %d ref: %d\n",
+ __func__, on_demand_supply_name[w->shift], event,
+ atomic_read(&sdm660_cdc->on_demand_list[w->shift].ref));
+
+ supply = &sdm660_cdc->on_demand_list[w->shift];
+ WARN_ONCE(!supply->supply, "%s isn't defined\n",
+ on_demand_supply_name[w->shift]);
+ if (!supply->supply) {
+ dev_err(codec->dev, "%s: err supply not present ond for %d",
+ __func__, w->shift);
+ goto out;
+ }
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (atomic_inc_return(&supply->ref) == 1) {
+ ret = regulator_set_voltage(supply->supply,
+ supply->min_uv,
+ supply->max_uv);
+ if (ret) {
+ dev_err(codec->dev,
+ "Setting regulator voltage(en) for micbias with err = %d\n",
+ ret);
+ goto out;
+ }
+ ret = regulator_set_load(supply->supply,
+ supply->optimum_ua);
+ if (ret < 0) {
+ dev_err(codec->dev,
+ "Setting regulator optimum mode(en) failed for micbias with err = %d\n",
+ ret);
+ goto out;
+ }
+ ret = regulator_enable(supply->supply);
+ }
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to enable %s\n",
+ __func__,
+ on_demand_supply_name[w->shift]);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (atomic_read(&supply->ref) == 0) {
+ dev_dbg(codec->dev, "%s: %s supply has been disabled.\n",
+ __func__, on_demand_supply_name[w->shift]);
+ goto out;
+ }
+ if (atomic_dec_return(&supply->ref) == 0) {
+ ret = regulator_disable(supply->supply);
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to disable %s\n",
+ __func__,
+ on_demand_supply_name[w->shift]);
+ ret = regulator_set_voltage(supply->supply,
+ 0,
+ supply->max_uv);
+ if (ret) {
+ dev_err(codec->dev,
+ "Setting regulator voltage(dis) failed for micbias with err = %d\n",
+ ret);
+ goto out;
+ }
+ ret = regulator_set_load(supply->supply, 0);
+ if (ret < 0)
+ dev_err(codec->dev,
+ "Setting regulator optimum mode(dis) failed for micbias with err = %d\n",
+ ret);
+ }
+ break;
+ default:
+ break;
+ }
+out:
+ return ret;
+}
+
+static int msm_anlg_cdc_codec_enable_clock_block(struct snd_soc_codec *codec,
+ int enable)
+{
+ struct msm_asoc_mach_data *pdata = NULL;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+ if (enable) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30, 0x30);
+ msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_CLK_ON);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x0C);
+ } else {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x00);
+ }
+ return 0;
+}
+
+static int msm_anlg_cdc_codec_enable_charge_pump(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: event = %d\n", __func__, event);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ msm_anlg_cdc_codec_enable_clock_block(codec, 1);
+ if (!(strcmp(w->name, "EAR CP"))) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x80, 0x80);
+ msm_anlg_cdc_boost_mode_sequence(codec, EAR_PMU);
+ } else if (get_codec_version(sdm660_cdc) >= DIANGU) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x80, 0x80);
+ } else {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0xC0, 0xC0);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Wait for 1ms post powerup of chargepump */
+ usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Wait for 1ms post powerdown of chargepump */
+ usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+ if (!(strcmp(w->name, "EAR CP"))) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x80, 0x00);
+ if (sdm660_cdc->boost_option != BOOST_ALWAYS) {
+ dev_dbg(codec->dev,
+ "%s: boost_option:%d, tear down ear\n",
+ __func__, sdm660_cdc->boost_option);
+ msm_anlg_cdc_boost_mode_sequence(codec,
+ EAR_PMD);
+ }
+ /*
+ * Reset pa select bit from ear to hph after ear pa
+ * is disabled and HPH DAC disable to reduce ear
+ * turn off pop and avoid HPH pop in concurrency
+ */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x80, 0x00);
+ } else {
+ if (get_codec_version(sdm660_cdc) < DIANGU)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x40, 0x00);
+ if (sdm660_cdc->rx_bias_count == 0)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x80, 0x00);
+ dev_dbg(codec->dev, "%s: rx_bias_count = %d\n",
+ __func__, sdm660_cdc->rx_bias_count);
+ }
+ break;
+ }
+ return 0;
+}
+
+static int msm_anlg_cdc_ear_pa_boost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] =
+ (sdm660_cdc->ear_pa_boost_set ? 1 : 0);
+ dev_dbg(codec->dev, "%s: sdm660_cdc->ear_pa_boost_set = %d\n",
+ __func__, sdm660_cdc->ear_pa_boost_set);
+ return 0;
+}
+
+static int msm_anlg_cdc_ear_pa_boost_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ sdm660_cdc->ear_pa_boost_set =
+ (ucontrol->value.integer.value[0] ? true : false);
+ return 0;
+}
+
+static int msm_anlg_cdc_loopback_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_asoc_mach_data *pdata = NULL;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ return pdata->lb_mode;
+}
+
+static int msm_anlg_cdc_loopback_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_asoc_mach_data *pdata = NULL;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ pdata->lb_mode = false;
+ break;
+ case 1:
+ pdata->lb_mode = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_anlg_cdc_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 ear_pa_gain;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (get_codec_version(sdm660_cdc) >= DIANGU) {
+ ear_pa_gain = snd_soc_read(codec,
+ MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC);
+ ear_pa_gain = (ear_pa_gain >> 1) & 0x3;
+
+ if (ear_pa_gain == 0x00) {
+ ucontrol->value.integer.value[0] = 3;
+ } else if (ear_pa_gain == 0x01) {
+ ucontrol->value.integer.value[0] = 2;
+ } else if (ear_pa_gain == 0x02) {
+ ucontrol->value.integer.value[0] = 1;
+ } else if (ear_pa_gain == 0x03) {
+ ucontrol->value.integer.value[0] = 0;
+ } else {
+ dev_err(codec->dev,
+ "%s: ERROR: Unsupported Ear Gain = 0x%x\n",
+ __func__, ear_pa_gain);
+ return -EINVAL;
+ }
+ } else {
+ ear_pa_gain = snd_soc_read(codec,
+ MSM89XX_PMIC_ANALOG_RX_EAR_CTL);
+ ear_pa_gain = (ear_pa_gain >> 5) & 0x1;
+ if (ear_pa_gain == 0x00) {
+ ucontrol->value.integer.value[0] = 0;
+ } else if (ear_pa_gain == 0x01) {
+ ucontrol->value.integer.value[0] = 3;
+ } else {
+ dev_err(codec->dev,
+ "%s: ERROR: Unsupported Ear Gain = 0x%x\n",
+ __func__, ear_pa_gain);
+ return -EINVAL;
+ }
+ }
+ dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__, ear_pa_gain);
+ return 0;
+}
+
+static int msm_anlg_cdc_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 ear_pa_gain;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ if (get_codec_version(sdm660_cdc) >= DIANGU) {
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ ear_pa_gain = 0x06;
+ break;
+ case 1:
+ ear_pa_gain = 0x04;
+ break;
+ case 2:
+ ear_pa_gain = 0x02;
+ break;
+ case 3:
+ ear_pa_gain = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+ 0x06, ear_pa_gain);
+ } else {
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ ear_pa_gain = 0x00;
+ break;
+ case 3:
+ ear_pa_gain = 0x20;
+ break;
+ case 1:
+ case 2:
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+ 0x20, ear_pa_gain);
+ }
+ return 0;
+}
+
+static int msm_anlg_cdc_hph_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (sdm660_cdc->hph_mode == NORMAL_MODE) {
+ ucontrol->value.integer.value[0] = 0;
+ } else if (sdm660_cdc->hph_mode == HD2_MODE) {
+ ucontrol->value.integer.value[0] = 1;
+ } else {
+ dev_err(codec->dev, "%s: ERROR: Default HPH Mode= %d\n",
+ __func__, sdm660_cdc->hph_mode);
+ }
+
+ dev_dbg(codec->dev, "%s: sdm660_cdc->hph_mode = %d\n", __func__,
+ sdm660_cdc->hph_mode);
+ return 0;
+}
+
+static int msm_anlg_cdc_hph_mode_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ sdm660_cdc->hph_mode = NORMAL_MODE;
+ break;
+ case 1:
+ if (get_codec_version(sdm660_cdc) >= DIANGU)
+ sdm660_cdc->hph_mode = HD2_MODE;
+ break;
+ default:
+ sdm660_cdc->hph_mode = NORMAL_MODE;
+ break;
+ }
+ dev_dbg(codec->dev, "%s: sdm660_cdc->hph_mode_set = %d\n",
+ __func__, sdm660_cdc->hph_mode);
+ return 0;
+}
+
+static int msm_anlg_cdc_boost_option_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (sdm660_cdc->boost_option == BOOST_SWITCH) {
+ ucontrol->value.integer.value[0] = 0;
+ } else if (sdm660_cdc->boost_option == BOOST_ALWAYS) {
+ ucontrol->value.integer.value[0] = 1;
+ } else if (sdm660_cdc->boost_option == BYPASS_ALWAYS) {
+ ucontrol->value.integer.value[0] = 2;
+ } else if (sdm660_cdc->boost_option == BOOST_ON_FOREVER) {
+ ucontrol->value.integer.value[0] = 3;
+ } else {
+ dev_err(codec->dev, "%s: ERROR: Unsupported Boost option= %d\n",
+ __func__, sdm660_cdc->boost_option);
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "%s: sdm660_cdc->boost_option = %d\n", __func__,
+ sdm660_cdc->boost_option);
+ return 0;
+}
+
+static int msm_anlg_cdc_boost_option_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ sdm660_cdc->boost_option = BOOST_SWITCH;
+ break;
+ case 1:
+ sdm660_cdc->boost_option = BOOST_ALWAYS;
+ break;
+ case 2:
+ sdm660_cdc->boost_option = BYPASS_ALWAYS;
+ msm_anlg_cdc_bypass_on(codec);
+ break;
+ case 3:
+ sdm660_cdc->boost_option = BOOST_ON_FOREVER;
+ msm_anlg_cdc_boost_on(codec);
+ break;
+ default:
+ pr_err("%s: invalid boost option: %d\n", __func__,
+ sdm660_cdc->boost_option);
+ return -EINVAL;
+ }
+ dev_dbg(codec->dev, "%s: sdm660_cdc->boost_option_set = %d\n",
+ __func__, sdm660_cdc->boost_option);
+ return 0;
+}
+
+static int msm_anlg_cdc_spk_boost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (sdm660_cdc->spk_boost_set == false) {
+ ucontrol->value.integer.value[0] = 0;
+ } else if (sdm660_cdc->spk_boost_set == true) {
+ ucontrol->value.integer.value[0] = 1;
+ } else {
+ dev_err(codec->dev, "%s: ERROR: Unsupported Speaker Boost = %d\n",
+ __func__, sdm660_cdc->spk_boost_set);
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "%s: sdm660_cdc->spk_boost_set = %d\n", __func__,
+ sdm660_cdc->spk_boost_set);
+ return 0;
+}
+
+static int msm_anlg_cdc_spk_boost_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ sdm660_cdc->spk_boost_set = false;
+ break;
+ case 1:
+ sdm660_cdc->spk_boost_set = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev_dbg(codec->dev, "%s: sdm660_cdc->spk_boost_set = %d\n",
+ __func__, sdm660_cdc->spk_boost_set);
+ return 0;
+}
+
+static int msm_anlg_cdc_ext_spk_boost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (sdm660_cdc->ext_spk_boost_set == false)
+ ucontrol->value.integer.value[0] = 0;
+ else
+ ucontrol->value.integer.value[0] = 1;
+
+ dev_dbg(codec->dev, "%s: sdm660_cdc->ext_spk_boost_set = %d\n",
+ __func__, sdm660_cdc->ext_spk_boost_set);
+ return 0;
+}
+
+static int msm_anlg_cdc_ext_spk_boost_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ sdm660_cdc->ext_spk_boost_set = false;
+ break;
+ case 1:
+ sdm660_cdc->ext_spk_boost_set = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev_dbg(codec->dev, "%s: sdm660_cdc->spk_boost_set = %d\n",
+ __func__, sdm660_cdc->spk_boost_set);
+ return 0;
+}
+
+
+static const char * const msm_anlg_cdc_loopback_mode_ctrl_text[] = {
+ "DISABLE", "ENABLE"};
+static const struct soc_enum msm_anlg_cdc_loopback_mode_ctl_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_loopback_mode_ctrl_text),
+};
+
+static const char * const msm_anlg_cdc_ear_pa_boost_ctrl_text[] = {
+ "DISABLE", "ENABLE"};
+static const struct soc_enum msm_anlg_cdc_ear_pa_boost_ctl_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_ear_pa_boost_ctrl_text),
+};
+
+static const char * const msm_anlg_cdc_ear_pa_gain_text[] = {
+ "POS_1P5_DB", "POS_3_DB", "POS_4P5_DB", "POS_6_DB"};
+static const struct soc_enum msm_anlg_cdc_ear_pa_gain_enum[] = {
+ SOC_ENUM_SINGLE_EXT(4, msm_anlg_cdc_ear_pa_gain_text),
+};
+
+static const char * const msm_anlg_cdc_boost_option_ctrl_text[] = {
+ "BOOST_SWITCH", "BOOST_ALWAYS", "BYPASS_ALWAYS",
+ "BOOST_ON_FOREVER"};
+static const struct soc_enum msm_anlg_cdc_boost_option_ctl_enum[] = {
+ SOC_ENUM_SINGLE_EXT(4, msm_anlg_cdc_boost_option_ctrl_text),
+};
+static const char * const msm_anlg_cdc_spk_boost_ctrl_text[] = {
+ "DISABLE", "ENABLE"};
+static const struct soc_enum msm_anlg_cdc_spk_boost_ctl_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_spk_boost_ctrl_text),
+};
+
+static const char * const msm_anlg_cdc_ext_spk_boost_ctrl_text[] = {
+ "DISABLE", "ENABLE"};
+static const struct soc_enum msm_anlg_cdc_ext_spk_boost_ctl_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_ext_spk_boost_ctrl_text),
+};
+
+static const char * const msm_anlg_cdc_hph_mode_ctrl_text[] = {
+ "NORMAL", "HD2"};
+static const struct soc_enum msm_anlg_cdc_hph_mode_ctl_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(msm_anlg_cdc_hph_mode_ctrl_text),
+ msm_anlg_cdc_hph_mode_ctrl_text),
+};
+
+/*cut of frequency for high pass filter*/
+static const char * const cf_text[] = {
+ "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz"
+};
+
+
+static const struct snd_kcontrol_new msm_anlg_cdc_snd_controls[] = {
+
+ SOC_ENUM_EXT("RX HPH Mode", msm_anlg_cdc_hph_mode_ctl_enum[0],
+ msm_anlg_cdc_hph_mode_get, msm_anlg_cdc_hph_mode_set),
+
+ SOC_ENUM_EXT("Boost Option", msm_anlg_cdc_boost_option_ctl_enum[0],
+ msm_anlg_cdc_boost_option_get, msm_anlg_cdc_boost_option_set),
+
+ SOC_ENUM_EXT("EAR PA Boost", msm_anlg_cdc_ear_pa_boost_ctl_enum[0],
+ msm_anlg_cdc_ear_pa_boost_get, msm_anlg_cdc_ear_pa_boost_set),
+
+ SOC_ENUM_EXT("EAR PA Gain", msm_anlg_cdc_ear_pa_gain_enum[0],
+ msm_anlg_cdc_pa_gain_get, msm_anlg_cdc_pa_gain_put),
+
+ SOC_ENUM_EXT("Speaker Boost", msm_anlg_cdc_spk_boost_ctl_enum[0],
+ msm_anlg_cdc_spk_boost_get, msm_anlg_cdc_spk_boost_set),
+
+ SOC_ENUM_EXT("Ext Spk Boost", msm_anlg_cdc_ext_spk_boost_ctl_enum[0],
+ msm_anlg_cdc_ext_spk_boost_get, msm_anlg_cdc_ext_spk_boost_set),
+
+ SOC_ENUM_EXT("LOOPBACK Mode", msm_anlg_cdc_loopback_mode_ctl_enum[0],
+ msm_anlg_cdc_loopback_mode_get, msm_anlg_cdc_loopback_mode_put),
+ SOC_SINGLE_TLV("ADC1 Volume", MSM89XX_PMIC_ANALOG_TX_1_EN, 3,
+ 8, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", MSM89XX_PMIC_ANALOG_TX_2_EN, 3,
+ 8, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", MSM89XX_PMIC_ANALOG_TX_3_EN, 3,
+ 8, 0, analog_gain),
+
+
+};
+
+static int tombak_hph_impedance_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret;
+ uint32_t zl, zr;
+ bool hphr;
+ struct soc_multi_mixer_control *mc;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+
+ hphr = mc->shift;
+ ret = wcd_mbhc_get_impedance(&priv->mbhc, &zl, &zr);
+ if (ret)
+ dev_dbg(codec->dev, "%s: Failed to get mbhc imped", __func__);
+ dev_dbg(codec->dev, "%s: zl %u, zr %u\n", __func__, zl, zr);
+ ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+ SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+ tombak_hph_impedance_get, NULL),
+ SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+ tombak_hph_impedance_get, NULL),
+};
+
+static int tombak_get_hph_type(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct sdm660_cdc_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct wcd_mbhc *mbhc;
+
+ if (!priv) {
+ dev_err(codec->dev,
+ "%s: sdm660_cdc-wcd private data is NULL\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ mbhc = &priv->mbhc;
+ if (!mbhc) {
+ dev_err(codec->dev, "%s: mbhc not initialized\n", __func__);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = (u32) mbhc->hph_type;
+ dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+ SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+ tombak_get_hph_type, NULL),
+};
+
+static const char * const rdac2_mux_text[] = {
+ "ZERO", "RX2", "RX1"
+};
+
+static const struct snd_kcontrol_new adc1_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct soc_enum rdac2_mux_enum =
+ SOC_ENUM_SINGLE(MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL,
+ 0, 3, rdac2_mux_text);
+
+static const char * const adc2_mux_text[] = {
+ "ZERO", "INP2", "INP3"
+};
+
+static const char * const ext_spk_text[] = {
+ "Off", "On"
+};
+
+static const char * const wsa_spk_text[] = {
+ "ZERO", "WSA"
+};
+
+static const struct soc_enum adc2_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+static const struct soc_enum ext_spk_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ ARRAY_SIZE(ext_spk_text), ext_spk_text);
+
+static const struct soc_enum wsa_spk_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ ARRAY_SIZE(wsa_spk_text), wsa_spk_text);
+
+
+
+static const struct snd_kcontrol_new ext_spk_mux =
+ SOC_DAPM_ENUM("Ext Spk Switch Mux", ext_spk_enum);
+
+
+
+static const struct snd_kcontrol_new tx_adc2_mux =
+ SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+
+static const struct snd_kcontrol_new rdac2_mux =
+ SOC_DAPM_ENUM("RDAC2 MUX Mux", rdac2_mux_enum);
+
+static const char * const ear_text[] = {
+ "ZERO", "Switch",
+};
+
+static const struct soc_enum ear_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(ear_text), ear_text);
+
+static const struct snd_kcontrol_new ear_pa_mux[] = {
+ SOC_DAPM_ENUM("EAR_S", ear_enum)
+};
+
+static const struct snd_kcontrol_new wsa_spk_mux[] = {
+ SOC_DAPM_ENUM("WSA Spk Switch", wsa_spk_enum)
+};
+
+
+
+static const char * const hph_text[] = {
+ "ZERO", "Switch",
+};
+
+static const struct soc_enum hph_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(hph_text), hph_text);
+
+static const struct snd_kcontrol_new hphl_mux[] = {
+ SOC_DAPM_ENUM("HPHL", hph_enum)
+};
+
+static const struct snd_kcontrol_new hphr_mux[] = {
+ SOC_DAPM_ENUM("HPHR", hph_enum)
+};
+
+static const struct snd_kcontrol_new spkr_mux[] = {
+ SOC_DAPM_ENUM("SPK", hph_enum)
+};
+
+static const char * const lo_text[] = {
+ "ZERO", "Switch",
+};
+
+static const struct soc_enum lo_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(hph_text), hph_text);
+
+static const struct snd_kcontrol_new lo_mux[] = {
+ SOC_DAPM_ENUM("LINE_OUT", lo_enum)
+};
+
+static void msm_anlg_cdc_codec_enable_adc_block(struct snd_soc_codec *codec,
+ int enable)
+{
+ struct sdm660_cdc_priv *wcd8x16 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %d\n", __func__, enable);
+
+ if (enable) {
+ wcd8x16->adc_count++;
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL,
+ 0x20, 0x20);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x10, 0x10);
+ } else {
+ wcd8x16->adc_count--;
+ if (!wcd8x16->adc_count) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x10, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL,
+ 0x20, 0x0);
+ }
+ }
+}
+
+static int msm_anlg_cdc_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 adc_reg;
+ u8 init_bit_shift;
+
+ dev_dbg(codec->dev, "%s %d\n", __func__, event);
+
+ adc_reg = MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2;
+
+ if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN)
+ init_bit_shift = 5;
+ else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) ||
+ (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN))
+ init_bit_shift = 4;
+ else {
+ dev_err(codec->dev, "%s: Error, invalid adc register\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ msm_anlg_cdc_codec_enable_adc_block(codec, 1);
+ if (w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x02, 0x02);
+ /*
+ * Add delay of 10 ms to give sufficient time for the voltage
+ * to shoot up and settle so that the txfe init does not
+ * happen when the input voltage is changing too much.
+ */
+ usleep_range(10000, 10010);
+ snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift,
+ 1 << init_bit_shift);
+ if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL,
+ 0x03, 0x00);
+ else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) ||
+ (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN))
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL,
+ 0x03, 0x00);
+ /* Wait for 1ms to allow txfe settling time */
+ usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * Add delay of 12 ms before deasserting the init
+ * to reduce the tx pop
+ */
+ usleep_range(12000, 12010);
+ snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00);
+ /* Wait for 1ms to allow txfe settling time post powerup */
+ usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ msm_anlg_cdc_codec_enable_adc_block(codec, 0);
+ if (w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x02, 0x00);
+ if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL,
+ 0x03, 0x02);
+ else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) ||
+ (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN))
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL,
+ 0x03, 0x02);
+
+ break;
+ }
+ return 0;
+}
+
+static int msm_anlg_cdc_codec_enable_spk_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x01);
+ switch (sdm660_cdc->boost_option) {
+ case BOOST_SWITCH:
+ if (!sdm660_cdc->spk_boost_set)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL,
+ 0x10, 0x10);
+ break;
+ case BOOST_ALWAYS:
+ case BOOST_ON_FOREVER:
+ break;
+ case BYPASS_ALWAYS:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL,
+ 0x10, 0x10);
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: invalid boost option: %d\n", __func__,
+ sdm660_cdc->boost_option);
+ break;
+ }
+ /* Wait for 1ms after SPK_DAC CTL setting */
+ usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0xE0);
+ if (get_codec_version(sdm660_cdc) != TOMBAK_1_0)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x01, 0x01);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Wait for 1ms after SPK_VBAT_LDO Enable */
+ usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+ switch (sdm660_cdc->boost_option) {
+ case BOOST_SWITCH:
+ if (sdm660_cdc->spk_boost_set)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+ 0xEF, 0xEF);
+ else
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL,
+ 0x10, 0x00);
+ break;
+ case BOOST_ALWAYS:
+ case BOOST_ON_FOREVER:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+ 0xEF, 0xEF);
+ break;
+ case BYPASS_ALWAYS:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x00);
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: invalid boost option: %d\n", __func__,
+ sdm660_cdc->boost_option);
+ break;
+ }
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_RX3_MUTE_OFF);
+ snd_soc_update_bits(codec, w->reg, 0x80, 0x80);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_RX3_MUTE_ON);
+ /*
+ * Add 1 ms sleep for the mute to take effect
+ */
+ usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x10);
+ if (get_codec_version(sdm660_cdc) < CAJON_2_0)
+ msm_anlg_cdc_boost_mode_sequence(codec, SPK_PMD);
+ snd_soc_update_bits(codec, w->reg, 0x80, 0x00);
+ switch (sdm660_cdc->boost_option) {
+ case BOOST_SWITCH:
+ if (sdm660_cdc->spk_boost_set)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+ 0xEF, 0x69);
+ break;
+ case BOOST_ALWAYS:
+ case BOOST_ON_FOREVER:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+ 0xEF, 0x69);
+ break;
+ case BYPASS_ALWAYS:
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: invalid boost option: %d\n", __func__,
+ sdm660_cdc->boost_option);
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0x00);
+ /* Wait for 1ms to allow setting time for spkr path disable */
+ usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x00);
+ if (get_codec_version(sdm660_cdc) != TOMBAK_1_0)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x00);
+ if (get_codec_version(sdm660_cdc) >= CAJON_2_0)
+ msm_anlg_cdc_boost_mode_sequence(codec, SPK_PMD);
+ break;
+ }
+ return 0;
+}
+
+static int msm_anlg_cdc_codec_enable_dig_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+ struct msm_asoc_mach_data *pdata = NULL;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+
+ dev_dbg(codec->dev, "%s event %d w->name %s\n", __func__,
+ event, w->name);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ msm_anlg_cdc_codec_enable_clock_block(codec, 1);
+ snd_soc_update_bits(codec, w->reg, 0x80, 0x80);
+ msm_anlg_cdc_boost_mode_sequence(codec, SPK_PMU);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (sdm660_cdc->rx_bias_count == 0)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x80, 0x00);
+ }
+ return 0;
+}
+
+
+
+static bool msm_anlg_cdc_use_mb(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (get_codec_version(sdm660_cdc) < CAJON)
+ return true;
+ else
+ return false;
+}
+
+static void msm_anlg_cdc_set_auto_zeroing(struct snd_soc_codec *codec,
+ bool enable)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (get_codec_version(sdm660_cdc) < CONGA) {
+ if (enable)
+ /*
+ * Set autozeroing for special headset detection and
+ * buttons to work.
+ */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_2_EN,
+ 0x18, 0x10);
+ else
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_2_EN,
+ 0x18, 0x00);
+
+ } else {
+ dev_dbg(codec->dev,
+ "%s: Auto Zeroing is not required from CONGA\n",
+ __func__);
+ }
+}
+
+static void msm_anlg_cdc_trim_btn_reg(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ if (get_codec_version(sdm660_cdc) == TOMBAK_1_0) {
+ pr_debug("%s: This device needs to be trimmed\n", __func__);
+ /*
+ * Calculate the trim value for each device used
+ * till is comes in production by hardware team
+ */
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SEC_ACCESS,
+ 0xA5, 0xA5);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_TRIM_CTRL2,
+ 0xFF, 0x30);
+ } else {
+ dev_dbg(codec->dev, "%s: This device is trimmed at ATE\n",
+ __func__);
+ }
+}
+
+static int msm_anlg_cdc_enable_ext_mb_source(struct wcd_mbhc *wcd_mbhc,
+ bool turn_on)
+{
+ int ret = 0;
+ static int count;
+ struct snd_soc_codec *codec = wcd_mbhc->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on,
+ count);
+ if (turn_on) {
+ if (!count) {
+ ret = snd_soc_dapm_force_enable_pin(dapm,
+ "MICBIAS_REGULATOR");
+ snd_soc_dapm_sync(dapm);
+ }
+ count++;
+ } else {
+ if (count > 0)
+ count--;
+ if (!count) {
+ ret = snd_soc_dapm_disable_pin(dapm,
+ "MICBIAS_REGULATOR");
+ snd_soc_dapm_sync(dapm);
+ }
+ }
+
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to %s external micbias source\n",
+ __func__, turn_on ? "enable" : "disabled");
+ else
+ dev_dbg(codec->dev, "%s: %s external micbias source\n",
+ __func__, turn_on ? "Enabled" : "Disabled");
+
+ return ret;
+}
+
+static int msm_anlg_cdc_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+ u16 micb_int_reg;
+ char *internal1_text = "Internal1";
+ char *internal2_text = "Internal2";
+ char *internal3_text = "Internal3";
+ char *external2_text = "External2";
+ char *external_text = "External";
+ bool micbias2;
+
+ dev_dbg(codec->dev, "%s %d\n", __func__, event);
+ switch (w->reg) {
+ case MSM89XX_PMIC_ANALOG_MICB_1_EN:
+ case MSM89XX_PMIC_ANALOG_MICB_2_EN:
+ micb_int_reg = MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS;
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: Error, invalid micbias register 0x%x\n",
+ __func__, w->reg);
+ return -EINVAL;
+ }
+
+ micbias2 = (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MICB_2_EN) & 0x80);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (strnstr(w->name, internal1_text, strlen(w->name))) {
+ if (get_codec_version(sdm660_cdc) >= CAJON)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x80);
+ } else if (strnstr(w->name, internal2_text, strlen(w->name))) {
+ snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x10);
+ snd_soc_update_bits(codec, w->reg, 0x60, 0x00);
+ } else if (strnstr(w->name, internal3_text, strlen(w->name))) {
+ snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x2);
+ /*
+ * update MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2
+ * for external bias only, not for external2.
+ */
+ } else if (!strnstr(w->name, external2_text, strlen(w->name)) &&
+ strnstr(w->name, external_text,
+ strlen(w->name))) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2,
+ 0x02, 0x02);
+ }
+ if (!strnstr(w->name, external_text, strlen(w->name)))
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x05, 0x04);
+ if (w->reg == MSM89XX_PMIC_ANALOG_MICB_1_EN)
+ msm_anlg_cdc_configure_cap(codec, true, micbias2);
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (get_codec_version(sdm660_cdc) <= TOMBAK_2_0)
+ /*
+ * Wait for 20ms post micbias enable
+ * for version < tombak 2.0.
+ */
+ usleep_range(20000, 20100);
+ if (strnstr(w->name, internal1_text, strlen(w->name))) {
+ snd_soc_update_bits(codec, micb_int_reg, 0x40, 0x40);
+ } else if (strnstr(w->name, internal2_text, strlen(w->name))) {
+ snd_soc_update_bits(codec, micb_int_reg, 0x08, 0x08);
+ msm_anlg_cdc_notifier_call(codec,
+ WCD_EVENT_POST_MICBIAS_2_ON);
+ } else if (strnstr(w->name, internal3_text, 30)) {
+ snd_soc_update_bits(codec, micb_int_reg, 0x01, 0x01);
+ } else if (strnstr(w->name, external2_text, strlen(w->name))) {
+ msm_anlg_cdc_notifier_call(codec,
+ WCD_EVENT_POST_MICBIAS_2_ON);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (strnstr(w->name, internal1_text, strlen(w->name))) {
+ snd_soc_update_bits(codec, micb_int_reg, 0xC0, 0x40);
+ } else if (strnstr(w->name, internal2_text, strlen(w->name))) {
+ msm_anlg_cdc_notifier_call(codec,
+ WCD_EVENT_POST_MICBIAS_2_OFF);
+ } else if (strnstr(w->name, internal3_text, 30)) {
+ snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
+ } else if (strnstr(w->name, external2_text, strlen(w->name))) {
+ /*
+ * send micbias turn off event to mbhc driver and then
+ * break, as no need to set MICB_1_EN register.
+ */
+ msm_anlg_cdc_notifier_call(codec,
+ WCD_EVENT_POST_MICBIAS_2_OFF);
+ break;
+ }
+ if (w->reg == MSM89XX_PMIC_ANALOG_MICB_1_EN)
+ msm_anlg_cdc_configure_cap(codec, false, micbias2);
+ break;
+ }
+ return 0;
+}
+
+static void set_compander_mode(void *handle, int val)
+{
+ struct sdm660_cdc_priv *handle_cdc = handle;
+ struct snd_soc_codec *codec = handle_cdc->codec;
+
+ if (get_codec_version(handle_cdc) >= DIANGU) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+ 0x08, val);
+ };
+}
+static void update_clkdiv(void *handle, int val)
+{
+ struct sdm660_cdc_priv *handle_cdc = handle;
+ struct snd_soc_codec *codec = handle_cdc->codec;
+
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV,
+ 0xFF, val);
+}
+
+static int get_cdc_version(void *handle)
+{
+ struct sdm660_cdc_priv *sdm660_cdc = handle;
+
+ return get_codec_version(sdm660_cdc);
+}
+
+static int sdm660_wcd_codec_enable_vdd_spkr(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if (!sdm660_cdc->ext_spk_boost_set) {
+ dev_dbg(codec->dev, "%s: ext_boost not supported/disabled\n",
+ __func__);
+ return 0;
+ }
+ dev_dbg(codec->dev, "%s: %s %d\n", __func__, w->name, event);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (sdm660_cdc->spkdrv_reg) {
+ ret = regulator_enable(sdm660_cdc->spkdrv_reg);
+ if (ret)
+ dev_err(codec->dev,
+ "%s Failed to enable spkdrv reg %s\n",
+ __func__, MSM89XX_VDD_SPKDRV_NAME);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (sdm660_cdc->spkdrv_reg) {
+ ret = regulator_disable(sdm660_cdc->spkdrv_reg);
+ if (ret)
+ dev_err(codec->dev,
+ "%s: Failed to disable spkdrv_reg %s\n",
+ __func__, MSM89XX_VDD_SPKDRV_NAME);
+ }
+ break;
+ }
+ return 0;
+}
+
+
+/* The register address is the same as other codec so it can use resmgr */
+static int msm_anlg_cdc_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ sdm660_cdc->rx_bias_count++;
+ if (sdm660_cdc->rx_bias_count == 1) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+ 0x01, 0x01);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ sdm660_cdc->rx_bias_count--;
+ if (sdm660_cdc->rx_bias_count == 0) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+ 0x80, 0x00);
+ }
+ break;
+ }
+ dev_dbg(codec->dev, "%s rx_bias_count = %d\n",
+ __func__, sdm660_cdc->rx_bias_count);
+ return 0;
+}
+
+static uint32_t wcd_get_impedance_value(uint32_t imped)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wcd_imped_val) - 1; i++) {
+ if (imped >= wcd_imped_val[i] &&
+ imped < wcd_imped_val[i + 1])
+ break;
+ }
+
+ pr_debug("%s: selected impedance value = %d\n",
+ __func__, wcd_imped_val[i]);
+ return wcd_imped_val[i];
+}
+
+static void wcd_imped_config(struct snd_soc_codec *codec,
+ uint32_t imped, bool set_gain)
+{
+ uint32_t value;
+ int codec_version;
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ value = wcd_get_impedance_value(imped);
+
+ if (value < wcd_imped_val[0]) {
+ dev_dbg(codec->dev,
+ "%s, detected impedance is less than 4 Ohm\n",
+ __func__);
+ return;
+ }
+
+ codec_version = get_codec_version(sdm660_cdc);
+
+ if (set_gain) {
+ switch (codec_version) {
+ case TOMBAK_1_0:
+ case TOMBAK_2_0:
+ case CONGA:
+ /*
+ * For 32Ohm load and higher loads, Set 0x19E
+ * bit 5 to 1 (POS_0_DB_DI). For loads lower
+ * than 32Ohm (such as 16Ohm load), Set 0x19E
+ * bit 5 to 0 (POS_M4P5_DB_DI)
+ */
+ if (value >= 32)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+ 0x20, 0x20);
+ else
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+ 0x20, 0x00);
+ break;
+ case CAJON:
+ case CAJON_2_0:
+ case DIANGU:
+ case DRAX_CDC:
+ if (value >= 13) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+ 0x20, 0x20);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_NCP_VCTRL,
+ 0x07, 0x07);
+ } else {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+ 0x20, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_NCP_VCTRL,
+ 0x07, 0x04);
+ }
+ break;
+ }
+ } else {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+ 0x20, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_NCP_VCTRL,
+ 0x07, 0x04);
+ }
+
+ dev_dbg(codec->dev, "%s: Exit\n", __func__);
+}
+
+static int msm_anlg_cdc_hphl_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ uint32_t impedl, impedr;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+ ret = wcd_mbhc_get_impedance(&sdm660_cdc->mbhc,
+ &impedl, &impedr);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (get_codec_version(sdm660_cdc) > CAJON)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN,
+ 0x08, 0x08);
+ if (get_codec_version(sdm660_cdc) == CAJON ||
+ get_codec_version(sdm660_cdc) == CAJON_2_0) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST,
+ 0x80, 0x80);
+ }
+ if (get_codec_version(sdm660_cdc) > CAJON)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN,
+ 0x08, 0x00);
+ if (sdm660_cdc->hph_mode == HD2_MODE)
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_PRE_RX1_INT_ON);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x02);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x02);
+ if (!ret)
+ wcd_imped_config(codec, impedl, true);
+ else
+ dev_dbg(codec->dev, "Failed to get mbhc impedance %d\n",
+ ret);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd_imped_config(codec, impedl, false);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x00);
+ if (sdm660_cdc->hph_mode == HD2_MODE)
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_POST_RX1_INT_OFF);
+ break;
+ }
+ return 0;
+}
+
+static int msm_anlg_cdc_lo_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x20, 0x20);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x08);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x40, 0x40);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x40, 0x40);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Wait for 20ms before powerdown of lineout_dac */
+ usleep_range(20000, 20100);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x80, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x40, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x80, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x40, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x20, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int msm_anlg_cdc_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (sdm660_cdc->hph_mode == HD2_MODE)
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_PRE_RX2_INT_ON);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x02);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x02);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x01);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x00);
+ if (sdm660_cdc->hph_mode == HD2_MODE)
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_POST_RX2_INT_OFF);
+ break;
+ }
+ return 0;
+}
+
+static int msm_anlg_cdc_hph_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: %s event = %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (w->shift == 5)
+ msm_anlg_cdc_notifier_call(codec,
+ WCD_EVENT_PRE_HPHL_PA_ON);
+ else if (w->shift == 4)
+ msm_anlg_cdc_notifier_call(codec,
+ WCD_EVENT_PRE_HPHR_PA_ON);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x20, 0x20);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ /* Wait for 7ms to allow setting time for HPH_PA Enable */
+ usleep_range(7000, 7100);
+ if (w->shift == 5) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x04);
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_RX1_MUTE_OFF);
+ } else if (w->shift == 4) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x04);
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_RX2_MUTE_OFF);
+ }
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ if (w->shift == 5) {
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_RX1_MUTE_ON);
+ /* Wait for 20ms after HPHL RX digital mute */
+ msleep(20);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x00);
+ msm_anlg_cdc_notifier_call(codec,
+ WCD_EVENT_PRE_HPHL_PA_OFF);
+ } else if (w->shift == 4) {
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_RX2_MUTE_ON);
+ /* Wait for 20ms after HPHR RX digital mute */
+ msleep(20);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x00);
+ msm_anlg_cdc_notifier_call(codec,
+ WCD_EVENT_PRE_HPHR_PA_OFF);
+ }
+ if (get_codec_version(sdm660_cdc) >= CAJON) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP,
+ 0xF0, 0x30);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (w->shift == 5) {
+ clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK,
+ &sdm660_cdc->mbhc.hph_pa_dac_state);
+ msm_anlg_cdc_notifier_call(codec,
+ WCD_EVENT_POST_HPHL_PA_OFF);
+ } else if (w->shift == 4) {
+ clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK,
+ &sdm660_cdc->mbhc.hph_pa_dac_state);
+ msm_anlg_cdc_notifier_call(codec,
+ WCD_EVENT_POST_HPHR_PA_OFF);
+ }
+ /* Wait for 15ms after HPH RX teardown */
+ usleep_range(15000, 15100);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* RDAC Connections */
+ {"HPHR DAC", NULL, "RDAC2 MUX"},
+ {"RDAC2 MUX", "RX1", "PDM_IN_RX1"},
+ {"RDAC2 MUX", "RX2", "PDM_IN_RX2"},
+
+ /* WSA */
+ {"WSA_SPK OUT", NULL, "WSA Spk Switch"},
+ {"WSA Spk Switch", "WSA", "EAR PA"},
+
+ /* Earpiece (RX MIX1) */
+ {"EAR", NULL, "EAR_S"},
+ {"EAR_S", "Switch", "EAR PA"},
+ {"EAR PA", NULL, "RX_BIAS"},
+ {"EAR PA", NULL, "HPHL DAC"},
+ {"EAR PA", NULL, "HPHR DAC"},
+ {"EAR PA", NULL, "EAR CP"},
+
+ /* Headset (RX MIX1 and RX MIX2) */
+ {"HEADPHONE", NULL, "HPHL PA"},
+ {"HEADPHONE", NULL, "HPHR PA"},
+
+ {"Ext Spk", NULL, "Ext Spk Switch"},
+ {"Ext Spk Switch", "On", "HPHL PA"},
+ {"Ext Spk Switch", "On", "HPHR PA"},
+
+ {"HPHL PA", NULL, "HPHL"},
+ {"HPHR PA", NULL, "HPHR"},
+ {"HPHL", "Switch", "HPHL DAC"},
+ {"HPHR", "Switch", "HPHR DAC"},
+ {"HPHL PA", NULL, "CP"},
+ {"HPHL PA", NULL, "RX_BIAS"},
+ {"HPHR PA", NULL, "CP"},
+ {"HPHR PA", NULL, "RX_BIAS"},
+ {"HPHL DAC", NULL, "PDM_IN_RX1"},
+
+ {"SPK_OUT", NULL, "SPK PA"},
+ {"SPK PA", NULL, "SPK_RX_BIAS"},
+ {"SPK PA", NULL, "SPK"},
+ {"SPK", "Switch", "SPK DAC"},
+ {"SPK DAC", NULL, "PDM_IN_RX3"},
+ {"SPK DAC", NULL, "VDD_SPKDRV"},
+
+ /* lineout */
+ {"LINEOUT", NULL, "LINEOUT PA"},
+ {"LINEOUT PA", NULL, "SPK_RX_BIAS"},
+ {"LINEOUT PA", NULL, "LINE_OUT"},
+ {"LINE_OUT", "Switch", "LINEOUT DAC"},
+ {"LINEOUT DAC", NULL, "PDM_IN_RX3"},
+
+ /* lineout to WSA */
+ {"WSA_SPK OUT", NULL, "LINEOUT PA"},
+
+ {"PDM_IN_RX1", NULL, "RX1 CLK"},
+ {"PDM_IN_RX2", NULL, "RX2 CLK"},
+ {"PDM_IN_RX3", NULL, "RX3 CLK"},
+
+ {"ADC1_OUT", NULL, "ADC1"},
+ {"ADC2_OUT", NULL, "ADC2"},
+ {"ADC3_OUT", NULL, "ADC3"},
+
+ /* ADC Connections */
+ {"ADC2", NULL, "ADC2 MUX"},
+ {"ADC3", NULL, "ADC2 MUX"},
+ {"ADC2 MUX", "INP2", "ADC2_INP2"},
+ {"ADC2 MUX", "INP3", "ADC2_INP3"},
+
+ {"ADC1", NULL, "ADC1_INP1"},
+ {"ADC1_INP1", "Switch", "AMIC1"},
+ {"ADC2_INP2", NULL, "AMIC2"},
+ {"ADC2_INP3", NULL, "AMIC3"},
+
+ {"MIC BIAS Internal1", NULL, "INT_LDO_H"},
+ {"MIC BIAS Internal2", NULL, "INT_LDO_H"},
+ {"MIC BIAS External", NULL, "INT_LDO_H"},
+ {"MIC BIAS External2", NULL, "INT_LDO_H"},
+ {"MIC BIAS Internal1", NULL, "MICBIAS_REGULATOR"},
+ {"MIC BIAS Internal2", NULL, "MICBIAS_REGULATOR"},
+ {"MIC BIAS External", NULL, "MICBIAS_REGULATOR"},
+ {"MIC BIAS External2", NULL, "MICBIAS_REGULATOR"},
+};
+
+static int msm_anlg_cdc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(dai->codec);
+
+ dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n",
+ __func__,
+ substream->name, substream->stream);
+ /*
+ * If status_mask is BUS_DOWN it means SSR is not complete.
+ * So return error.
+ */
+ if (test_bit(BUS_DOWN, &sdm660_cdc->status_mask)) {
+ dev_err(dai->codec->dev, "Error, Device is not up post SSR\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void msm_anlg_cdc_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ dev_dbg(dai->codec->dev,
+ "%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+}
+
+int msm_anlg_cdc_mclk_enable(struct snd_soc_codec *codec,
+ int mclk_enable, bool dapm)
+{
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: mclk_enable = %u, dapm = %d\n",
+ __func__, mclk_enable, dapm);
+ if (mclk_enable) {
+ sdm660_cdc->int_mclk0_enabled = true;
+ msm_anlg_cdc_codec_enable_clock_block(codec, 1);
+ } else {
+ if (!sdm660_cdc->int_mclk0_enabled) {
+ dev_err(codec->dev, "Error, MCLK already diabled\n");
+ return -EINVAL;
+ }
+ sdm660_cdc->int_mclk0_enabled = false;
+ msm_anlg_cdc_codec_enable_clock_block(codec, 0);
+ }
+ return 0;
+}
+
+static int msm_anlg_cdc_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ dev_dbg(dai->codec->dev, "%s\n", __func__);
+ return 0;
+}
+
+static int msm_anlg_cdc_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ dev_dbg(dai->codec->dev, "%s\n", __func__);
+ return 0;
+}
+
+static int msm_anlg_cdc_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+
+{
+ dev_dbg(dai->codec->dev, "%s\n", __func__);
+ return 0;
+}
+
+static int msm_anlg_cdc_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+
+{
+ dev_dbg(dai->codec->dev, "%s\n", __func__);
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_anlg_cdc_dai_ops = {
+ .startup = msm_anlg_cdc_startup,
+ .shutdown = msm_anlg_cdc_shutdown,
+ .set_sysclk = msm_anlg_cdc_set_dai_sysclk,
+ .set_fmt = msm_anlg_cdc_set_dai_fmt,
+ .set_channel_map = msm_anlg_cdc_set_channel_map,
+ .get_channel_map = msm_anlg_cdc_get_channel_map,
+};
+
+static struct snd_soc_dai_driver msm_anlg_cdc_i2s_dai[] = {
+ {
+ .name = "msm_anlg_cdc_i2s_rx1",
+ .id = AIF1_PB,
+ .playback = {
+ .stream_name = "PDM Playback",
+ .rates = SDM660_CDC_RATES,
+ .formats = SDM660_CDC_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 3,
+ },
+ .ops = &msm_anlg_cdc_dai_ops,
+ },
+ {
+ .name = "msm_anlg_cdc_i2s_tx1",
+ .id = AIF1_CAP,
+ .capture = {
+ .stream_name = "PDM Capture",
+ .rates = SDM660_CDC_RATES,
+ .formats = SDM660_CDC_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &msm_anlg_cdc_dai_ops,
+ },
+ {
+ .name = "msm_anlg_cdc_i2s_tx2",
+ .id = AIF3_SVA,
+ .capture = {
+ .stream_name = "RecordSVA",
+ .rates = SDM660_CDC_RATES,
+ .formats = SDM660_CDC_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &msm_anlg_cdc_dai_ops,
+ },
+ {
+ .name = "msm_anlg_vifeedback",
+ .id = AIF2_VIFEED,
+ .capture = {
+ .stream_name = "VIfeed",
+ .rates = SDM660_CDC_RATES,
+ .formats = SDM660_CDC_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &msm_anlg_cdc_dai_ops,
+ },
+};
+
+
+static int msm_anlg_cdc_codec_enable_lo_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ dev_dbg(codec->dev, "%s: %d %s\n", __func__, event, w->name);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_RX3_MUTE_OFF);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_RX3_MUTE_ON);
+ break;
+ }
+
+ return 0;
+}
+
+static int msm_anlg_cdc_codec_enable_spk_ext_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: %s event = %d\n", __func__, w->name, event);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(codec->dev,
+ "%s: enable external speaker PA\n", __func__);
+ if (sdm660_cdc->codec_spk_ext_pa_cb)
+ sdm660_cdc->codec_spk_ext_pa_cb(codec, 1);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_dbg(codec->dev,
+ "%s: enable external speaker PA\n", __func__);
+ if (sdm660_cdc->codec_spk_ext_pa_cb)
+ sdm660_cdc->codec_spk_ext_pa_cb(codec, 0);
+ break;
+ }
+ return 0;
+}
+
+static int msm_anlg_cdc_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(codec->dev,
+ "%s: Sleeping 20ms after select EAR PA\n",
+ __func__);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+ 0x80, 0x80);
+ if (get_codec_version(sdm660_cdc) < CONGA)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFF, 0x2A);
+ if (get_codec_version(sdm660_cdc) >= DIANGU) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x08, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x04);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x04);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(codec->dev,
+ "%s: Sleeping 20ms after enabling EAR PA\n",
+ __func__);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+ 0x40, 0x40);
+ /* Wait for 7ms after EAR PA enable */
+ usleep_range(7000, 7100);
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_RX1_MUTE_OFF);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_RX1_MUTE_ON);
+ /* Wait for 20ms for RX digital mute to take effect */
+ msleep(20);
+ if (sdm660_cdc->boost_option == BOOST_ALWAYS) {
+ dev_dbg(codec->dev,
+ "%s: boost_option:%d, tear down ear\n",
+ __func__, sdm660_cdc->boost_option);
+ msm_anlg_cdc_boost_mode_sequence(codec, EAR_PMD);
+ }
+ if (get_codec_version(sdm660_cdc) >= DIANGU) {
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x0);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x0);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(codec->dev,
+ "%s: Sleeping 7ms after disabling EAR PA\n",
+ __func__);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+ 0x40, 0x00);
+ /* Wait for 7ms after EAR PA teardown */
+ usleep_range(7000, 7100);
+ if (get_codec_version(sdm660_cdc) < CONGA)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFF, 0x16);
+ if (get_codec_version(sdm660_cdc) >= DIANGU)
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x08, 0x08);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget msm_anlg_cdc_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, msm_anlg_cdc_codec_enable_ear_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHL PA", MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN,
+ 5, 0, NULL, 0,
+ msm_anlg_cdc_hph_pa_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PA", MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN,
+ 4, 0, NULL, 0,
+ msm_anlg_cdc_hph_pa_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("SPK PA", MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+ 7, 0, NULL, 0, msm_anlg_cdc_codec_enable_spk_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT PA", MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL,
+ 5, 0, NULL, 0, msm_anlg_cdc_codec_enable_lo_pa,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, ear_pa_mux),
+ SND_SOC_DAPM_MUX("SPK", SND_SOC_NOPM, 0, 0, spkr_mux),
+ SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, hphl_mux),
+ SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, hphr_mux),
+ SND_SOC_DAPM_MUX("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux),
+ SND_SOC_DAPM_MUX("WSA Spk Switch", SND_SOC_NOPM, 0, 0, wsa_spk_mux),
+ SND_SOC_DAPM_MUX("Ext Spk Switch", SND_SOC_NOPM, 0, 0, &ext_spk_mux),
+ SND_SOC_DAPM_MUX("LINE_OUT", SND_SOC_NOPM, 0, 0, lo_mux),
+ SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+
+ SND_SOC_DAPM_MIXER_E("HPHL DAC",
+ MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL,
+ 0, msm_anlg_cdc_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("HPHR DAC",
+ MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL,
+ 0, msm_anlg_cdc_hphr_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("SPK DAC", NULL, MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL,
+ 7, 0),
+ SND_SOC_DAPM_DAC_E("LINEOUT DAC", NULL,
+ SND_SOC_NOPM, 0, 0, msm_anlg_cdc_lo_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SPK("Ext Spk", msm_anlg_cdc_codec_enable_spk_ext_pa),
+
+ SND_SOC_DAPM_SWITCH("ADC1_INP1", SND_SOC_NOPM, 0, 0,
+ &adc1_switch),
+ SND_SOC_DAPM_SUPPLY("RX1 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RX2 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RX3 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 2, 0, msm_anlg_cdc_codec_enable_dig_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("CP", MSM89XX_PMIC_ANALOG_NCP_EN, 0, 0,
+ msm_anlg_cdc_codec_enable_charge_pump,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("EAR CP", MSM89XX_PMIC_ANALOG_NCP_EN, 4, 0,
+ msm_anlg_cdc_codec_enable_charge_pump,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("RX_BIAS", 1, SND_SOC_NOPM,
+ 0, 0, msm_anlg_cdc_codec_enable_rx_bias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("SPK_RX_BIAS", 1, SND_SOC_NOPM, 0, 0,
+ msm_anlg_cdc_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("VDD_SPKDRV", SND_SOC_NOPM, 0, 0,
+ sdm660_wcd_codec_enable_vdd_spkr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM,
+ ON_DEMAND_MICBIAS, 0,
+ msm_anlg_cdc_codec_enable_on_demand_supply,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal1",
+ MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0,
+ msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal2",
+ MSM89XX_PMIC_ANALOG_MICB_2_EN, 7, 0,
+ msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal3",
+ MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0,
+ msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, MSM89XX_PMIC_ANALOG_TX_1_EN, 7, 0,
+ msm_anlg_cdc_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2_INP2",
+ NULL, MSM89XX_PMIC_ANALOG_TX_2_EN, 7, 0,
+ msm_anlg_cdc_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2_INP3",
+ NULL, MSM89XX_PMIC_ANALOG_TX_3_EN, 7, 0,
+ msm_anlg_cdc_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS External",
+ MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0,
+ msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS External2",
+ MSM89XX_PMIC_ANALOG_MICB_2_EN, 7, 0,
+ msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_AIF_IN("PDM_IN_RX1", "PDM Playback",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PDM_IN_RX2", "PDM Playback",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PDM_IN_RX3", "PDM Playback",
+ 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("WSA_SPK OUT"),
+ SND_SOC_DAPM_OUTPUT("HEADPHONE"),
+ SND_SOC_DAPM_OUTPUT("SPK_OUT"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT"),
+ SND_SOC_DAPM_AIF_OUT("ADC1_OUT", "PDM Capture",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ADC2_OUT", "PDM Capture",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ADC3_OUT", "PDM Capture",
+ 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct sdm660_cdc_reg_mask_val msm_anlg_cdc_reg_defaults[] = {
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1),
+};
+
+static const struct sdm660_cdc_reg_mask_val
+ msm_anlg_cdc_reg_defaults_2_0[] = {
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4F),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x28),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x5F),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x88),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80),
+};
+
+static const struct sdm660_cdc_reg_mask_val conga_wcd_reg_defaults[] = {
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x28),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x0A),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80),
+};
+
+static const struct sdm660_cdc_reg_mask_val cajon_wcd_reg_defaults[] = {
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0xA8),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0xA4),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x41),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0xFA),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80),
+};
+
+static const struct sdm660_cdc_reg_mask_val cajon2p0_wcd_reg_defaults[] = {
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0xA2),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0xA8),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0xA4),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x41),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x10),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x18),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0xFA),
+ MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80),
+};
+
+static void msm_anlg_cdc_update_reg_defaults(struct snd_soc_codec *codec)
+{
+ u32 i, version;
+ struct sdm660_cdc_priv *sdm660_cdc =
+ snd_soc_codec_get_drvdata(codec);
+
+ version = get_codec_version(sdm660_cdc);
+ if (version == TOMBAK_1_0) {
+ for (i = 0; i < ARRAY_SIZE(msm_anlg_cdc_reg_defaults); i++)
+ snd_soc_write(codec, msm_anlg_cdc_reg_defaults[i].reg,
+ msm_anlg_cdc_reg_defaults[i].val);
+ } else if (version == TOMBAK_2_0) {
+ for (i = 0; i < ARRAY_SIZE(msm_anlg_cdc_reg_defaults_2_0); i++)
+ snd_soc_write(codec,
+ msm_anlg_cdc_reg_defaults_2_0[i].reg,
+ msm_anlg_cdc_reg_defaults_2_0[i].val);
+ } else if (version == CONGA) {
+ for (i = 0; i < ARRAY_SIZE(conga_wcd_reg_defaults); i++)
+ snd_soc_write(codec,
+ conga_wcd_reg_defaults[i].reg,
+ conga_wcd_reg_defaults[i].val);
+ } else if (version == CAJON) {
+ for (i = 0; i < ARRAY_SIZE(cajon_wcd_reg_defaults); i++)
+ snd_soc_write(codec,
+ cajon_wcd_reg_defaults[i].reg,
+ cajon_wcd_reg_defaults[i].val);
+ } else if (version == CAJON_2_0 || version == DIANGU
+ || version == DRAX_CDC) {
+ for (i = 0; i < ARRAY_SIZE(cajon2p0_wcd_reg_defaults); i++)
+ snd_soc_write(codec,
+ cajon2p0_wcd_reg_defaults[i].reg,
+ cajon2p0_wcd_reg_defaults[i].val);
+ }
+}
+
+static const struct sdm660_cdc_reg_mask_val
+ msm_anlg_cdc_codec_reg_init_val[] = {
+
+ /* Initialize current threshold to 350MA
+ * number of wait and run cycles to 4096
+ */
+ {MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xFF, 0x12},
+ {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0xFF, 0xFF},
+};
+
+static void msm_anlg_cdc_codec_init_reg(struct snd_soc_codec *codec)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(msm_anlg_cdc_codec_reg_init_val); i++)
+ snd_soc_update_bits(codec,
+ msm_anlg_cdc_codec_reg_init_val[i].reg,
+ msm_anlg_cdc_codec_reg_init_val[i].mask,
+ msm_anlg_cdc_codec_reg_init_val[i].val);
+}
+
+static int msm_anlg_cdc_bringup(struct snd_soc_codec *codec)
+{
+ snd_soc_write(codec,
+ MSM89XX_PMIC_DIGITAL_SEC_ACCESS,
+ 0xA5);
+ snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x01);
+ snd_soc_write(codec,
+ MSM89XX_PMIC_ANALOG_SEC_ACCESS,
+ 0xA5);
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x01);
+ snd_soc_write(codec,
+ MSM89XX_PMIC_DIGITAL_SEC_ACCESS,
+ 0xA5);
+ snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00);
+ snd_soc_write(codec,
+ MSM89XX_PMIC_ANALOG_SEC_ACCESS,
+ 0xA5);
+ snd_soc_write(codec, MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00);
+
+ return 0;
+}
+
+static struct regulator *msm_anlg_cdc_find_regulator(
+ const struct sdm660_cdc_priv *sdm660_cdc,
+ const char *name)
+{
+ int i;
+
+ for (i = 0; i < sdm660_cdc->num_of_supplies; i++) {
+ if (sdm660_cdc->supplies[i].supply &&
+ !strcmp(sdm660_cdc->supplies[i].supply, name))
+ return sdm660_cdc->supplies[i].consumer;
+ }
+
+ dev_dbg(sdm660_cdc->dev, "Error: regulator not found:%s\n"
+ , name);
+ return NULL;
+}
+
+static void msm_anlg_cdc_update_micbias_regulator(
+ const struct sdm660_cdc_priv *sdm660_cdc,
+ const char *name,
+ struct on_demand_supply *micbias_supply)
+{
+ int i;
+ struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data;
+
+ for (i = 0; i < sdm660_cdc->num_of_supplies; i++) {
+ if (sdm660_cdc->supplies[i].supply &&
+ !strcmp(sdm660_cdc->supplies[i].supply, name)) {
+ micbias_supply->supply =
+ sdm660_cdc->supplies[i].consumer;
+ micbias_supply->min_uv = pdata->regulator[i].min_uv;
+ micbias_supply->max_uv = pdata->regulator[i].max_uv;
+ micbias_supply->optimum_ua =
+ pdata->regulator[i].optimum_ua;
+ return;
+ }
+ }
+
+ dev_err(sdm660_cdc->dev, "Error: regulator not found:%s\n", name);
+}
+
+static int msm_anlg_cdc_device_down(struct snd_soc_codec *codec)
+{
+ struct msm_asoc_mach_data *pdata = NULL;
+ struct sdm660_cdc_priv *sdm660_cdc_priv =
+ snd_soc_codec_get_drvdata(codec);
+ unsigned int tx_1_en;
+ unsigned int tx_2_en;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+ dev_dbg(codec->dev, "%s: device down!\n", __func__);
+
+ tx_1_en = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_TX_1_EN);
+ tx_2_en = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_TX_2_EN);
+ tx_1_en = tx_1_en & 0x7f;
+ tx_2_en = tx_2_en & 0x7f;
+ snd_soc_write(codec,
+ MSM89XX_PMIC_ANALOG_TX_1_EN, tx_1_en);
+ snd_soc_write(codec,
+ MSM89XX_PMIC_ANALOG_TX_2_EN, tx_2_en);
+ if (sdm660_cdc_priv->boost_option == BOOST_ON_FOREVER) {
+ if ((snd_soc_read(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL)
+ & 0x80) == 0) {
+ msm_anlg_cdc_dig_notifier_call(codec,
+ DIG_CDC_EVENT_CLK_ON);
+ snd_soc_write(codec,
+ MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL,
+ 0x0C, 0x0C);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+ 0x84, 0x84);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL,
+ 0x10, 0x10);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL,
+ 0x1F, 0x1F);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+ 0x90, 0x90);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+ 0xFF, 0xFF);
+ /* Wait for 20us for boost settings to take effect */
+ usleep_range(20, 21);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL,
+ 0xFF, 0xFF);
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+ 0xE9, 0xE9);
+ }
+ }
+ msm_anlg_cdc_boost_off(codec);
+ sdm660_cdc_priv->hph_mode = NORMAL_MODE;
+ /* Disable PA to avoid pop during codec bring up */
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN,
+ 0x30, 0x00);
+ /* 40ms to allow boost to discharge */
+ msleep(40);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+ 0x80, 0x00);
+ snd_soc_write(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20);
+ snd_soc_write(codec,
+ MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20);
+ snd_soc_write(codec,
+ MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12);
+ snd_soc_write(codec,
+ MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x93);
+
+ msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_SSR_DOWN);
+ atomic_set(&pdata->int_mclk0_enabled, false);
+ set_bit(BUS_DOWN, &sdm660_cdc_priv->status_mask);
+ snd_soc_card_change_online_state(codec->component.card, 0);
+
+ return 0;
+}
+
+static int msm_anlg_cdc_device_up(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc_priv =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: device up!\n", __func__);
+
+ msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_SSR_UP);
+ clear_bit(BUS_DOWN, &sdm660_cdc_priv->status_mask);
+ snd_soc_card_change_online_state(codec->component.card, 1);
+ /* delay is required to make sure sound card state updated */
+ usleep_range(5000, 5100);
+
+ snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_INT_EN_SET,
+ MSM89XX_PMIC_DIGITAL_INT_EN_SET__POR);
+ snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_INT_EN_CLR,
+ MSM89XX_PMIC_DIGITAL_INT_EN_CLR__POR);
+
+ msm_anlg_cdc_set_boost_v(codec);
+ msm_anlg_cdc_set_micb_v(codec);
+ if (sdm660_cdc_priv->boost_option == BOOST_ON_FOREVER)
+ msm_anlg_cdc_boost_on(codec);
+ else if (sdm660_cdc_priv->boost_option == BYPASS_ALWAYS)
+ msm_anlg_cdc_bypass_on(codec);
+
+ return 0;
+}
+
+static int sdm660_cdc_notifier_service_cb(struct notifier_block *nb,
+ unsigned long opcode, void *ptr)
+{
+ struct snd_soc_codec *codec;
+ struct sdm660_cdc_priv *sdm660_cdc_priv =
+ container_of(nb, struct sdm660_cdc_priv,
+ audio_ssr_nb);
+ bool adsp_ready = false;
+ bool timedout;
+ unsigned long timeout;
+ static bool initial_boot = true;
+
+ codec = sdm660_cdc_priv->codec;
+ dev_dbg(codec->dev, "%s: Service opcode 0x%lx\n", __func__, opcode);
+
+ switch (opcode) {
+ case AUDIO_NOTIFIER_SERVICE_DOWN:
+ if (initial_boot) {
+ initial_boot = false;
+ break;
+ }
+ dev_dbg(codec->dev,
+ "ADSP is about to power down. teardown/reset codec\n");
+ msm_anlg_cdc_device_down(codec);
+ break;
+ case AUDIO_NOTIFIER_SERVICE_UP:
+ if (initial_boot)
+ initial_boot = false;
+ dev_dbg(codec->dev,
+ "ADSP is about to power up. bring up codec\n");
+
+ if (!q6core_is_adsp_ready()) {
+ dev_dbg(codec->dev,
+ "ADSP isn't ready\n");
+ timeout = jiffies +
+ msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+ while (!(timedout = time_after(jiffies, timeout))) {
+ if (!q6core_is_adsp_ready()) {
+ dev_dbg(codec->dev,
+ "ADSP isn't ready\n");
+ } else {
+ dev_dbg(codec->dev,
+ "ADSP is ready\n");
+ adsp_ready = true;
+ goto powerup;
+ }
+ }
+ } else {
+ adsp_ready = true;
+ dev_dbg(codec->dev, "%s: DSP is ready\n", __func__);
+ }
+powerup:
+ if (adsp_ready)
+ msm_anlg_cdc_device_up(codec);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+int msm_anlg_cdc_hs_detect(struct snd_soc_codec *codec,
+ struct wcd_mbhc_config *mbhc_cfg)
+{
+ struct sdm660_cdc_priv *sdm660_cdc_priv =
+ snd_soc_codec_get_drvdata(codec);
+
+ return wcd_mbhc_start(&sdm660_cdc_priv->mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL(msm_anlg_cdc_hs_detect);
+
+void msm_anlg_cdc_hs_detect_exit(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc_priv =
+ snd_soc_codec_get_drvdata(codec);
+
+ wcd_mbhc_stop(&sdm660_cdc_priv->mbhc);
+}
+EXPORT_SYMBOL(msm_anlg_cdc_hs_detect_exit);
+
+void msm_anlg_cdc_update_int_spk_boost(bool enable)
+{
+ pr_debug("%s: enable = %d\n", __func__, enable);
+ spkr_boost_en = enable;
+}
+EXPORT_SYMBOL(msm_anlg_cdc_update_int_spk_boost);
+
+static void msm_anlg_cdc_set_micb_v(struct snd_soc_codec *codec)
+{
+
+ struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec);
+ struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data;
+ u8 reg_val;
+
+ reg_val = VOLTAGE_CONVERTER(pdata->micbias.cfilt1_mv, MICBIAS_MIN_VAL,
+ MICBIAS_STEP_SIZE);
+ dev_dbg(codec->dev, "cfilt1_mv %d reg_val %x\n",
+ (u32)pdata->micbias.cfilt1_mv, reg_val);
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_VAL,
+ 0xF8, (reg_val << 3));
+}
+
+static void msm_anlg_cdc_set_boost_v(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc_priv =
+ snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE,
+ 0x1F, sdm660_cdc_priv->boost_voltage);
+}
+
+static void msm_anlg_cdc_configure_cap(struct snd_soc_codec *codec,
+ bool micbias1, bool micbias2)
+{
+
+ struct msm_asoc_mach_data *pdata = NULL;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+
+ pr_debug("\n %s: micbias1 %x micbias2 = %d\n", __func__, micbias1,
+ micbias2);
+ if (micbias1 && micbias2) {
+ if ((pdata->micbias1_cap_mode
+ == MICBIAS_EXT_BYP_CAP) ||
+ (pdata->micbias2_cap_mode
+ == MICBIAS_EXT_BYP_CAP))
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_1_EN,
+ 0x40, (MICBIAS_EXT_BYP_CAP << 6));
+ else
+ snd_soc_update_bits(codec,
+ MSM89XX_PMIC_ANALOG_MICB_1_EN,
+ 0x40, (MICBIAS_NO_EXT_BYP_CAP << 6));
+ } else if (micbias2) {
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_EN,
+ 0x40, (pdata->micbias2_cap_mode << 6));
+ } else if (micbias1) {
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_EN,
+ 0x40, (pdata->micbias1_cap_mode << 6));
+ } else {
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_EN,
+ 0x40, 0x00);
+ }
+}
+
+static ssize_t msm_anlg_codec_version_read(struct snd_info_entry *entry,
+ void *file_private_data,
+ struct file *file,
+ char __user *buf, size_t count,
+ loff_t pos)
+{
+ struct sdm660_cdc_priv *sdm660_cdc_priv;
+ char buffer[MSM_ANLG_CDC_VERSION_ENTRY_SIZE];
+ int len = 0;
+
+ sdm660_cdc_priv = (struct sdm660_cdc_priv *) entry->private_data;
+ if (!sdm660_cdc_priv) {
+ pr_err("%s: sdm660_cdc_priv is null\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (get_codec_version(sdm660_cdc_priv)) {
+ case DRAX_CDC:
+ len = snprintf(buffer, sizeof(buffer), "DRAX-CDC_1_0\n");
+ break;
+ default:
+ len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n");
+ }
+
+ return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static struct snd_info_entry_ops msm_anlg_codec_info_ops = {
+ .read = msm_anlg_codec_version_read,
+};
+
+/*
+ * msm_anlg_codec_info_create_codec_entry - creates pmic_analog module
+ * @codec_root: The parent directory
+ * @codec: Codec instance
+ *
+ * Creates pmic_analog module and version entry under the given
+ * parent directory.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int msm_anlg_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+ struct snd_soc_codec *codec)
+{
+ struct snd_info_entry *version_entry;
+ struct sdm660_cdc_priv *sdm660_cdc_priv;
+ struct snd_soc_card *card;
+ int ret;
+
+ if (!codec_root || !codec)
+ return -EINVAL;
+
+ sdm660_cdc_priv = snd_soc_codec_get_drvdata(codec);
+ card = codec->component.card;
+ sdm660_cdc_priv->entry = snd_register_module_info(codec_root->module,
+ "spmi0-03",
+ codec_root);
+ if (!sdm660_cdc_priv->entry) {
+ dev_dbg(codec->dev, "%s: failed to create pmic_analog entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry = snd_info_create_card_entry(card->snd_card,
+ "version",
+ sdm660_cdc_priv->entry);
+ if (!version_entry) {
+ dev_dbg(codec->dev, "%s: failed to create pmic_analog version entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry->private_data = sdm660_cdc_priv;
+ version_entry->size = MSM_ANLG_CDC_VERSION_ENTRY_SIZE;
+ version_entry->content = SNDRV_INFO_CONTENT_DATA;
+ version_entry->c.ops = &msm_anlg_codec_info_ops;
+
+ if (snd_info_register(version_entry) < 0) {
+ snd_info_free_entry(version_entry);
+ return -ENOMEM;
+ }
+ sdm660_cdc_priv->version_entry = version_entry;
+
+ sdm660_cdc_priv->audio_ssr_nb.notifier_call =
+ sdm660_cdc_notifier_service_cb;
+ ret = audio_notifier_register("pmic_analog_cdc",
+ AUDIO_NOTIFIER_ADSP_DOMAIN,
+ &sdm660_cdc_priv->audio_ssr_nb);
+ if (ret < 0) {
+ pr_err("%s: Audio notifier register failed ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(msm_anlg_codec_info_create_codec_entry);
+
+static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ int ret;
+
+ sdm660_cdc = dev_get_drvdata(codec->dev);
+ sdm660_cdc->codec = codec;
+
+ /* codec resmgr module init */
+ sdm660_cdc->spkdrv_reg =
+ msm_anlg_cdc_find_regulator(sdm660_cdc,
+ MSM89XX_VDD_SPKDRV_NAME);
+ sdm660_cdc->pmic_rev =
+ snd_soc_read(codec,
+ MSM89XX_PMIC_DIGITAL_REVISION1);
+ sdm660_cdc->codec_version =
+ snd_soc_read(codec,
+ MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE);
+ sdm660_cdc->analog_major_rev =
+ snd_soc_read(codec,
+ MSM89XX_PMIC_ANALOG_REVISION4);
+
+ if (sdm660_cdc->codec_version == CONGA) {
+ dev_dbg(codec->dev, "%s :Conga REV: %d\n", __func__,
+ sdm660_cdc->codec_version);
+ sdm660_cdc->ext_spk_boost_set = true;
+ } else {
+ dev_dbg(codec->dev, "%s :PMIC REV: %d\n", __func__,
+ sdm660_cdc->pmic_rev);
+ if (sdm660_cdc->pmic_rev == TOMBAK_1_0 &&
+ sdm660_cdc->codec_version == CAJON_2_0) {
+ if (sdm660_cdc->analog_major_rev == 0x02) {
+ sdm660_cdc->codec_version = DRAX_CDC;
+ dev_dbg(codec->dev,
+ "%s : Drax codec detected\n", __func__);
+ } else {
+ sdm660_cdc->codec_version = DIANGU;
+ dev_dbg(codec->dev, "%s : Diangu detected\n",
+ __func__);
+ }
+ } else if (sdm660_cdc->pmic_rev == TOMBAK_1_0 &&
+ (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_NCP_FBCTRL)
+ & 0x80)) {
+ sdm660_cdc->codec_version = CAJON;
+ dev_dbg(codec->dev, "%s : Cajon detected\n", __func__);
+ } else if (sdm660_cdc->pmic_rev == TOMBAK_2_0 &&
+ (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_NCP_FBCTRL)
+ & 0x80)) {
+ sdm660_cdc->codec_version = CAJON_2_0;
+ dev_dbg(codec->dev, "%s : Cajon 2.0 detected\n",
+ __func__);
+ }
+ }
+ /*
+ * set to default boost option BOOST_SWITCH, user mixer path can change
+ * it to BOOST_ALWAYS or BOOST_BYPASS based on solution chosen.
+ */
+ sdm660_cdc->boost_option = BOOST_SWITCH;
+ sdm660_cdc->hph_mode = NORMAL_MODE;
+
+ msm_anlg_cdc_dt_parse_boost_info(codec);
+ msm_anlg_cdc_set_boost_v(codec);
+
+ snd_soc_add_codec_controls(codec, impedance_detect_controls,
+ ARRAY_SIZE(impedance_detect_controls));
+ snd_soc_add_codec_controls(codec, hph_type_detect_controls,
+ ARRAY_SIZE(hph_type_detect_controls));
+
+ msm_anlg_cdc_bringup(codec);
+ msm_anlg_cdc_codec_init_reg(codec);
+ msm_anlg_cdc_update_reg_defaults(codec);
+
+ wcd9xxx_spmi_set_codec(codec);
+
+ msm_anlg_cdc_update_micbias_regulator(
+ sdm660_cdc,
+ on_demand_supply_name[ON_DEMAND_MICBIAS],
+ &sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS]);
+ atomic_set(&sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS].ref,
+ 0);
+
+ sdm660_cdc->fw_data = devm_kzalloc(codec->dev,
+ sizeof(*(sdm660_cdc->fw_data)),
+ GFP_KERNEL);
+ if (!sdm660_cdc->fw_data)
+ return -ENOMEM;
+
+ set_bit(WCD9XXX_MBHC_CAL, sdm660_cdc->fw_data->cal_bit);
+ ret = wcd_cal_create_hwdep(sdm660_cdc->fw_data,
+ WCD9XXX_CODEC_HWDEP_NODE, codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ wcd_mbhc_init(&sdm660_cdc->mbhc, codec, &mbhc_cb, &intr_ids,
+ wcd_mbhc_registers, true);
+
+ sdm660_cdc->int_mclk0_enabled = false;
+ /*Update speaker boost configuration*/
+ sdm660_cdc->spk_boost_set = spkr_boost_en;
+ pr_debug("%s: speaker boost configured = %d\n",
+ __func__, sdm660_cdc->spk_boost_set);
+
+ /* Set initial MICBIAS voltage level */
+ msm_anlg_cdc_set_micb_v(codec);
+
+ /* Set initial cap mode */
+ msm_anlg_cdc_configure_cap(codec, false, false);
+
+ snd_soc_dapm_ignore_suspend(dapm, "PDM Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "PDM Capture");
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static int msm_anlg_cdc_soc_remove(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc_priv =
+ dev_get_drvdata(codec->dev);
+
+ sdm660_cdc_priv->spkdrv_reg = NULL;
+ sdm660_cdc_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL;
+ atomic_set(&sdm660_cdc_priv->on_demand_list[ON_DEMAND_MICBIAS].ref,
+ 0);
+ wcd_mbhc_deinit(&sdm660_cdc_priv->mbhc);
+
+ return 0;
+}
+
+static int msm_anlg_cdc_enable_static_supplies_to_optimum(
+ struct sdm660_cdc_priv *sdm660_cdc,
+ struct sdm660_cdc_pdata *pdata)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < sdm660_cdc->num_of_supplies; i++) {
+ if (pdata->regulator[i].ondemand)
+ continue;
+ if (regulator_count_voltages(
+ sdm660_cdc->supplies[i].consumer) <= 0)
+ continue;
+
+ ret = regulator_set_voltage(
+ sdm660_cdc->supplies[i].consumer,
+ pdata->regulator[i].min_uv,
+ pdata->regulator[i].max_uv);
+ if (ret) {
+ dev_err(sdm660_cdc->dev,
+ "Setting volt failed for regulator %s err %d\n",
+ sdm660_cdc->supplies[i].supply, ret);
+ }
+
+ ret = regulator_set_load(sdm660_cdc->supplies[i].consumer,
+ pdata->regulator[i].optimum_ua);
+ dev_dbg(sdm660_cdc->dev, "Regulator %s set optimum mode\n",
+ sdm660_cdc->supplies[i].supply);
+ }
+
+ return ret;
+}
+
+static int msm_anlg_cdc_disable_static_supplies_to_optimum(
+ struct sdm660_cdc_priv *sdm660_cdc,
+ struct sdm660_cdc_pdata *pdata)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < sdm660_cdc->num_of_supplies; i++) {
+ if (pdata->regulator[i].ondemand)
+ continue;
+ if (regulator_count_voltages(
+ sdm660_cdc->supplies[i].consumer) <= 0)
+ continue;
+ regulator_set_voltage(sdm660_cdc->supplies[i].consumer, 0,
+ pdata->regulator[i].max_uv);
+ regulator_set_load(sdm660_cdc->supplies[i].consumer, 0);
+ dev_dbg(sdm660_cdc->dev, "Regulator %s set optimum mode\n",
+ sdm660_cdc->supplies[i].supply);
+ }
+
+ return ret;
+}
+
+static int msm_anlg_cdc_suspend(struct snd_soc_codec *codec)
+{
+ struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec);
+ struct sdm660_cdc_pdata *sdm660_cdc_pdata =
+ sdm660_cdc->dev->platform_data;
+
+ msm_anlg_cdc_disable_static_supplies_to_optimum(sdm660_cdc,
+ sdm660_cdc_pdata);
+ return 0;
+}
+
+static int msm_anlg_cdc_resume(struct snd_soc_codec *codec)
+{
+ struct msm_asoc_mach_data *pdata = NULL;
+ struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec);
+ struct sdm660_cdc_pdata *sdm660_cdc_pdata =
+ sdm660_cdc->dev->platform_data;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+ msm_anlg_cdc_enable_static_supplies_to_optimum(sdm660_cdc,
+ sdm660_cdc_pdata);
+ return 0;
+}
+
+static struct regmap *msm_anlg_get_regmap(struct device *dev)
+{
+ return dev_get_regmap(dev->parent, NULL);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_sdm660_cdc = {
+ .probe = msm_anlg_cdc_soc_probe,
+ .remove = msm_anlg_cdc_soc_remove,
+ .suspend = msm_anlg_cdc_suspend,
+ .resume = msm_anlg_cdc_resume,
+ .reg_word_size = 1,
+ .controls = msm_anlg_cdc_snd_controls,
+ .num_controls = ARRAY_SIZE(msm_anlg_cdc_snd_controls),
+ .dapm_widgets = msm_anlg_cdc_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(msm_anlg_cdc_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .get_regmap = msm_anlg_get_regmap,
+};
+
+static int msm_anlg_cdc_init_supplies(struct sdm660_cdc_priv *sdm660_cdc,
+ struct sdm660_cdc_pdata *pdata)
+{
+ int ret;
+ int i;
+
+ sdm660_cdc->supplies = devm_kzalloc(sdm660_cdc->dev,
+ sizeof(struct regulator_bulk_data) *
+ ARRAY_SIZE(pdata->regulator),
+ GFP_KERNEL);
+ if (!sdm660_cdc->supplies) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ sdm660_cdc->num_of_supplies = 0;
+ if (ARRAY_SIZE(pdata->regulator) > MAX_REGULATOR) {
+ dev_err(sdm660_cdc->dev, "%s: Array Size out of bound\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+ if (pdata->regulator[i].name) {
+ sdm660_cdc->supplies[i].supply =
+ pdata->regulator[i].name;
+ sdm660_cdc->num_of_supplies++;
+ }
+ }
+
+ ret = devm_regulator_bulk_get(sdm660_cdc->dev,
+ sdm660_cdc->num_of_supplies,
+ sdm660_cdc->supplies);
+ if (ret != 0) {
+ dev_err(sdm660_cdc->dev,
+ "Failed to get supplies: err = %d\n",
+ ret);
+ goto err_supplies;
+ }
+
+ for (i = 0; i < sdm660_cdc->num_of_supplies; i++) {
+ if (regulator_count_voltages(
+ sdm660_cdc->supplies[i].consumer) <= 0)
+ continue;
+ if (pdata->regulator[i].ondemand) {
+ ret = regulator_set_voltage(
+ sdm660_cdc->supplies[i].consumer,
+ 0, pdata->regulator[i].max_uv);
+ if (ret) {
+ dev_err(sdm660_cdc->dev,
+ "Setting regulator voltage failed for regulator %s err = %d\n",
+ sdm660_cdc->supplies[i].supply, ret);
+ goto err_supplies;
+ }
+ ret = regulator_set_load(
+ sdm660_cdc->supplies[i].consumer, 0);
+ if (ret < 0) {
+ dev_err(sdm660_cdc->dev,
+ "Setting regulator optimum mode failed for regulator %s err = %d\n",
+ sdm660_cdc->supplies[i].supply, ret);
+ goto err_supplies;
+ } else {
+ ret = 0;
+ continue;
+ }
+ }
+ ret = regulator_set_voltage(sdm660_cdc->supplies[i].consumer,
+ pdata->regulator[i].min_uv,
+ pdata->regulator[i].max_uv);
+ if (ret) {
+ dev_err(sdm660_cdc->dev,
+ "Setting regulator voltage failed for regulator %s err = %d\n",
+ sdm660_cdc->supplies[i].supply, ret);
+ goto err_supplies;
+ }
+ ret = regulator_set_load(sdm660_cdc->supplies[i].consumer,
+ pdata->regulator[i].optimum_ua);
+ if (ret < 0) {
+ dev_err(sdm660_cdc->dev,
+ "Setting regulator optimum mode failed for regulator %s err = %d\n",
+ sdm660_cdc->supplies[i].supply, ret);
+ goto err_supplies;
+ } else {
+ ret = 0;
+ }
+ }
+
+ return ret;
+
+err_supplies:
+ kfree(sdm660_cdc->supplies);
+err:
+ return ret;
+}
+
+static int msm_anlg_cdc_enable_static_supplies(
+ struct sdm660_cdc_priv *sdm660_cdc,
+ struct sdm660_cdc_pdata *pdata)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < sdm660_cdc->num_of_supplies; i++) {
+ if (pdata->regulator[i].ondemand)
+ continue;
+ ret = regulator_enable(sdm660_cdc->supplies[i].consumer);
+ if (ret) {
+ dev_err(sdm660_cdc->dev, "Failed to enable %s\n",
+ sdm660_cdc->supplies[i].supply);
+ break;
+ }
+ dev_dbg(sdm660_cdc->dev, "Enabled regulator %s\n",
+ sdm660_cdc->supplies[i].supply);
+ }
+
+ while (ret && --i)
+ if (!pdata->regulator[i].ondemand)
+ regulator_disable(sdm660_cdc->supplies[i].consumer);
+ return ret;
+}
+
+static void msm_anlg_cdc_disable_supplies(struct sdm660_cdc_priv *sdm660_cdc,
+ struct sdm660_cdc_pdata *pdata)
+{
+ int i;
+
+ regulator_bulk_disable(sdm660_cdc->num_of_supplies,
+ sdm660_cdc->supplies);
+ for (i = 0; i < sdm660_cdc->num_of_supplies; i++) {
+ if (regulator_count_voltages(
+ sdm660_cdc->supplies[i].consumer) <= 0)
+ continue;
+ regulator_set_voltage(sdm660_cdc->supplies[i].consumer, 0,
+ pdata->regulator[i].max_uv);
+ regulator_set_load(sdm660_cdc->supplies[i].consumer, 0);
+ }
+ regulator_bulk_free(sdm660_cdc->num_of_supplies,
+ sdm660_cdc->supplies);
+ kfree(sdm660_cdc->supplies);
+}
+
+static const struct of_device_id sdm660_codec_of_match[] = {
+ { .compatible = "qcom,pmic-analog-codec", },
+ {},
+};
+
+static void msm_anlg_add_child_devices(struct work_struct *work)
+{
+ struct sdm660_cdc_priv *pdata;
+ struct platform_device *pdev;
+ struct device_node *node;
+ struct msm_dig_ctrl_data *dig_ctrl_data = NULL, *temp;
+ int ret, ctrl_num = 0;
+ struct msm_dig_ctrl_platform_data *platdata;
+ char plat_dev_name[MSM_DIG_CDC_STRING_LEN];
+
+ pdata = container_of(work, struct sdm660_cdc_priv,
+ msm_anlg_add_child_devices_work);
+ if (!pdata) {
+ pr_err("%s: Memory for pdata does not exist\n",
+ __func__);
+ return;
+ }
+ if (!pdata->dev->of_node) {
+ dev_err(pdata->dev,
+ "%s: DT node for pdata does not exist\n", __func__);
+ return;
+ }
+
+ platdata = &pdata->dig_plat_data;
+
+ for_each_child_of_node(pdata->dev->of_node, node) {
+ if (!strcmp(node->name, "msm-dig-codec"))
+ strlcpy(plat_dev_name, "msm_digital_codec",
+ (MSM_DIG_CDC_STRING_LEN - 1));
+ else
+ continue;
+
+ pdev = platform_device_alloc(plat_dev_name, -1);
+ if (!pdev) {
+ dev_err(pdata->dev, "%s: pdev memory alloc failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err;
+ }
+ pdev->dev.parent = pdata->dev;
+ pdev->dev.of_node = node;
+
+ if (!strcmp(node->name, "msm-dig-codec")) {
+ ret = platform_device_add_data(pdev, platdata,
+ sizeof(*platdata));
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: cannot add plat data ctrl:%d\n",
+ __func__, ctrl_num);
+ goto fail_pdev_add;
+ }
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: Cannot add platform device\n",
+ __func__);
+ goto fail_pdev_add;
+ }
+
+ if (!strcmp(node->name, "msm-dig-codec")) {
+ temp = krealloc(dig_ctrl_data,
+ (ctrl_num + 1) * sizeof(
+ struct msm_dig_ctrl_data),
+ GFP_KERNEL);
+ if (!temp) {
+ dev_err(&pdev->dev, "out of memory\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dig_ctrl_data = temp;
+ dig_ctrl_data[ctrl_num].dig_pdev = pdev;
+ ctrl_num++;
+ dev_dbg(&pdev->dev,
+ "%s: Added digital codec device(s)\n",
+ __func__);
+ pdata->dig_ctrl_data = dig_ctrl_data;
+ }
+ }
+
+ return;
+fail_pdev_add:
+ platform_device_put(pdev);
+err:
+ return;
+}
+
+static int msm_anlg_cdc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct sdm660_cdc_priv *sdm660_cdc = NULL;
+ struct sdm660_cdc_pdata *pdata;
+ int adsp_state;
+
+ adsp_state = apr_get_subsys_state();
+ if (adsp_state != APR_SUBSYS_LOADED ||
+ !q6core_is_adsp_ready()) {
+ dev_err(&pdev->dev, "Adsp is not loaded yet %d\n",
+ adsp_state);
+ return -EPROBE_DEFER;
+ }
+ device_init_wakeup(&pdev->dev, true);
+
+ if (pdev->dev.of_node) {
+ dev_dbg(&pdev->dev, "%s:Platform data from device tree\n",
+ __func__);
+ pdata = msm_anlg_cdc_populate_dt_pdata(&pdev->dev);
+ pdev->dev.platform_data = pdata;
+ } else {
+ dev_dbg(&pdev->dev, "%s:Platform data from board file\n",
+ __func__);
+ pdata = pdev->dev.platform_data;
+ }
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "%s:Platform data failed to populate\n",
+ __func__);
+ goto rtn;
+ }
+ sdm660_cdc = devm_kzalloc(&pdev->dev, sizeof(struct sdm660_cdc_priv),
+ GFP_KERNEL);
+ if (sdm660_cdc == NULL) {
+ ret = -ENOMEM;
+ goto rtn;
+ }
+
+ sdm660_cdc->dev = &pdev->dev;
+ ret = msm_anlg_cdc_init_supplies(sdm660_cdc, pdata);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: Fail to enable Codec supplies\n",
+ __func__);
+ goto rtn;
+ }
+ ret = msm_anlg_cdc_enable_static_supplies(sdm660_cdc, pdata);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: Fail to enable Codec pre-reset supplies\n",
+ __func__);
+ goto rtn;
+ }
+ /* Allow supplies to be ready */
+ usleep_range(5, 6);
+
+ wcd9xxx_spmi_set_dev(pdev, 0);
+ wcd9xxx_spmi_set_dev(pdev, 1);
+ if (wcd9xxx_spmi_irq_init()) {
+ dev_err(&pdev->dev,
+ "%s: irq initialization failed\n", __func__);
+ } else {
+ dev_dbg(&pdev->dev,
+ "%s: irq initialization passed\n", __func__);
+ }
+ dev_set_drvdata(&pdev->dev, sdm660_cdc);
+
+ ret = snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_sdm660_cdc,
+ msm_anlg_cdc_i2s_dai,
+ ARRAY_SIZE(msm_anlg_cdc_i2s_dai));
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s:snd_soc_register_codec failed with error %d\n",
+ __func__, ret);
+ goto err_supplies;
+ }
+ BLOCKING_INIT_NOTIFIER_HEAD(&sdm660_cdc->notifier);
+ BLOCKING_INIT_NOTIFIER_HEAD(&sdm660_cdc->notifier_mbhc);
+
+ sdm660_cdc->dig_plat_data.handle = (void *) sdm660_cdc;
+ sdm660_cdc->dig_plat_data.set_compander_mode = set_compander_mode;
+ sdm660_cdc->dig_plat_data.update_clkdiv = update_clkdiv;
+ sdm660_cdc->dig_plat_data.get_cdc_version = get_cdc_version;
+ sdm660_cdc->dig_plat_data.register_notifier =
+ msm_anlg_cdc_dig_register_notifier;
+ INIT_WORK(&sdm660_cdc->msm_anlg_add_child_devices_work,
+ msm_anlg_add_child_devices);
+ schedule_work(&sdm660_cdc->msm_anlg_add_child_devices_work);
+
+ return ret;
+err_supplies:
+ msm_anlg_cdc_disable_supplies(sdm660_cdc, pdata);
+rtn:
+ return ret;
+}
+
+static int msm_anlg_cdc_remove(struct platform_device *pdev)
+{
+ struct sdm660_cdc_priv *sdm660_cdc = dev_get_drvdata(&pdev->dev);
+ struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data;
+
+ snd_soc_unregister_codec(&pdev->dev);
+ msm_anlg_cdc_disable_supplies(sdm660_cdc, pdata);
+ return 0;
+}
+
+static struct platform_driver msm_anlg_codec_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(sdm660_codec_of_match)
+ },
+ .probe = msm_anlg_cdc_probe,
+ .remove = msm_anlg_cdc_remove,
+};
+module_platform_driver(msm_anlg_codec_driver);
+
+MODULE_DESCRIPTION("MSM Audio Analog codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h
new file mode 100644
index 000000000000..d07d1bee4d6b
--- /dev/null
+++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h
@@ -0,0 +1,241 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef MSM_ANALOG_CDC_H
+#define MSM_ANALOG_CDC_H
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/q6afe-v2.h>
+#include "../wcd-mbhc-v2.h"
+#include "../wcdcal-hwdep.h"
+#include "sdm660-cdc-registers.h"
+
+#define MICBIAS_EXT_BYP_CAP 0x00
+#define MICBIAS_NO_EXT_BYP_CAP 0x01
+
+#define MSM89XX_NUM_IRQ_REGS 2
+#define MAX_REGULATOR 7
+#define MSM89XX_REG_VAL(reg, val) {reg, 0, val}
+
+#define MSM89XX_VDD_SPKDRV_NAME "cdc-vdd-spkdrv"
+
+#define DEFAULT_MULTIPLIER 800
+#define DEFAULT_GAIN 9
+#define DEFAULT_OFFSET 100
+
+extern const u8 msm89xx_pmic_cdc_reg_readable[MSM89XX_PMIC_CDC_CACHE_SIZE];
+extern const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE];
+extern struct regmap_config msm89xx_cdc_core_regmap_config;
+extern struct regmap_config msm89xx_pmic_cdc_regmap_config;
+
+enum wcd_curr_ref {
+ I_h4_UA = 0,
+ I_pt5_UA,
+ I_14_UA,
+ I_l4_UA,
+ I_1_UA,
+};
+
+enum wcd_mbhc_imp_det_pin {
+ WCD_MBHC_DET_NONE = 0,
+ WCD_MBHC_DET_HPHL,
+ WCD_MBHC_DET_HPHR,
+ WCD_MBHC_DET_BOTH,
+};
+
+
+/* Each micbias can be assigned to one of three cfilters
+ * Vbatt_min >= .15V + ldoh_v
+ * ldoh_v >= .15v + cfiltx_mv
+ * If ldoh_v = 1.95 160 mv < cfiltx_mv < 1800 mv
+ * If ldoh_v = 2.35 200 mv < cfiltx_mv < 2200 mv
+ * If ldoh_v = 2.75 240 mv < cfiltx_mv < 2600 mv
+ * If ldoh_v = 2.85 250 mv < cfiltx_mv < 2700 mv
+ */
+
+struct wcd_micbias_setting {
+ u8 ldoh_v;
+ u32 cfilt1_mv; /* in mv */
+ u32 cfilt2_mv; /* in mv */
+ u32 cfilt3_mv; /* in mv */
+ /* Different WCD9xxx series codecs may not
+ * have 4 mic biases. If a codec has fewer
+ * mic biases, some of these properties will
+ * not be used.
+ */
+ u8 bias1_cfilt_sel;
+ u8 bias2_cfilt_sel;
+ u8 bias3_cfilt_sel;
+ u8 bias4_cfilt_sel;
+ u8 bias1_cap_mode;
+ u8 bias2_cap_mode;
+ u8 bias3_cap_mode;
+ u8 bias4_cap_mode;
+ bool bias2_is_headset_only;
+};
+
+enum sdm660_cdc_pid_current {
+ MSM89XX_PID_MIC_2P5_UA,
+ MSM89XX_PID_MIC_5_UA,
+ MSM89XX_PID_MIC_10_UA,
+ MSM89XX_PID_MIC_20_UA,
+};
+
+struct sdm660_cdc_reg_mask_val {
+ u16 reg;
+ u8 mask;
+ u8 val;
+};
+
+enum {
+ /* INTR_REG 0 - Digital Periph */
+ MSM89XX_IRQ_SPKR_CNP = 0,
+ MSM89XX_IRQ_SPKR_CLIP,
+ MSM89XX_IRQ_SPKR_OCP,
+ MSM89XX_IRQ_MBHC_INSREM_DET1,
+ MSM89XX_IRQ_MBHC_RELEASE,
+ MSM89XX_IRQ_MBHC_PRESS,
+ MSM89XX_IRQ_MBHC_INSREM_DET,
+ MSM89XX_IRQ_MBHC_HS_DET,
+ /* INTR_REG 1 - Analog Periph */
+ MSM89XX_IRQ_EAR_OCP,
+ MSM89XX_IRQ_HPHR_OCP,
+ MSM89XX_IRQ_HPHL_OCP,
+ MSM89XX_IRQ_EAR_CNP,
+ MSM89XX_IRQ_HPHR_CNP,
+ MSM89XX_IRQ_HPHL_CNP,
+ MSM89XX_NUM_IRQS,
+};
+
+enum {
+ ON_DEMAND_MICBIAS = 0,
+ ON_DEMAND_SPKDRV,
+ ON_DEMAND_SUPPLIES_MAX,
+};
+
+/*
+ * The delay list is per codec HW specification.
+ * Please add delay in the list in the future instead
+ * of magic number
+ */
+enum {
+ CODEC_DELAY_1_MS = 1000,
+ CODEC_DELAY_1_1_MS = 1100,
+};
+
+struct sdm660_cdc_regulator {
+ const char *name;
+ int min_uv;
+ int max_uv;
+ int optimum_ua;
+ bool ondemand;
+ struct regulator *regulator;
+};
+
+struct on_demand_supply {
+ struct regulator *supply;
+ atomic_t ref;
+ int min_uv;
+ int max_uv;
+ int optimum_ua;
+};
+
+struct wcd_imped_i_ref {
+ enum wcd_curr_ref curr_ref;
+ int min_val;
+ int multiplier;
+ int gain_adj;
+ int offset;
+};
+
+enum sdm660_cdc_micbias_num {
+ MSM89XX_MICBIAS1 = 0,
+};
+
+/* Hold instance to digital codec platform device */
+struct msm_dig_ctrl_data {
+ struct platform_device *dig_pdev;
+};
+
+struct msm_dig_ctrl_platform_data {
+ void *handle;
+ void (*set_compander_mode)(void *handle, int val);
+ void (*update_clkdiv)(void *handle, int val);
+ int (*get_cdc_version)(void *handle);
+ int (*register_notifier)(void *handle,
+ struct notifier_block *nblock,
+ bool enable);
+};
+
+struct sdm660_cdc_priv {
+ struct device *dev;
+ u32 num_of_supplies;
+ struct regulator_bulk_data *supplies;
+ struct snd_soc_codec *codec;
+ struct work_struct msm_anlg_add_child_devices_work;
+ struct msm_dig_ctrl_platform_data dig_plat_data;
+ /* digital codec data structure */
+ struct msm_dig_ctrl_data *dig_ctrl_data;
+ struct blocking_notifier_head notifier;
+ u16 pmic_rev;
+ u16 codec_version;
+ u16 analog_major_rev;
+ u32 boost_voltage;
+ u32 adc_count;
+ u32 rx_bias_count;
+ bool int_mclk0_enabled;
+ u16 boost_option;
+ /* mode to select hd2 */
+ u32 hph_mode;
+ /* compander used for each rx chain */
+ bool spk_boost_set;
+ bool ear_pa_boost_set;
+ bool ext_spk_boost_set;
+ struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX];
+ struct regulator *spkdrv_reg;
+ struct blocking_notifier_head notifier_mbhc;
+ /* mbhc module */
+ struct wcd_mbhc mbhc;
+ /* cal info for codec */
+ struct fw_info *fw_data;
+ struct notifier_block audio_ssr_nb;
+ int (*codec_spk_ext_pa_cb)(struct snd_soc_codec *codec, int enable);
+ unsigned long status_mask;
+ struct wcd_imped_i_ref imped_i_ref;
+ enum wcd_mbhc_imp_det_pin imped_det_pin;
+ /* Entry for version info */
+ struct snd_info_entry *entry;
+ struct snd_info_entry *version_entry;
+};
+
+struct sdm660_cdc_pdata {
+ struct wcd_micbias_setting micbias;
+ struct sdm660_cdc_regulator regulator[MAX_REGULATOR];
+};
+
+
+extern int msm_anlg_cdc_mclk_enable(struct snd_soc_codec *codec,
+ int mclk_enable, bool dapm);
+
+extern int msm_anlg_cdc_hs_detect(struct snd_soc_codec *codec,
+ struct wcd_mbhc_config *mbhc_cfg);
+
+extern void msm_anlg_cdc_hs_detect_exit(struct snd_soc_codec *codec);
+
+extern void sdm660_cdc_update_int_spk_boost(bool enable);
+
+extern void msm_anlg_cdc_spk_ext_pa_cb(
+ int (*codec_spk_ext_pa)(struct snd_soc_codec *codec,
+ int enable), struct snd_soc_codec *codec);
+int msm_anlg_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+ struct snd_soc_codec *codec);
+#endif
diff --git a/sound/soc/codecs/sdm660_cdc/msm-cdc-common.h b/sound/soc/codecs/sdm660_cdc/msm-cdc-common.h
new file mode 100644
index 000000000000..5d5cee124bca
--- /dev/null
+++ b/sound/soc/codecs/sdm660_cdc/msm-cdc-common.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include "sdm660-cdc-registers.h"
+
+extern struct reg_default
+ msm89xx_cdc_core_defaults[MSM89XX_CDC_CORE_CACHE_SIZE];
+extern struct reg_default
+ msm89xx_pmic_cdc_defaults[MSM89XX_PMIC_CDC_CACHE_SIZE];
+
+bool msm89xx_cdc_core_readable_reg(struct device *dev, unsigned int reg);
+bool msm89xx_cdc_core_writeable_reg(struct device *dev, unsigned int reg);
+bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg);
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ AIF2_VIFEED,
+ AIF3_SVA,
+ NUM_CODEC_DAIS,
+};
+
+enum codec_versions {
+ TOMBAK_1_0,
+ TOMBAK_2_0,
+ CONGA,
+ CAJON,
+ CAJON_2_0,
+ DIANGU,
+ DRAX_CDC,
+ UNSUPPORTED,
+};
+
+/* Support different hph modes */
+enum {
+ NORMAL_MODE = 0,
+ HD2_MODE,
+};
+
+enum dig_cdc_notify_event {
+ DIG_CDC_EVENT_INVALID,
+ DIG_CDC_EVENT_CLK_ON,
+ DIG_CDC_EVENT_CLK_OFF,
+ DIG_CDC_EVENT_RX1_MUTE_ON,
+ DIG_CDC_EVENT_RX1_MUTE_OFF,
+ DIG_CDC_EVENT_RX2_MUTE_ON,
+ DIG_CDC_EVENT_RX2_MUTE_OFF,
+ DIG_CDC_EVENT_RX3_MUTE_ON,
+ DIG_CDC_EVENT_RX3_MUTE_OFF,
+ DIG_CDC_EVENT_PRE_RX1_INT_ON,
+ DIG_CDC_EVENT_PRE_RX2_INT_ON,
+ DIG_CDC_EVENT_POST_RX1_INT_OFF,
+ DIG_CDC_EVENT_POST_RX2_INT_OFF,
+ DIG_CDC_EVENT_SSR_DOWN,
+ DIG_CDC_EVENT_SSR_UP,
+ DIG_CDC_EVENT_LAST,
+};
diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
new file mode 100644
index 000000000000..e5f079978398
--- /dev/null
+++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
@@ -0,0 +1,2237 @@
+/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/workqueue.h>
+#include <linux/regmap.h>
+#include <sound/q6afe-v2.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "sdm660-cdc-registers.h"
+#include "msm-digital-cdc.h"
+#include "msm-cdc-common.h"
+#include "../../msm/sdm660-common.h"
+#include "../../../../drivers/base/regmap/internal.h"
+
+#define DRV_NAME "msm_digital_codec"
+#define MCLK_RATE_9P6MHZ 9600000
+#define MCLK_RATE_12P288MHZ 12288000
+#define TX_MUX_CTL_CUT_OFF_FREQ_MASK 0x30
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+
+#define DEC_SVA 5
+#define MSM_DIG_CDC_VERSION_ENTRY_SIZE 32
+#define ADSP_UP 1
+
+static unsigned long rx_digital_gain_reg[] = {
+ MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL,
+ MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL,
+ MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL,
+};
+
+static unsigned long tx_digital_gain_reg[] = {
+ MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN,
+ MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN,
+ MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN,
+ MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN,
+ MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN,
+};
+
+#define SDM660_TX_UNMUTE_DELAY_MS 40
+static int tx_unmute_delay = SDM660_TX_UNMUTE_DELAY_MS;
+module_param(tx_unmute_delay, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path");
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+
+struct snd_soc_codec *registered_digcodec;
+struct hpf_work tx_hpf_work[NUM_DECIMATORS];
+
+/* Codec supports 2 IIR filters */
+enum {
+ IIR1 = 0,
+ IIR2,
+ IIR_MAX,
+};
+
+static int msm_digcdc_clock_control(bool flag)
+{
+ int ret = -EINVAL;
+ struct msm_asoc_mach_data *pdata = NULL;
+ struct msm_dig_priv *msm_dig_cdc =
+ snd_soc_codec_get_drvdata(registered_digcodec);
+
+ pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card);
+
+ if (flag) {
+ mutex_lock(&pdata->cdc_int_mclk0_mutex);
+ if (atomic_read(&pdata->int_mclk0_enabled) == false) {
+ if (msm_dig_cdc->regmap->cache_only == true) {
+ if (test_bit(ADSP_UP,
+ &msm_dig_cdc->status_mask))
+ msm_dig_cdc->regmap->cache_only = false;
+ else
+ return ret;
+ }
+ if (pdata->native_clk_set)
+ pdata->digital_cdc_core_clk.clk_freq_in_hz =
+ NATIVE_MCLK_RATE;
+ else
+ pdata->digital_cdc_core_clk.clk_freq_in_hz =
+ DEFAULT_MCLK_RATE;
+ pdata->digital_cdc_core_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT0_MI2S_RX,
+ &pdata->digital_cdc_core_clk);
+ if (ret < 0) {
+ pr_err("%s:failed to enable the MCLK\n",
+ __func__);
+ /*
+ * Avoid access to lpass register
+ * as clock enable failed during SSR.
+ */
+ msm_dig_cdc->regmap->cache_only = true;
+ return ret;
+ }
+ pr_debug("enabled digital codec core clk\n");
+ atomic_set(&pdata->int_mclk0_enabled, true);
+ schedule_delayed_work(&pdata->disable_int_mclk0_work,
+ 50);
+ }
+ } else {
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+ dev_dbg(registered_digcodec->dev,
+ "disable MCLK, workq to disable set already\n");
+ }
+ return 0;
+}
+
+static void enable_digital_callback(void *flag)
+{
+ msm_digcdc_clock_control(true);
+}
+
+static void disable_digital_callback(void *flag)
+{
+ msm_digcdc_clock_control(false);
+ pr_debug("disable mclk happens in workq\n");
+}
+
+static int msm_dig_cdc_put_dec_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *w = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int dec_mux, decimator;
+ char *dec_name = NULL;
+ char *widget_name = NULL;
+ char *temp;
+ u16 tx_mux_ctl_reg;
+ u8 adc_dmic_sel = 0x0;
+ int ret = 0;
+ char *dec_num;
+
+ if (ucontrol->value.enumerated.item[0] > e->items) {
+ dev_err(codec->dev, "%s: Invalid enum value: %d\n",
+ __func__, ucontrol->value.enumerated.item[0]);
+ return -EINVAL;
+ }
+ dec_mux = ucontrol->value.enumerated.item[0];
+
+ widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+ if (!widget_name) {
+ dev_err(codec->dev, "%s: failed to copy string\n",
+ __func__);
+ return -ENOMEM;
+ }
+ temp = widget_name;
+
+ dec_name = strsep(&widget_name, " ");
+ widget_name = temp;
+ if (!dec_name) {
+ dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+ __func__, w->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dec_num = strpbrk(dec_name, "12345");
+ if (dec_num == NULL) {
+ dev_err(codec->dev, "%s: Invalid DEC selected\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = kstrtouint(dec_num, 10, &decimator);
+ if (ret < 0) {
+ dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+ __func__, dec_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dev_dbg(w->dapm->dev, "%s(): widget = %s decimator = %u dec_mux = %u\n"
+ , __func__, w->name, decimator, dec_mux);
+
+ switch (decimator) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ if ((dec_mux == 4) || (dec_mux == 5) ||
+ (dec_mux == 6) || (dec_mux == 7))
+ adc_dmic_sel = 0x1;
+ else
+ adc_dmic_sel = 0x0;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid Decimator = %u\n",
+ __func__, decimator);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ tx_mux_ctl_reg =
+ MSM89XX_CDC_CORE_TX1_MUX_CTL + 32 * (decimator - 1);
+
+ if (decimator == DEC_SVA)
+ tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX5_MUX_CTL;
+
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x1, adc_dmic_sel);
+
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+out:
+ kfree(widget_name);
+ return ret;
+}
+
+
+static int msm_dig_cdc_codec_config_compander(struct snd_soc_codec *codec,
+ int interp_n, int event)
+{
+ struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec);
+ int comp_ch_bits_set = 0x03;
+ int comp_ch_value;
+
+ dev_dbg(codec->dev, "%s: event %d shift %d, enabled %d\n",
+ __func__, event, interp_n,
+ dig_cdc->comp_enabled[interp_n]);
+
+ /* compander is invalid */
+ if (dig_cdc->comp_enabled[interp_n] != COMPANDER_1 &&
+ dig_cdc->comp_enabled[interp_n]) {
+ dev_dbg(codec->dev, "%s: Invalid compander %d\n", __func__,
+ dig_cdc->comp_enabled[interp_n]);
+ return 0;
+ }
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* compander is not enabled */
+ if (!dig_cdc->comp_enabled[interp_n]) {
+ dig_cdc->set_compander_mode(dig_cdc->handle, 0x00);
+ return 0;
+ };
+ comp_ch_value = snd_soc_read(codec,
+ MSM89XX_CDC_CORE_COMP0_B1_CTL);
+ if (interp_n == 0) {
+ if ((comp_ch_value & 0x02) == 0x02) {
+ dev_dbg(codec->dev,
+ "%s comp ch already enabled\n",
+ __func__);
+ return 0;
+ }
+ }
+ if (interp_n == 1) {
+ if ((comp_ch_value & 0x01) == 0x01) {
+ dev_dbg(codec->dev,
+ "%s comp ch already enabled\n",
+ __func__);
+ return 0;
+ }
+ }
+ dig_cdc->set_compander_mode(dig_cdc->handle, 0x08);
+ /* Enable Compander Clock */
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x09);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x01);
+ if (dig_cdc->comp_enabled[MSM89XX_RX1]) {
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B1_CTL,
+ 0x02, 0x02);
+ }
+ if (dig_cdc->comp_enabled[MSM89XX_RX2]) {
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B1_CTL,
+ 0x01, 0x01);
+ }
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x01);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0x50);
+ /* add sleep for compander to settle */
+ usleep_range(1000, 1100);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x28);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0xB0);
+
+ /* Enable Compander GPIO */
+ if (dig_cdc->codec_hph_comp_gpio)
+ dig_cdc->codec_hph_comp_gpio(1, codec);
+ } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ /* Disable Compander GPIO */
+ if (dig_cdc->codec_hph_comp_gpio)
+ dig_cdc->codec_hph_comp_gpio(0, codec);
+
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B1_CTL,
+ 1 << interp_n, 0);
+ comp_ch_bits_set = snd_soc_read(codec,
+ MSM89XX_CDC_CORE_COMP0_B1_CTL);
+ if ((comp_ch_bits_set & 0x03) == 0x00) {
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x05);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x00);
+ }
+ }
+ return 0;
+}
+
+/**
+ * msm_dig_cdc_hph_comp_cb - registers callback to codec by machine driver.
+ *
+ * @codec_hph_comp_gpio: function pointer to set comp gpio at machine driver
+ * @codec: codec pointer
+ *
+ */
+void msm_dig_cdc_hph_comp_cb(
+ int (*codec_hph_comp_gpio)(bool enable, struct snd_soc_codec *codec),
+ struct snd_soc_codec *codec)
+{
+ struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: Enter\n", __func__);
+ dig_cdc->codec_hph_comp_gpio = codec_hph_comp_gpio;
+}
+EXPORT_SYMBOL(msm_dig_cdc_hph_comp_cb);
+
+static int msm_dig_cdc_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
+
+ if (w->shift >= MSM89XX_RX_MAX || w->shift < 0) {
+ dev_err(codec->dev, "%s: wrong RX index: %d\n",
+ __func__, w->shift);
+ return -EINVAL;
+ }
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msm_dig_cdc_codec_config_compander(codec, w->shift, event);
+ /* apply the digital gain after the interpolator is enabled*/
+ if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg))
+ snd_soc_write(codec,
+ rx_digital_gain_reg[w->shift],
+ snd_soc_read(codec,
+ rx_digital_gain_reg[w->shift])
+ );
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ msm_dig_cdc_codec_config_compander(codec, w->shift, event);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_CLK_RX_RESET_CTL,
+ 1 << w->shift, 1 << w->shift);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_CLK_RX_RESET_CTL,
+ 1 << w->shift, 0x0);
+ /*
+ * disable the mute enabled during the PMD of this device
+ */
+ if ((w->shift == 0) &&
+ (msm_dig_cdc->mute_mask & HPHL_PA_DISABLE)) {
+ pr_debug("disabling HPHL mute\n");
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00);
+ msm_dig_cdc->mute_mask &= ~(HPHL_PA_DISABLE);
+ } else if ((w->shift == 1) &&
+ (msm_dig_cdc->mute_mask & HPHR_PA_DISABLE)) {
+ pr_debug("disabling HPHR mute\n");
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00);
+ msm_dig_cdc->mute_mask &= ~(HPHR_PA_DISABLE);
+ } else if ((w->shift == 2) &&
+ (msm_dig_cdc->mute_mask & SPKR_PA_DISABLE)) {
+ pr_debug("disabling SPKR mute\n");
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00);
+ msm_dig_cdc->mute_mask &= ~(SPKR_PA_DISABLE);
+ }
+ }
+ return 0;
+}
+
+static int msm_dig_cdc_get_iir_enable_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] =
+ (snd_soc_read(codec,
+ (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) &
+ (1 << band_idx)) != 0;
+
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
+ iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_dig_cdc_put_iir_enable_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+
+ /* Mask first 5 bits, 6-8 are reserved */
+ snd_soc_update_bits(codec,
+ (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx),
+ (1 << band_idx), (value << band_idx));
+
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
+ iir_idx, band_idx,
+ ((snd_soc_read(codec,
+ (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) &
+ (1 << band_idx)) != 0));
+
+ return 0;
+}
+
+static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec,
+ int iir_idx, int band_idx,
+ int coeff_idx)
+{
+ uint32_t value = 0;
+
+ /* Address does not automatically update if reading */
+ snd_soc_write(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t)) & 0x7F);
+
+ value |= snd_soc_read(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx));
+
+ snd_soc_write(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 1) & 0x7F);
+
+ value |= (snd_soc_read(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 8);
+
+ snd_soc_write(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 2) & 0x7F);
+
+ value |= (snd_soc_read(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 16);
+
+ snd_soc_write(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 3) & 0x7F);
+
+ /* Mask bits top 2 bits since they are reserved */
+ value |= ((snd_soc_read(codec, (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL
+ + 64 * iir_idx)) & 0x3f) << 24);
+
+ return value;
+
+}
+
+static void set_iir_band_coeff(struct snd_soc_codec *codec,
+ int iir_idx, int band_idx,
+ uint32_t value)
+{
+ snd_soc_write(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value & 0xFF));
+
+ snd_soc_write(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value >> 8) & 0xFF);
+
+ snd_soc_write(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value >> 16) & 0xFF);
+
+ /* Mask top 2 bits, 7-8 are reserved */
+ snd_soc_write(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx),
+ (value >> 24) & 0x3F);
+
+}
+
+static int msm_dig_cdc_get_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 0);
+ ucontrol->value.integer.value[1] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 1);
+ ucontrol->value.integer.value[2] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 2);
+ ucontrol->value.integer.value[3] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 3);
+ ucontrol->value.integer.value[4] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 4);
+
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n"
+ "%s: IIR #%d band #%d b1 = 0x%x\n"
+ "%s: IIR #%d band #%d b2 = 0x%x\n"
+ "%s: IIR #%d band #%d a1 = 0x%x\n"
+ "%s: IIR #%d band #%d a2 = 0x%x\n",
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[0],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[1],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[2],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[3],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[4]);
+ return 0;
+}
+
+static int msm_dig_cdc_put_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ /* Mask top bit it is reserved */
+ /* Updates addr automatically for each B2 write */
+ snd_soc_write(codec,
+ (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx),
+ (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[0]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[1]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[2]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[3]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[4]);
+
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n"
+ "%s: IIR #%d band #%d b1 = 0x%x\n"
+ "%s: IIR #%d band #%d b2 = 0x%x\n"
+ "%s: IIR #%d band #%d a1 = 0x%x\n"
+ "%s: IIR #%d band #%d a2 = 0x%x\n",
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 0),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 1),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 2),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 3),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 4));
+ return 0;
+}
+
+static void tx_hpf_corner_freq_callback(struct work_struct *work)
+{
+ struct delayed_work *hpf_delayed_work;
+ struct hpf_work *hpf_work;
+ struct snd_soc_codec *codec;
+ struct msm_dig_priv *msm_dig_cdc;
+ u16 tx_mux_ctl_reg;
+ u8 hpf_cut_of_freq;
+
+ hpf_delayed_work = to_delayed_work(work);
+ hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
+ codec = hpf_work->dig_cdc->codec;
+ msm_dig_cdc = hpf_work->dig_cdc;
+ hpf_cut_of_freq = hpf_work->tx_hpf_cut_of_freq;
+
+ tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL +
+ (hpf_work->decimator - 1) * 32;
+
+ dev_dbg(codec->dev, "%s(): decimator %u hpf_cut_of_freq 0x%x\n",
+ __func__, hpf_work->decimator, (unsigned int)hpf_cut_of_freq);
+ msm_dig_cdc->update_clkdiv(msm_dig_cdc->handle, 0x51);
+
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, hpf_cut_of_freq << 4);
+}
+
+static int msm_dig_cdc_codec_set_iir_gain(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int value = 0, reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (w->shift == 0)
+ reg = MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL;
+ else if (w->shift == 1)
+ reg = MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL;
+ else
+ goto ret;
+ value = snd_soc_read(codec, reg);
+ snd_soc_write(codec, reg, value);
+ break;
+ default:
+ pr_err("%s: event = %d not expected\n", __func__, event);
+ }
+ret:
+ return 0;
+}
+
+static int msm_dig_cdc_compander_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec);
+ int comp_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int rx_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ dev_dbg(codec->dev, "%s: msm_dig_cdc->comp[%d]_enabled[%d] = %d\n",
+ __func__, comp_idx, rx_idx,
+ dig_cdc->comp_enabled[rx_idx]);
+
+ ucontrol->value.integer.value[0] = dig_cdc->comp_enabled[rx_idx];
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_dig_cdc_compander_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec);
+ int comp_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int rx_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ if (dig_cdc->version >= DIANGU) {
+ if (!value)
+ dig_cdc->comp_enabled[rx_idx] = 0;
+ else
+ dig_cdc->comp_enabled[rx_idx] = comp_idx;
+ }
+
+ dev_dbg(codec->dev, "%s: msm_dig_cdc->comp[%d]_enabled[%d] = %d\n",
+ __func__, comp_idx, rx_idx,
+ dig_cdc->comp_enabled[rx_idx]);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new compander_kcontrols[] = {
+ SOC_SINGLE_EXT("COMP0 RX1", COMPANDER_1, MSM89XX_RX1, 1, 0,
+ msm_dig_cdc_compander_get, msm_dig_cdc_compander_set),
+
+ SOC_SINGLE_EXT("COMP0 RX2", COMPANDER_1, MSM89XX_RX2, 1, 0,
+ msm_dig_cdc_compander_get, msm_dig_cdc_compander_set),
+
+};
+
+static int msm_dig_cdc_set_interpolator_rate(struct snd_soc_dai *dai,
+ u8 rx_fs_rate_reg_val,
+ u32 sample_rate)
+{
+ snd_soc_update_bits(dai->codec,
+ MSM89XX_CDC_CORE_RX1_B5_CTL, 0xF0, rx_fs_rate_reg_val);
+ snd_soc_update_bits(dai->codec,
+ MSM89XX_CDC_CORE_RX2_B5_CTL, 0xF0, rx_fs_rate_reg_val);
+ return 0;
+}
+
+static int msm_dig_cdc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ u8 tx_fs_rate, rx_fs_rate, rx_clk_fs_rate;
+ int ret;
+
+ dev_dbg(dai->codec->dev,
+ "%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n",
+ __func__, dai->name, dai->id, params_rate(params),
+ params_channels(params), params_format(params));
+
+ switch (params_rate(params)) {
+ case 8000:
+ tx_fs_rate = 0x00;
+ rx_fs_rate = 0x00;
+ rx_clk_fs_rate = 0x00;
+ break;
+ case 16000:
+ tx_fs_rate = 0x20;
+ rx_fs_rate = 0x20;
+ rx_clk_fs_rate = 0x01;
+ break;
+ case 32000:
+ tx_fs_rate = 0x40;
+ rx_fs_rate = 0x40;
+ rx_clk_fs_rate = 0x02;
+ break;
+ case 44100:
+ case 48000:
+ tx_fs_rate = 0x60;
+ rx_fs_rate = 0x60;
+ rx_clk_fs_rate = 0x03;
+ break;
+ case 96000:
+ tx_fs_rate = 0x80;
+ rx_fs_rate = 0x80;
+ rx_clk_fs_rate = 0x04;
+ break;
+ case 192000:
+ tx_fs_rate = 0xA0;
+ rx_fs_rate = 0xA0;
+ rx_clk_fs_rate = 0x05;
+ break;
+ default:
+ dev_err(dai->codec->dev,
+ "%s: Invalid sampling rate %d\n", __func__,
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(dai->codec,
+ MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x0F, rx_clk_fs_rate);
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ break;
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = msm_dig_cdc_set_interpolator_rate(dai, rx_fs_rate,
+ params_rate(params));
+ if (ret < 0) {
+ dev_err(dai->codec->dev,
+ "%s: set decimator rate failed %d\n", __func__,
+ ret);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(dai->codec->dev,
+ "%s: Invalid stream type %d\n", __func__,
+ substream->stream);
+ return -EINVAL;
+ }
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(dai->codec,
+ MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ snd_soc_update_bits(dai->codec,
+ MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x00);
+ break;
+ default:
+ dev_err(dai->codec->dev, "%s: wrong format selected\n",
+ __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int msm_dig_cdc_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec);
+ u8 dmic_clk_en;
+ u16 dmic_clk_reg;
+ s32 *dmic_clk_cnt;
+ unsigned int dmic;
+ int ret;
+ char *dmic_num = strpbrk(w->name, "1234");
+
+ if (dmic_num == NULL) {
+ dev_err(codec->dev, "%s: Invalid DMIC\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(dmic_num, 10, &dmic);
+ if (ret < 0) {
+ dev_err(codec->dev,
+ "%s: Invalid DMIC line on the codec\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (dmic) {
+ case 1:
+ case 2:
+ dmic_clk_en = 0x01;
+ dmic_clk_cnt = &(dig_cdc->dmic_1_2_clk_cnt);
+ dmic_clk_reg = MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL;
+ dev_dbg(codec->dev,
+ "%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n",
+ __func__, event, dmic, *dmic_clk_cnt);
+ break;
+ case 3:
+ case 4:
+ dmic_clk_en = 0x01;
+ dmic_clk_cnt = &(dig_cdc->dmic_3_4_clk_cnt);
+ dmic_clk_reg = MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL;
+ dev_dbg(codec->dev,
+ "%s() event %d DMIC%d dmic_3_4_clk_cnt %d\n",
+ __func__, event, dmic, *dmic_clk_cnt);
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid DMIC Selection\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ (*dmic_clk_cnt)++;
+ if (*dmic_clk_cnt == 1) {
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ 0x0E, 0x04);
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ dmic_clk_en, dmic_clk_en);
+ }
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_TX1_DMIC_CTL + (dmic - 1) * 0x20,
+ 0x07, 0x02);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ (*dmic_clk_cnt)--;
+ if (*dmic_clk_cnt == 0)
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ dmic_clk_en, 0);
+ break;
+ }
+ return 0;
+}
+
+static int msm_dig_cdc_codec_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct msm_asoc_mach_data *pdata = NULL;
+ unsigned int decimator;
+ struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec);
+ char *dec_name = NULL;
+ char *widget_name = NULL;
+ char *temp;
+ int ret = 0, i;
+ u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg;
+ u8 dec_hpf_cut_of_freq;
+ int offset;
+ char *dec_num;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+ dev_dbg(codec->dev, "%s %d\n", __func__, event);
+
+ widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+ if (!widget_name)
+ return -ENOMEM;
+ temp = widget_name;
+
+ dec_name = strsep(&widget_name, " ");
+ widget_name = temp;
+ if (!dec_name) {
+ dev_err(codec->dev,
+ "%s: Invalid decimator = %s\n", __func__, w->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dec_num = strpbrk(dec_name, "12345");
+ if (dec_num == NULL) {
+ dev_err(codec->dev, "%s: Invalid Decimator\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = kstrtouint(dec_num, 10, &decimator);
+ if (ret < 0) {
+ dev_err(codec->dev,
+ "%s: Invalid decimator = %s\n", __func__, dec_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dev_dbg(codec->dev,
+ "%s(): widget = %s dec_name = %s decimator = %u\n", __func__,
+ w->name, dec_name, decimator);
+
+ if (w->reg == MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL) {
+ dec_reset_reg = MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL;
+ offset = 0;
+ } else {
+ dev_err(codec->dev, "%s: Error, incorrect dec\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG +
+ 32 * (decimator - 1);
+ tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL +
+ 32 * (decimator - 1);
+ if (decimator == DEC_SVA) {
+ tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG;
+ tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX5_MUX_CTL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enableable TX digital mute */
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01);
+ for (i = 0; i < NUM_DECIMATORS; i++) {
+ if (decimator == i + 1)
+ msm_dig_cdc->dec_active[i] = true;
+ }
+
+ dec_hpf_cut_of_freq = snd_soc_read(codec, tx_mux_ctl_reg);
+
+ dec_hpf_cut_of_freq = (dec_hpf_cut_of_freq & 0x30) >> 4;
+
+ tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq =
+ dec_hpf_cut_of_freq;
+
+ if (dec_hpf_cut_of_freq != CF_MIN_3DB_150HZ) {
+
+ /* set cut of freq to CF_MIN_3DB_150HZ (0x1); */
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30,
+ CF_MIN_3DB_150HZ << 4);
+ }
+ msm_dig_cdc->update_clkdiv(msm_dig_cdc->handle, 0x42);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* enable HPF */
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x00);
+
+ schedule_delayed_work(
+ &msm_dig_cdc->tx_mute_dwork[decimator - 1].dwork,
+ msecs_to_jiffies(tx_unmute_delay));
+ if (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq !=
+ CF_MIN_3DB_150HZ) {
+
+ schedule_delayed_work(&tx_hpf_work[decimator - 1].dwork,
+ msecs_to_jiffies(300));
+ }
+ /* apply the digital gain after the decimator is enabled*/
+ if ((w->shift) < ARRAY_SIZE(tx_digital_gain_reg))
+ snd_soc_write(codec,
+ tx_digital_gain_reg[w->shift + offset],
+ snd_soc_read(codec,
+ tx_digital_gain_reg[w->shift + offset])
+ );
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01);
+ msleep(20);
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08);
+ cancel_delayed_work_sync(&tx_hpf_work[decimator - 1].dwork);
+ cancel_delayed_work_sync(
+ &msm_dig_cdc->tx_mute_dwork[decimator - 1].dwork);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift,
+ 1 << w->shift);
+ snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0);
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08);
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30,
+ (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq) << 4);
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00);
+ for (i = 0; i < NUM_DECIMATORS; i++) {
+ if (decimator == i + 1)
+ msm_dig_cdc->dec_active[i] = false;
+ }
+ break;
+ }
+out:
+ kfree(widget_name);
+ return ret;
+}
+
+static int msm_dig_cdc_event_notify(struct notifier_block *block,
+ unsigned long val,
+ void *data)
+{
+ enum dig_cdc_notify_event event = (enum dig_cdc_notify_event)val;
+ struct snd_soc_codec *codec = registered_digcodec;
+ struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec);
+ struct msm_asoc_mach_data *pdata = NULL;
+ int ret = -EINVAL;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+
+ switch (event) {
+ case DIG_CDC_EVENT_CLK_ON:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x03);
+ if (pdata->mclk_freq == MCLK_RATE_12P288MHZ ||
+ pdata->native_clk_set)
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x00);
+ else if (pdata->mclk_freq == MCLK_RATE_9P6MHZ)
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x01);
+ break;
+ case DIG_CDC_EVENT_CLK_OFF:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x00);
+ break;
+ case DIG_CDC_EVENT_RX1_MUTE_ON:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x01);
+ msm_dig_cdc->mute_mask |= HPHL_PA_DISABLE;
+ break;
+ case DIG_CDC_EVENT_RX1_MUTE_OFF:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00);
+ msm_dig_cdc->mute_mask &= (~HPHL_PA_DISABLE);
+ break;
+ case DIG_CDC_EVENT_RX2_MUTE_ON:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x01);
+ msm_dig_cdc->mute_mask |= HPHR_PA_DISABLE;
+ break;
+ case DIG_CDC_EVENT_RX2_MUTE_OFF:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00);
+ msm_dig_cdc->mute_mask &= (~HPHR_PA_DISABLE);
+ break;
+ case DIG_CDC_EVENT_RX3_MUTE_ON:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x01);
+ msm_dig_cdc->mute_mask |= SPKR_PA_DISABLE;
+ break;
+ case DIG_CDC_EVENT_RX3_MUTE_OFF:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00);
+ msm_dig_cdc->mute_mask &= (~SPKR_PA_DISABLE);
+ break;
+ case DIG_CDC_EVENT_PRE_RX1_INT_ON:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x28);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0x10);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x80);
+ break;
+ case DIG_CDC_EVENT_PRE_RX2_INT_ON:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x28);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0x10);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x80);
+ break;
+ case DIG_CDC_EVENT_POST_RX1_INT_OFF:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0xFF);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x00);
+ break;
+ case DIG_CDC_EVENT_POST_RX2_INT_OFF:
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x00);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0xFF);
+ snd_soc_update_bits(codec,
+ MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x00);
+ break;
+ case DIG_CDC_EVENT_SSR_DOWN:
+ clear_bit(ADSP_UP, &msm_dig_cdc->status_mask);
+ regcache_cache_only(msm_dig_cdc->regmap, true);
+ break;
+ case DIG_CDC_EVENT_SSR_UP:
+ set_bit(ADSP_UP, &msm_dig_cdc->status_mask);
+ regcache_cache_only(msm_dig_cdc->regmap, false);
+ regcache_mark_dirty(msm_dig_cdc->regmap);
+
+ mutex_lock(&pdata->cdc_int_mclk0_mutex);
+ pdata->digital_cdc_core_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT0_MI2S_RX,
+ &pdata->digital_cdc_core_clk);
+ if (ret < 0) {
+ pr_err("%s:failed to enable the MCLK\n",
+ __func__);
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+ break;
+ }
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+
+ regcache_sync(msm_dig_cdc->regmap);
+
+ mutex_lock(&pdata->cdc_int_mclk0_mutex);
+ pdata->digital_cdc_core_clk.enable = 0;
+ afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT0_MI2S_RX,
+ &pdata->digital_cdc_core_clk);
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+ break;
+ case DIG_CDC_EVENT_INVALID:
+ default:
+ break;
+ }
+ return 0;
+}
+
+static ssize_t msm_dig_codec_version_read(struct snd_info_entry *entry,
+ void *file_private_data,
+ struct file *file,
+ char __user *buf, size_t count,
+ loff_t pos)
+{
+ struct msm_dig_priv *msm_dig;
+ char buffer[MSM_DIG_CDC_VERSION_ENTRY_SIZE];
+ int len = 0;
+
+ msm_dig = (struct msm_dig_priv *) entry->private_data;
+ if (!msm_dig) {
+ pr_err("%s: msm_dig priv is null\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (msm_dig->version) {
+ case DRAX_CDC:
+ len = snprintf(buffer, sizeof(buffer), "SDM660-CDC_1_0\n");
+ break;
+ default:
+ len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n");
+ }
+
+ return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static struct snd_info_entry_ops msm_dig_codec_info_ops = {
+ .read = msm_dig_codec_version_read,
+};
+
+/*
+ * msm_dig_codec_info_create_codec_entry - creates msm_dig module
+ * @codec_root: The parent directory
+ * @codec: Codec instance
+ *
+ * Creates msm_dig module and version entry under the given
+ * parent directory.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int msm_dig_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+ struct snd_soc_codec *codec)
+{
+ struct snd_info_entry *version_entry;
+ struct msm_dig_priv *msm_dig;
+ struct snd_soc_card *card;
+
+ if (!codec_root || !codec)
+ return -EINVAL;
+
+ msm_dig = snd_soc_codec_get_drvdata(codec);
+ card = codec->component.card;
+ msm_dig->entry = snd_register_module_info(codec_root->module,
+ "msm_digital_codec",
+ codec_root);
+ if (!msm_dig->entry) {
+ dev_dbg(codec->dev, "%s: failed to create msm_digital entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry = snd_info_create_card_entry(card->snd_card,
+ "version",
+ msm_dig->entry);
+ if (!version_entry) {
+ dev_dbg(codec->dev, "%s: failed to create msm_digital version entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry->private_data = msm_dig;
+ version_entry->size = MSM_DIG_CDC_VERSION_ENTRY_SIZE;
+ version_entry->content = SNDRV_INFO_CONTENT_DATA;
+ version_entry->c.ops = &msm_dig_codec_info_ops;
+
+ if (snd_info_register(version_entry) < 0) {
+ snd_info_free_entry(version_entry);
+ return -ENOMEM;
+ }
+ msm_dig->version_entry = version_entry;
+ if (msm_dig->get_cdc_version)
+ msm_dig->version = msm_dig->get_cdc_version(msm_dig->handle);
+ else
+ msm_dig->version = DRAX_CDC;
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_dig_codec_info_create_codec_entry);
+
+static void sdm660_tx_mute_update_callback(struct work_struct *work)
+{
+ struct tx_mute_work *tx_mute_dwork;
+ struct snd_soc_codec *codec = NULL;
+ struct msm_dig_priv *dig_cdc;
+ struct delayed_work *delayed_work;
+ u16 tx_vol_ctl_reg = 0;
+ u8 decimator = 0, i;
+
+ delayed_work = to_delayed_work(work);
+ tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork);
+ dig_cdc = tx_mute_dwork->dig_cdc;
+ codec = dig_cdc->codec;
+
+ for (i = 0; i < NUM_DECIMATORS; i++) {
+ if (dig_cdc->dec_active[i])
+ decimator = i + 1;
+ if (decimator && decimator <= NUM_DECIMATORS) {
+ /* unmute decimators corresponding to Tx DAI's*/
+ tx_vol_ctl_reg =
+ MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG +
+ 32 * (decimator - 1);
+ if (decimator == DEC_SVA)
+ tx_vol_ctl_reg =
+ MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG;
+
+ snd_soc_update_bits(codec, tx_vol_ctl_reg,
+ 0x01, 0x00);
+ }
+ decimator = 0;
+ }
+}
+
+static int msm_dig_cdc_soc_probe(struct snd_soc_codec *codec)
+{
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ int i, ret;
+
+ msm_dig_cdc->codec = codec;
+
+ snd_soc_add_codec_controls(codec, compander_kcontrols,
+ ARRAY_SIZE(compander_kcontrols));
+
+ for (i = 0; i < NUM_DECIMATORS; i++) {
+ tx_hpf_work[i].dig_cdc = msm_dig_cdc;
+ tx_hpf_work[i].decimator = i + 1;
+ INIT_DELAYED_WORK(&tx_hpf_work[i].dwork,
+ tx_hpf_corner_freq_callback);
+ msm_dig_cdc->tx_mute_dwork[i].dig_cdc = msm_dig_cdc;
+ msm_dig_cdc->tx_mute_dwork[i].decimator = i + 1;
+ INIT_DELAYED_WORK(&msm_dig_cdc->tx_mute_dwork[i].dwork,
+ sdm660_tx_mute_update_callback);
+ }
+
+ for (i = 0; i < MSM89XX_RX_MAX; i++)
+ msm_dig_cdc->comp_enabled[i] = COMPANDER_NONE;
+
+ /* Register event notifier */
+ msm_dig_cdc->nblock.notifier_call = msm_dig_cdc_event_notify;
+ if (msm_dig_cdc->register_notifier) {
+ ret = msm_dig_cdc->register_notifier(msm_dig_cdc->handle,
+ &msm_dig_cdc->nblock,
+ true);
+ if (ret) {
+ pr_err("%s: Failed to register notifier %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ registered_digcodec = codec;
+
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "ADC1_IN");
+ snd_soc_dapm_ignore_suspend(dapm, "ADC2_IN");
+ snd_soc_dapm_ignore_suspend(dapm, "ADC3_IN");
+ snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX1");
+ snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX2");
+ snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX3");
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static int msm_dig_cdc_soc_remove(struct snd_soc_codec *codec)
+{
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev);
+
+ if (msm_dig_cdc->register_notifier)
+ msm_dig_cdc->register_notifier(msm_dig_cdc->handle,
+ &msm_dig_cdc->nblock,
+ false);
+ iounmap(msm_dig_cdc->dig_base);
+ return 0;
+}
+
+static const struct snd_soc_dapm_route audio_dig_map[] = {
+ {"RX_I2S_CLK", NULL, "CDC_CONN"},
+ {"I2S RX1", NULL, "RX_I2S_CLK"},
+ {"I2S RX2", NULL, "RX_I2S_CLK"},
+ {"I2S RX3", NULL, "RX_I2S_CLK"},
+
+ {"I2S TX1", NULL, "TX_I2S_CLK"},
+ {"I2S TX2", NULL, "TX_I2S_CLK"},
+ {"I2S TX3", NULL, "TX_I2S_CLK"},
+ {"I2S TX4", NULL, "TX_I2S_CLK"},
+ {"I2S TX5", NULL, "TX_I2S_CLK"},
+ {"I2S TX6", NULL, "TX_I2S_CLK"},
+
+ {"I2S TX1", NULL, "DEC1 MUX"},
+ {"I2S TX2", NULL, "DEC2 MUX"},
+ {"I2S TX3", NULL, "I2S TX2 INP1"},
+ {"I2S TX4", NULL, "I2S TX2 INP2"},
+ {"I2S TX5", NULL, "DEC3 MUX"},
+ {"I2S TX6", NULL, "I2S TX3 INP2"},
+
+ {"I2S TX2 INP1", "RX_MIX1", "RX1 MIX2"},
+ {"I2S TX2 INP1", "DEC3", "DEC3 MUX"},
+ {"I2S TX2 INP2", "RX_MIX2", "RX2 MIX2"},
+ {"I2S TX2 INP2", "RX_MIX3", "RX3 MIX1"},
+ {"I2S TX2 INP2", "DEC4", "DEC4 MUX"},
+ {"I2S TX3 INP2", "DEC4", "DEC4 MUX"},
+ {"I2S TX3 INP2", "DEC5", "DEC5 MUX"},
+
+ {"PDM_OUT_RX1", NULL, "RX1 CHAIN"},
+ {"PDM_OUT_RX2", NULL, "RX2 CHAIN"},
+ {"PDM_OUT_RX3", NULL, "RX3 CHAIN"},
+
+ {"RX1 CHAIN", NULL, "RX1 MIX2"},
+ {"RX2 CHAIN", NULL, "RX2 MIX2"},
+ {"RX3 CHAIN", NULL, "RX3 MIX1"},
+
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP1"},
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP2"},
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP3"},
+ {"RX2 MIX1", NULL, "RX2 MIX1 INP1"},
+ {"RX2 MIX1", NULL, "RX2 MIX1 INP2"},
+ {"RX3 MIX1", NULL, "RX3 MIX1 INP1"},
+ {"RX3 MIX1", NULL, "RX3 MIX1 INP2"},
+ {"RX1 MIX2", NULL, "RX1 MIX1"},
+ {"RX1 MIX2", NULL, "RX1 MIX2 INP1"},
+ {"RX2 MIX2", NULL, "RX2 MIX1"},
+ {"RX2 MIX2", NULL, "RX2 MIX2 INP1"},
+
+ {"RX1 MIX1 INP1", "RX1", "I2S RX1"},
+ {"RX1 MIX1 INP1", "RX2", "I2S RX2"},
+ {"RX1 MIX1 INP1", "RX3", "I2S RX3"},
+ {"RX1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX1 MIX1 INP1", "IIR2", "IIR2"},
+ {"RX1 MIX1 INP2", "RX1", "I2S RX1"},
+ {"RX1 MIX1 INP2", "RX2", "I2S RX2"},
+ {"RX1 MIX1 INP2", "RX3", "I2S RX3"},
+ {"RX1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX1 MIX1 INP2", "IIR2", "IIR2"},
+ {"RX1 MIX1 INP3", "RX1", "I2S RX1"},
+ {"RX1 MIX1 INP3", "RX2", "I2S RX2"},
+ {"RX1 MIX1 INP3", "RX3", "I2S RX3"},
+
+ {"RX2 MIX1 INP1", "RX1", "I2S RX1"},
+ {"RX2 MIX1 INP1", "RX2", "I2S RX2"},
+ {"RX2 MIX1 INP1", "RX3", "I2S RX3"},
+ {"RX2 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX2 MIX1 INP1", "IIR2", "IIR2"},
+ {"RX2 MIX1 INP2", "RX1", "I2S RX1"},
+ {"RX2 MIX1 INP2", "RX2", "I2S RX2"},
+ {"RX2 MIX1 INP2", "RX3", "I2S RX3"},
+ {"RX2 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX2 MIX1 INP2", "IIR2", "IIR2"},
+ {"RX2 MIX1 INP3", "RX1", "I2S RX1"},
+ {"RX2 MIX1 INP3", "RX2", "I2S RX2"},
+ {"RX2 MIX1 INP3", "RX3", "I2S RX3"},
+
+ {"RX3 MIX1 INP1", "RX1", "I2S RX1"},
+ {"RX3 MIX1 INP1", "RX2", "I2S RX2"},
+ {"RX3 MIX1 INP1", "RX3", "I2S RX3"},
+ {"RX3 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX3 MIX1 INP1", "IIR2", "IIR2"},
+ {"RX3 MIX1 INP2", "RX1", "I2S RX1"},
+ {"RX3 MIX1 INP2", "RX2", "I2S RX2"},
+ {"RX3 MIX1 INP2", "RX3", "I2S RX3"},
+ {"RX3 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX3 MIX1 INP2", "IIR2", "IIR2"},
+ {"RX3 MIX1 INP3", "RX1", "I2S RX1"},
+ {"RX3 MIX1 INP3", "RX2", "I2S RX2"},
+ {"RX3 MIX1 INP3", "RX3", "I2S RX3"},
+
+ {"RX1 MIX2 INP1", "IIR1", "IIR1"},
+ {"RX2 MIX2 INP1", "IIR1", "IIR1"},
+ {"RX1 MIX2 INP1", "IIR2", "IIR2"},
+ {"RX2 MIX2 INP1", "IIR2", "IIR2"},
+
+ /* Decimator Inputs */
+ {"DEC1 MUX", "DMIC1", "DMIC1"},
+ {"DEC1 MUX", "DMIC2", "DMIC2"},
+ {"DEC1 MUX", "DMIC3", "DMIC3"},
+ {"DEC1 MUX", "DMIC4", "DMIC4"},
+ {"DEC1 MUX", "ADC1", "ADC1_IN"},
+ {"DEC1 MUX", "ADC2", "ADC2_IN"},
+ {"DEC1 MUX", "ADC3", "ADC3_IN"},
+ {"DEC1 MUX", NULL, "CDC_CONN"},
+
+ {"DEC2 MUX", "DMIC1", "DMIC1"},
+ {"DEC2 MUX", "DMIC2", "DMIC2"},
+ {"DEC2 MUX", "DMIC3", "DMIC3"},
+ {"DEC2 MUX", "DMIC4", "DMIC4"},
+ {"DEC2 MUX", "ADC1", "ADC1_IN"},
+ {"DEC2 MUX", "ADC2", "ADC2_IN"},
+ {"DEC2 MUX", "ADC3", "ADC3_IN"},
+ {"DEC2 MUX", NULL, "CDC_CONN"},
+
+ {"DEC3 MUX", "DMIC1", "DMIC1"},
+ {"DEC3 MUX", "DMIC2", "DMIC2"},
+ {"DEC3 MUX", "DMIC3", "DMIC3"},
+ {"DEC3 MUX", "DMIC4", "DMIC4"},
+ {"DEC3 MUX", "ADC1", "ADC1_IN"},
+ {"DEC3 MUX", "ADC2", "ADC2_IN"},
+ {"DEC3 MUX", "ADC3", "ADC3_IN"},
+ {"DEC3 MUX", NULL, "CDC_CONN"},
+
+ {"DEC4 MUX", "DMIC1", "DMIC1"},
+ {"DEC4 MUX", "DMIC2", "DMIC2"},
+ {"DEC4 MUX", "DMIC3", "DMIC3"},
+ {"DEC4 MUX", "DMIC4", "DMIC4"},
+ {"DEC4 MUX", "ADC1", "ADC1_IN"},
+ {"DEC4 MUX", "ADC2", "ADC2_IN"},
+ {"DEC4 MUX", "ADC3", "ADC3_IN"},
+ {"DEC4 MUX", NULL, "CDC_CONN"},
+
+ {"DEC5 MUX", "DMIC1", "DMIC1"},
+ {"DEC5 MUX", "DMIC2", "DMIC2"},
+ {"DEC5 MUX", "DMIC3", "DMIC3"},
+ {"DEC5 MUX", "DMIC4", "DMIC4"},
+ {"DEC5 MUX", "ADC1", "ADC1_IN"},
+ {"DEC5 MUX", "ADC2", "ADC2_IN"},
+ {"DEC5 MUX", "ADC3", "ADC3_IN"},
+ {"DEC5 MUX", NULL, "CDC_CONN"},
+
+ {"IIR1", NULL, "IIR1 INP1 MUX"},
+ {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"},
+ {"IIR1 INP1 MUX", "DEC3", "DEC3 MUX"},
+ {"IIR1 INP1 MUX", "DEC4", "DEC4 MUX"},
+ {"IIR2", NULL, "IIR2 INP1 MUX"},
+ {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"},
+ {"IIR1 INP1 MUX", "DEC3", "DEC3 MUX"},
+ {"IIR1 INP1 MUX", "DEC4", "DEC4 MUX"},
+};
+
+
+static const char * const i2s_tx2_inp1_text[] = {
+ "ZERO", "RX_MIX1", "DEC3"
+};
+
+static const char * const i2s_tx2_inp2_text[] = {
+ "ZERO", "RX_MIX2", "RX_MIX3", "DEC4"
+};
+
+static const char * const i2s_tx3_inp2_text[] = {
+ "DEC4", "DEC5"
+};
+
+static const char * const rx_mix1_text[] = {
+ "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3"
+};
+
+static const char * const rx_mix2_text[] = {
+ "ZERO", "IIR1", "IIR2"
+};
+
+static const char * const dec_mux_text[] = {
+ "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2", "DMIC3", "DMIC4"
+};
+
+static const char * const iir_inp1_text[] = {
+ "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3", "DEC3", "DEC4"
+};
+
+/* I2S TX MUXes */
+static const struct soc_enum i2s_tx2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL,
+ 2, 3, i2s_tx2_inp1_text);
+
+static const struct soc_enum i2s_tx2_inp2_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL,
+ 0, 4, i2s_tx2_inp2_text);
+
+static const struct soc_enum i2s_tx3_inp2_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL,
+ 4, 2, i2s_tx3_inp2_text);
+
+/* RX1 MIX1 */
+static const struct soc_enum rx_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL,
+ 0, 6, rx_mix1_text);
+
+static const struct soc_enum rx_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL,
+ 3, 6, rx_mix1_text);
+
+static const struct soc_enum rx_mix1_inp3_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B2_CTL,
+ 0, 6, rx_mix1_text);
+
+/* RX1 MIX2 */
+static const struct soc_enum rx_mix2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B3_CTL,
+ 0, 3, rx_mix2_text);
+
+/* RX2 MIX1 */
+static const struct soc_enum rx2_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL,
+ 0, 6, rx_mix1_text);
+
+static const struct soc_enum rx2_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL,
+ 3, 6, rx_mix1_text);
+
+static const struct soc_enum rx2_mix1_inp3_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL,
+ 0, 6, rx_mix1_text);
+
+/* RX2 MIX2 */
+static const struct soc_enum rx2_mix2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B3_CTL,
+ 0, 3, rx_mix2_text);
+
+/* RX3 MIX1 */
+static const struct soc_enum rx3_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL,
+ 0, 6, rx_mix1_text);
+
+static const struct soc_enum rx3_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL,
+ 3, 6, rx_mix1_text);
+
+static const struct soc_enum rx3_mix1_inp3_chain_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL,
+ 0, 6, rx_mix1_text);
+
+/* DEC */
+static const struct soc_enum dec1_mux_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL,
+ 0, 8, dec_mux_text);
+
+static const struct soc_enum dec2_mux_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL,
+ 3, 8, dec_mux_text);
+
+static const struct soc_enum dec3_mux_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B2_CTL,
+ 0, 8, dec_mux_text);
+
+static const struct soc_enum dec4_mux_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B2_CTL,
+ 3, 8, dec_mux_text);
+
+static const struct soc_enum decsva_mux_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B3_CTL,
+ 0, 8, dec_mux_text);
+
+static const struct soc_enum iir1_inp1_mux_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL,
+ 0, 8, iir_inp1_text);
+
+static const struct soc_enum iir2_inp1_mux_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL,
+ 0, 8, iir_inp1_text);
+
+/*cut of frequency for high pass filter*/
+static const char * const cf_text[] = {
+ "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz"
+};
+
+static const struct soc_enum cf_rxmix1_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX1_B4_CTL, 0, 3, cf_text);
+
+static const struct soc_enum cf_rxmix2_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX2_B4_CTL, 0, 3, cf_text);
+
+static const struct soc_enum cf_rxmix3_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX3_B4_CTL, 0, 3, cf_text);
+
+static const struct snd_kcontrol_new rx3_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum);
+
+#define MSM89XX_DEC_ENUM(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_dapm_get_enum_double, \
+ .put = msm_dig_cdc_put_dec_enum, \
+ .private_value = (unsigned long)&xenum }
+
+static const struct snd_kcontrol_new dec1_mux =
+ MSM89XX_DEC_ENUM("DEC1 MUX Mux", dec1_mux_enum);
+
+static const struct snd_kcontrol_new dec2_mux =
+ MSM89XX_DEC_ENUM("DEC2 MUX Mux", dec2_mux_enum);
+
+static const struct snd_kcontrol_new dec3_mux =
+ MSM89XX_DEC_ENUM("DEC3 MUX Mux", dec3_mux_enum);
+
+static const struct snd_kcontrol_new dec4_mux =
+ MSM89XX_DEC_ENUM("DEC4 MUX Mux", dec4_mux_enum);
+
+static const struct snd_kcontrol_new decsva_mux =
+ MSM89XX_DEC_ENUM("DEC5 MUX Mux", decsva_mux_enum);
+
+static const struct snd_kcontrol_new i2s_tx2_inp1_mux =
+ SOC_DAPM_ENUM("I2S TX2 INP1 Mux", i2s_tx2_inp1_chain_enum);
+
+static const struct snd_kcontrol_new i2s_tx2_inp2_mux =
+ SOC_DAPM_ENUM("I2S TX2 INP2 Mux", i2s_tx2_inp2_chain_enum);
+
+static const struct snd_kcontrol_new i2s_tx3_inp2_mux =
+ SOC_DAPM_ENUM("I2S TX3 INP2 Mux", i2s_tx3_inp2_chain_enum);
+
+static const struct snd_kcontrol_new iir1_inp1_mux =
+ SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum);
+
+static const struct snd_kcontrol_new iir2_inp1_mux =
+ SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_mix1_inp3_mux =
+ SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp3_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX2 MIX1 INP2 Mux", rx2_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix1_inp3_mux =
+ SOC_DAPM_ENUM("RX2 MIX1 INP3 Mux", rx2_mix1_inp3_chain_enum);
+
+static const struct snd_kcontrol_new rx3_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx3_mix1_inp3_mux =
+ SOC_DAPM_ENUM("RX3 MIX1 INP3 Mux", rx3_mix1_inp3_chain_enum);
+
+static const struct snd_kcontrol_new rx1_mix2_inp1_mux =
+ SOC_DAPM_ENUM("RX1 MIX2 INP1 Mux", rx_mix2_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix2_inp1_mux =
+ SOC_DAPM_ENUM("RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum);
+
+static const struct snd_soc_dapm_widget msm_dig_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("I2S RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("I2S TX1", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S TX2", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S TX3", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S TX4", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S TX5", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("I2S TX6", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MIXER_E("RX1 MIX2", MSM89XX_CDC_CORE_CLK_RX_B1_CTL,
+ MSM89XX_RX1, 0, NULL, 0,
+ msm_dig_cdc_codec_enable_interpolator,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX2 MIX2", MSM89XX_CDC_CORE_CLK_RX_B1_CTL,
+ MSM89XX_RX2, 0, NULL, 0,
+ msm_dig_cdc_codec_enable_interpolator,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX3 MIX1", MSM89XX_CDC_CORE_CLK_RX_B1_CTL,
+ MSM89XX_RX3, 0, NULL, 0,
+ msm_dig_cdc_codec_enable_interpolator,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("RX1 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX2 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX3 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp3_mux),
+
+ SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx2_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx2_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+ &rx2_mix1_inp3_mux),
+
+ SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx3_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx3_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+ &rx3_mix1_inp3_mux),
+
+ SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+ &rx1_mix2_inp1_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+ &rx2_mix2_inp1_mux),
+
+ SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, MSM89XX_CDC_CORE_CLK_OTHR_CTL,
+ 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX_E("DEC1 MUX",
+ MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0, 0,
+ &dec1_mux, msm_dig_cdc_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC2 MUX",
+ MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 1, 0,
+ &dec2_mux, msm_dig_cdc_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC3 MUX",
+ MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 2, 0,
+ &dec3_mux, msm_dig_cdc_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC4 MUX",
+ MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 3, 0,
+ &dec4_mux, msm_dig_cdc_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC5 MUX",
+ MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 4, 0,
+ &decsva_mux, msm_dig_cdc_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ /* Sidetone */
+ SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+ SND_SOC_DAPM_PGA_E("IIR1", MSM89XX_CDC_CORE_CLK_SD_CTL, 0, 0, NULL, 0,
+ msm_dig_cdc_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux),
+ SND_SOC_DAPM_PGA_E("IIR2", MSM89XX_CDC_CORE_CLK_SD_CTL, 1, 0, NULL, 0,
+ msm_dig_cdc_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("RX_I2S_CLK",
+ MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TX_I2S_CLK",
+ MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 4, 0, NULL, 0),
+
+
+ SND_SOC_DAPM_MUX("I2S TX2 INP1", SND_SOC_NOPM, 0, 0,
+ &i2s_tx2_inp1_mux),
+ SND_SOC_DAPM_MUX("I2S TX2 INP2", SND_SOC_NOPM, 0, 0,
+ &i2s_tx2_inp2_mux),
+ SND_SOC_DAPM_MUX("I2S TX3 INP2", SND_SOC_NOPM, 0, 0,
+ &i2s_tx3_inp2_mux),
+
+ /* Digital Mic Inputs */
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+ msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+ msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0,
+ msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0,
+ msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("ADC1_IN"),
+ SND_SOC_DAPM_INPUT("ADC2_IN"),
+ SND_SOC_DAPM_INPUT("ADC3_IN"),
+ SND_SOC_DAPM_OUTPUT("PDM_OUT_RX1"),
+ SND_SOC_DAPM_OUTPUT("PDM_OUT_RX2"),
+ SND_SOC_DAPM_OUTPUT("PDM_OUT_RX3"),
+};
+
+static const struct soc_enum cf_dec1_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX1_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec2_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX2_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec3_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX3_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec4_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX4_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_decsva_enum =
+ SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX5_MUX_CTL, 4, 3, cf_text);
+
+static const struct snd_kcontrol_new msm_dig_snd_controls[] = {
+ SOC_SINGLE_SX_TLV("DEC1 Volume",
+ MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC2 Volume",
+ MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC3 Volume",
+ MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC4 Volume",
+ MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC5 Volume",
+ MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN,
+ 0, -84, 40, digital_gain),
+
+ SOC_SINGLE_SX_TLV("IIR1 INP1 Volume",
+ MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP2 Volume",
+ MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP3 Volume",
+ MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP4 Volume",
+ MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR2 INP1 Volume",
+ MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL,
+ 0, -84, 40, digital_gain),
+
+ SOC_SINGLE_SX_TLV("RX1 Digital Volume",
+ MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX2 Digital Volume",
+ MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX3 Digital Volume",
+ MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL,
+ 0, -84, 40, digital_gain),
+
+ SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0,
+ msm_dig_cdc_get_iir_enable_audio_mixer,
+ msm_dig_cdc_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0,
+ msm_dig_cdc_get_iir_enable_audio_mixer,
+ msm_dig_cdc_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0,
+ msm_dig_cdc_get_iir_enable_audio_mixer,
+ msm_dig_cdc_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0,
+ msm_dig_cdc_get_iir_enable_audio_mixer,
+ msm_dig_cdc_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0,
+ msm_dig_cdc_get_iir_enable_audio_mixer,
+ msm_dig_cdc_put_iir_enable_audio_mixer),
+
+ SOC_SINGLE_EXT("IIR2 Enable Band1", IIR2, BAND1, 1, 0,
+ msm_dig_cdc_get_iir_enable_audio_mixer,
+ msm_dig_cdc_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band2", IIR2, BAND2, 1, 0,
+ msm_dig_cdc_get_iir_enable_audio_mixer,
+ msm_dig_cdc_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band3", IIR2, BAND3, 1, 0,
+ msm_dig_cdc_get_iir_enable_audio_mixer,
+ msm_dig_cdc_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band4", IIR2, BAND4, 1, 0,
+ msm_dig_cdc_get_iir_enable_audio_mixer,
+ msm_dig_cdc_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band5", IIR2, BAND5, 1, 0,
+ msm_dig_cdc_get_iir_enable_audio_mixer,
+ msm_dig_cdc_put_iir_enable_audio_mixer),
+
+ SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5,
+ msm_dig_cdc_get_iir_band_audio_mixer,
+ msm_dig_cdc_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5,
+ msm_dig_cdc_get_iir_band_audio_mixer,
+ msm_dig_cdc_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5,
+ msm_dig_cdc_get_iir_band_audio_mixer,
+ msm_dig_cdc_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5,
+ msm_dig_cdc_get_iir_band_audio_mixer,
+ msm_dig_cdc_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5,
+ msm_dig_cdc_get_iir_band_audio_mixer,
+ msm_dig_cdc_put_iir_band_audio_mixer),
+
+ SOC_SINGLE_MULTI_EXT("IIR2 Band1", IIR2, BAND1, 255, 0, 5,
+ msm_dig_cdc_get_iir_band_audio_mixer,
+ msm_dig_cdc_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band2", IIR2, BAND2, 255, 0, 5,
+ msm_dig_cdc_get_iir_band_audio_mixer,
+ msm_dig_cdc_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band3", IIR2, BAND3, 255, 0, 5,
+ msm_dig_cdc_get_iir_band_audio_mixer,
+ msm_dig_cdc_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band4", IIR2, BAND4, 255, 0, 5,
+ msm_dig_cdc_get_iir_band_audio_mixer,
+ msm_dig_cdc_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5,
+ msm_dig_cdc_get_iir_band_audio_mixer,
+ msm_dig_cdc_put_iir_band_audio_mixer),
+
+ SOC_SINGLE("RX1 HPF Switch",
+ MSM89XX_CDC_CORE_RX1_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX2 HPF Switch",
+ MSM89XX_CDC_CORE_RX2_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX3 HPF Switch",
+ MSM89XX_CDC_CORE_RX3_B5_CTL, 2, 1, 0),
+
+ SOC_ENUM("RX1 HPF cut off", cf_rxmix1_enum),
+ SOC_ENUM("RX2 HPF cut off", cf_rxmix2_enum),
+ SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum),
+
+ SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
+ SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
+ SOC_ENUM("TX3 HPF cut off", cf_dec3_enum),
+ SOC_ENUM("TX4 HPF cut off", cf_dec4_enum),
+ SOC_ENUM("TX5 HPF cut off", cf_decsva_enum),
+ SOC_SINGLE("TX1 HPF Switch",
+ MSM89XX_CDC_CORE_TX1_MUX_CTL, 3, 1, 0),
+ SOC_SINGLE("TX2 HPF Switch",
+ MSM89XX_CDC_CORE_TX2_MUX_CTL, 3, 1, 0),
+ SOC_SINGLE("TX3 HPF Switch",
+ MSM89XX_CDC_CORE_TX3_MUX_CTL, 3, 1, 0),
+ SOC_SINGLE("TX4 HPF Switch",
+ MSM89XX_CDC_CORE_TX4_MUX_CTL, 3, 1, 0),
+ SOC_SINGLE("TX5 HPF Switch",
+ MSM89XX_CDC_CORE_TX5_MUX_CTL, 3, 1, 0),
+};
+
+static struct snd_soc_dai_ops msm_dig_dai_ops = {
+ .hw_params = msm_dig_cdc_hw_params,
+};
+
+
+static struct snd_soc_dai_driver msm_codec_dais[] = {
+ {
+ .name = "msm_dig_cdc_dai_rx1",
+ .id = AIF1_PB,
+ .playback = { /* Support maximum range */
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ },
+ .ops = &msm_dig_dai_ops,
+ },
+ {
+ .name = "msm_dig_cdc_dai_tx1",
+ .id = AIF1_CAP,
+ .capture = { /* Support maximum range */
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &msm_dig_dai_ops,
+ },
+ {
+ .name = "msm_dig_cdc_dai_tx2",
+ .id = AIF3_SVA,
+ .capture = { /* Support maximum range */
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &msm_dig_dai_ops,
+ },
+ {
+ .name = "msm_dig_cdc_dai_vifeed",
+ .id = AIF2_VIFEED,
+ .capture = { /* Support maximum range */
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &msm_dig_dai_ops,
+ },
+};
+
+static struct regmap *msm_digital_get_regmap(struct device *dev)
+{
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(dev);
+
+ return msm_dig_cdc->regmap;
+}
+
+static int msm_dig_cdc_suspend(struct snd_soc_codec *codec)
+{
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev);
+
+ msm_dig_cdc->dapm_bias_off = 1;
+ return 0;
+}
+
+static int msm_dig_cdc_resume(struct snd_soc_codec *codec)
+{
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev);
+
+ msm_dig_cdc->dapm_bias_off = 0;
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_msm_dig_codec = {
+ .probe = msm_dig_cdc_soc_probe,
+ .remove = msm_dig_cdc_soc_remove,
+ .suspend = msm_dig_cdc_suspend,
+ .resume = msm_dig_cdc_resume,
+ .controls = msm_dig_snd_controls,
+ .num_controls = ARRAY_SIZE(msm_dig_snd_controls),
+ .dapm_widgets = msm_dig_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(msm_dig_dapm_widgets),
+ .dapm_routes = audio_dig_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_dig_map),
+ .get_regmap = msm_digital_get_regmap,
+};
+
+const struct regmap_config msm_digital_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 8,
+ .lock = enable_digital_callback,
+ .unlock = disable_digital_callback,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = msm89xx_cdc_core_defaults,
+ .num_reg_defaults = MSM89XX_CDC_CORE_MAX_REGISTER,
+ .writeable_reg = msm89xx_cdc_core_writeable_reg,
+ .readable_reg = msm89xx_cdc_core_readable_reg,
+ .volatile_reg = msm89xx_cdc_core_volatile_reg,
+ .reg_format_endian = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+ .max_register = MSM89XX_CDC_CORE_MAX_REGISTER,
+};
+
+static int msm_dig_cdc_probe(struct platform_device *pdev)
+{
+ int ret;
+ u32 dig_cdc_addr;
+ struct msm_dig_priv *msm_dig_cdc;
+ struct dig_ctrl_platform_data *pdata;
+
+ msm_dig_cdc = devm_kzalloc(&pdev->dev, sizeof(struct msm_dig_priv),
+ GFP_KERNEL);
+ if (!msm_dig_cdc)
+ return -ENOMEM;
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "%s: pdata from parent is NULL\n",
+ __func__);
+ ret = -EINVAL;
+ goto rtn;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "reg",
+ &dig_cdc_addr);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: could not find %s entry in dt\n",
+ __func__, "reg");
+ return ret;
+ }
+
+ msm_dig_cdc->dig_base = ioremap(dig_cdc_addr,
+ MSM89XX_CDC_CORE_MAX_REGISTER);
+ if (msm_dig_cdc->dig_base == NULL) {
+ dev_err(&pdev->dev, "%s ioremap failed\n", __func__);
+ return -ENOMEM;
+ }
+ msm_dig_cdc->regmap =
+ devm_regmap_init_mmio_clk(&pdev->dev, NULL,
+ msm_dig_cdc->dig_base, &msm_digital_regmap_config);
+
+ msm_dig_cdc->update_clkdiv = pdata->update_clkdiv;
+ msm_dig_cdc->set_compander_mode = pdata->set_compander_mode;
+ msm_dig_cdc->get_cdc_version = pdata->get_cdc_version;
+ msm_dig_cdc->handle = pdata->handle;
+ msm_dig_cdc->register_notifier = pdata->register_notifier;
+
+ dev_set_drvdata(&pdev->dev, msm_dig_cdc);
+ snd_soc_register_codec(&pdev->dev, &soc_msm_dig_codec,
+ msm_codec_dais, ARRAY_SIZE(msm_codec_dais));
+ dev_dbg(&pdev->dev, "%s: registered DIG CODEC 0x%x\n",
+ __func__, dig_cdc_addr);
+ set_bit(ADSP_UP, &msm_dig_cdc->status_mask);
+rtn:
+ return ret;
+}
+
+static int msm_dig_cdc_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int msm_dig_suspend(struct device *dev)
+{
+ struct msm_asoc_mach_data *pdata;
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(dev);
+
+ if (!registered_digcodec || !msm_dig_cdc) {
+ pr_debug("%s:digcodec not initialized, return\n", __func__);
+ return 0;
+ }
+ if (!registered_digcodec->component.card) {
+ pr_debug("%s:component not initialized, return\n", __func__);
+ return 0;
+ }
+ pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card);
+ if (!pdata) {
+ pr_debug("%s:card not initialized, return\n", __func__);
+ return 0;
+ }
+ if (msm_dig_cdc->dapm_bias_off) {
+ pr_debug("%s: mclk cnt = %d, mclk_enabled = %d\n",
+ __func__, atomic_read(&pdata->int_mclk0_rsc_ref),
+ atomic_read(&pdata->int_mclk0_enabled));
+
+ if (atomic_read(&pdata->int_mclk0_enabled) == true) {
+ cancel_delayed_work_sync(
+ &pdata->disable_int_mclk0_work);
+ mutex_lock(&pdata->cdc_int_mclk0_mutex);
+ pdata->digital_cdc_core_clk.enable = 0;
+ afe_set_lpass_clock_v2(AFE_PORT_ID_INT0_MI2S_RX,
+ &pdata->digital_cdc_core_clk);
+ atomic_set(&pdata->int_mclk0_enabled, false);
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+ }
+ }
+
+ return 0;
+}
+
+static int msm_dig_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops msm_dig_pm_ops = {
+ .suspend_late = msm_dig_suspend,
+ .resume_early = msm_dig_resume,
+};
+#endif
+
+static const struct of_device_id msm_dig_cdc_of_match[] = {
+ {.compatible = "qcom,msm-digital-codec"},
+ {},
+};
+
+static struct platform_driver msm_digcodec_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ .of_match_table = msm_dig_cdc_of_match,
+#ifdef CONFIG_PM
+ .pm = &msm_dig_pm_ops,
+#endif
+ },
+ .probe = msm_dig_cdc_probe,
+ .remove = msm_dig_cdc_remove,
+};
+module_platform_driver(msm_digcodec_driver);
+
+MODULE_DESCRIPTION("MSM Audio Digital codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h
new file mode 100644
index 000000000000..ab470de460ea
--- /dev/null
+++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h
@@ -0,0 +1,101 @@
+/* Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef MSM_DIGITAL_CDC_H
+#define MSM_DIGITAL_CDC_H
+
+#define HPHL_PA_DISABLE (0x01 << 1)
+#define HPHR_PA_DISABLE (0x01 << 2)
+#define SPKR_PA_DISABLE (0x01 << 3)
+
+#define NUM_DECIMATORS 5
+/* Codec supports 1 compander */
+enum {
+ COMPANDER_NONE = 0,
+ COMPANDER_1, /* HPHL/R */
+ COMPANDER_MAX,
+};
+
+/* Number of output I2S port */
+enum {
+ MSM89XX_RX1 = 0,
+ MSM89XX_RX2,
+ MSM89XX_RX3,
+ MSM89XX_RX_MAX,
+};
+
+struct tx_mute_work {
+ struct msm_dig_priv *dig_cdc;
+ u32 decimator;
+ struct delayed_work dwork;
+};
+
+struct msm_dig_priv {
+ struct snd_soc_codec *codec;
+ u32 comp_enabled[MSM89XX_RX_MAX];
+ int (*codec_hph_comp_gpio)(bool enable, struct snd_soc_codec *codec);
+ s32 dmic_1_2_clk_cnt;
+ s32 dmic_3_4_clk_cnt;
+ bool dec_active[NUM_DECIMATORS];
+ int version;
+ /* Entry for version info */
+ struct snd_info_entry *entry;
+ struct snd_info_entry *version_entry;
+ char __iomem *dig_base;
+ struct regmap *regmap;
+ struct notifier_block nblock;
+ u32 mute_mask;
+ int dapm_bias_off;
+ void *handle;
+ void (*set_compander_mode)(void *handle, int val);
+ void (*update_clkdiv)(void *handle, int val);
+ int (*get_cdc_version)(void *handle);
+ int (*register_notifier)(void *handle,
+ struct notifier_block *nblock,
+ bool enable);
+ struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS];
+ unsigned long status_mask;
+};
+
+struct dig_ctrl_platform_data {
+ void *handle;
+ void (*set_compander_mode)(void *handle, int val);
+ void (*update_clkdiv)(void *handle, int val);
+ int (*get_cdc_version)(void *handle);
+ int (*register_notifier)(void *handle,
+ struct notifier_block *nblock,
+ bool enable);
+};
+
+struct hpf_work {
+ struct msm_dig_priv *dig_cdc;
+ u32 decimator;
+ u8 tx_hpf_cut_of_freq;
+ struct delayed_work dwork;
+};
+
+/* Codec supports 5 bands */
+enum {
+ BAND1 = 0,
+ BAND2,
+ BAND3,
+ BAND4,
+ BAND5,
+ BAND_MAX,
+};
+
+extern void msm_dig_cdc_hph_comp_cb(
+ int (*codec_hph_comp_gpio)(
+ bool enable, struct snd_soc_codec *codec),
+ struct snd_soc_codec *codec);
+int msm_dig_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+ struct snd_soc_codec *codec);
+#endif
diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.c b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.c
new file mode 100644
index 000000000000..78a9a10a258f
--- /dev/null
+++ b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.c
@@ -0,0 +1,413 @@
+/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/of_irq.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/interrupt.h>
+#include <linux/pm_qos.h>
+#include <soc/qcom/pm.h>
+#include <sound/soc.h>
+#include "msm-analog-cdc.h"
+#include "sdm660-cdc-irq.h"
+#include "sdm660-cdc-registers.h"
+
+#define MAX_NUM_IRQS 14
+#define NUM_IRQ_REGS 2
+#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 700
+
+#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE))
+#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
+
+static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data);
+
+char *irq_names[MAX_NUM_IRQS] = {
+ "spk_cnp_int",
+ "spk_clip_int",
+ "spk_ocp_int",
+ "ins_rem_det1",
+ "but_rel_det",
+ "but_press_det",
+ "ins_rem_det",
+ "mbhc_int",
+ "ear_ocp_int",
+ "hphr_ocp_int",
+ "hphl_ocp_det",
+ "ear_cnp_int",
+ "hphr_cnp_int",
+ "hphl_cnp_int"
+};
+
+int order[MAX_NUM_IRQS] = {
+ MSM89XX_IRQ_SPKR_CNP,
+ MSM89XX_IRQ_SPKR_CLIP,
+ MSM89XX_IRQ_SPKR_OCP,
+ MSM89XX_IRQ_MBHC_INSREM_DET1,
+ MSM89XX_IRQ_MBHC_RELEASE,
+ MSM89XX_IRQ_MBHC_PRESS,
+ MSM89XX_IRQ_MBHC_INSREM_DET,
+ MSM89XX_IRQ_MBHC_HS_DET,
+ MSM89XX_IRQ_EAR_OCP,
+ MSM89XX_IRQ_HPHR_OCP,
+ MSM89XX_IRQ_HPHL_OCP,
+ MSM89XX_IRQ_EAR_CNP,
+ MSM89XX_IRQ_HPHR_CNP,
+ MSM89XX_IRQ_HPHL_CNP,
+};
+
+enum wcd9xxx_spmi_pm_state {
+ WCD9XXX_PM_SLEEPABLE,
+ WCD9XXX_PM_AWAKE,
+ WCD9XXX_PM_ASLEEP,
+};
+
+struct wcd9xxx_spmi_map {
+ uint8_t handled[NUM_IRQ_REGS];
+ uint8_t mask[NUM_IRQ_REGS];
+ int linuxirq[MAX_NUM_IRQS];
+ irq_handler_t handler[MAX_NUM_IRQS];
+ struct platform_device *spmi[NUM_IRQ_REGS];
+ struct snd_soc_codec *codec;
+
+ enum wcd9xxx_spmi_pm_state pm_state;
+ struct mutex pm_lock;
+ /* pm_wq notifies change of pm_state */
+ wait_queue_head_t pm_wq;
+ struct pm_qos_request pm_qos_req;
+ int wlock_holders;
+};
+
+struct wcd9xxx_spmi_map map;
+
+void wcd9xxx_spmi_enable_irq(int irq)
+{
+ pr_debug("%s: irqno =%d\n", __func__, irq);
+
+ if (!(map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq))))
+ return;
+
+ map.mask[BIT_BYTE(irq)] &=
+ ~(BYTE_BIT_MASK(irq));
+
+ enable_irq(map.linuxirq[irq]);
+}
+
+void wcd9xxx_spmi_disable_irq(int irq)
+{
+ pr_debug("%s: irqno =%d\n", __func__, irq);
+
+ if (map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq)))
+ return;
+
+ map.mask[BIT_BYTE(irq)] |=
+ (BYTE_BIT_MASK(irq));
+
+ disable_irq_nosync(map.linuxirq[irq]);
+}
+
+int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler,
+ const char *name, void *priv)
+{
+ int rc;
+ unsigned long irq_flags;
+
+ map.linuxirq[irq] =
+ platform_get_irq_byname(map.spmi[BIT_BYTE(irq)],
+ irq_names[irq]);
+
+ if (strcmp(name, "mbhc sw intr"))
+ irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT;
+ else
+ irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT | IRQF_NO_SUSPEND;
+ pr_debug("%s: name:%s irq_flags = %lx\n", __func__, name, irq_flags);
+
+ rc = devm_request_threaded_irq(&map.spmi[BIT_BYTE(irq)]->dev,
+ map.linuxirq[irq], NULL,
+ wcd9xxx_spmi_irq_handler,
+ irq_flags,
+ name, priv);
+ if (rc < 0) {
+ dev_err(&map.spmi[BIT_BYTE(irq)]->dev,
+ "Can't request %d IRQ\n", irq);
+ return rc;
+ }
+
+ dev_dbg(&map.spmi[BIT_BYTE(irq)]->dev,
+ "irq %d linuxIRQ: %d\n", irq, map.linuxirq[irq]);
+ map.mask[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq);
+ map.handler[irq] = handler;
+ enable_irq_wake(map.linuxirq[irq]);
+ return 0;
+}
+
+int wcd9xxx_spmi_free_irq(int irq, void *priv)
+{
+ devm_free_irq(&map.spmi[BIT_BYTE(irq)]->dev, map.linuxirq[irq],
+ priv);
+ map.mask[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq);
+ return 0;
+}
+
+static int get_irq_bit(int linux_irq)
+{
+ int i = 0;
+
+ for (; i < MAX_NUM_IRQS; i++)
+ if (map.linuxirq[i] == linux_irq)
+ return i;
+
+ return i;
+}
+
+static int get_order_irq(int i)
+{
+ return order[i];
+}
+
+static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data)
+{
+ int irq, i, j;
+ unsigned long status[NUM_IRQ_REGS] = {0};
+
+ if (unlikely(wcd9xxx_spmi_lock_sleep() == false)) {
+ pr_err("Failed to hold suspend\n");
+ return IRQ_NONE;
+ }
+
+ irq = get_irq_bit(linux_irq);
+ if (irq == MAX_NUM_IRQS)
+ return IRQ_HANDLED;
+
+ status[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq);
+ for (i = 0; i < NUM_IRQ_REGS; i++) {
+ status[i] |= snd_soc_read(map.codec,
+ BIT_BYTE(irq) * 0x100 +
+ MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS);
+ status[i] &= ~map.mask[i];
+ }
+ for (i = 0; i < MAX_NUM_IRQS; i++) {
+ j = get_order_irq(i);
+ if ((status[BIT_BYTE(j)] & BYTE_BIT_MASK(j)) &&
+ ((map.handled[BIT_BYTE(j)] &
+ BYTE_BIT_MASK(j)) == 0)) {
+ map.handler[j](irq, data);
+ map.handled[BIT_BYTE(j)] |=
+ BYTE_BIT_MASK(j);
+ }
+ }
+ map.handled[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq);
+ wcd9xxx_spmi_unlock_sleep();
+
+ return IRQ_HANDLED;
+}
+
+enum wcd9xxx_spmi_pm_state wcd9xxx_spmi_pm_cmpxchg(
+ enum wcd9xxx_spmi_pm_state o,
+ enum wcd9xxx_spmi_pm_state n)
+{
+ enum wcd9xxx_spmi_pm_state old;
+
+ mutex_lock(&map.pm_lock);
+ old = map.pm_state;
+ if (old == o)
+ map.pm_state = n;
+ pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state);
+ mutex_unlock(&map.pm_lock);
+ return old;
+}
+EXPORT_SYMBOL(wcd9xxx_spmi_pm_cmpxchg);
+
+int wcd9xxx_spmi_suspend(pm_message_t pmesg)
+{
+ int ret = 0;
+
+ pr_debug("%s: enter\n", __func__);
+ /*
+ * pm_qos_update_request() can be called after this suspend chain call
+ * started. thus suspend can be called while lock is being held
+ */
+ mutex_lock(&map.pm_lock);
+ if (map.pm_state == WCD9XXX_PM_SLEEPABLE) {
+ pr_debug("%s: suspending system, state %d, wlock %d\n",
+ __func__, map.pm_state,
+ map.wlock_holders);
+ map.pm_state = WCD9XXX_PM_ASLEEP;
+ } else if (map.pm_state == WCD9XXX_PM_AWAKE) {
+ /*
+ * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
+ * then set to WCD9XXX_PM_ASLEEP
+ */
+ pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
+ __func__, map.pm_state,
+ map.wlock_holders);
+ mutex_unlock(&map.pm_lock);
+ if (!(wait_event_timeout(map.pm_wq,
+ wcd9xxx_spmi_pm_cmpxchg(
+ WCD9XXX_PM_SLEEPABLE,
+ WCD9XXX_PM_ASLEEP) ==
+ WCD9XXX_PM_SLEEPABLE,
+ HZ))) {
+ pr_debug("%s: suspend failed state %d, wlock %d\n",
+ __func__, map.pm_state,
+ map.wlock_holders);
+ ret = -EBUSY;
+ } else {
+ pr_debug("%s: done, state %d, wlock %d\n", __func__,
+ map.pm_state,
+ map.wlock_holders);
+ }
+ mutex_lock(&map.pm_lock);
+ } else if (map.pm_state == WCD9XXX_PM_ASLEEP) {
+ pr_warn("%s: system is already suspended, state %d, wlock %dn",
+ __func__, map.pm_state,
+ map.wlock_holders);
+ }
+ mutex_unlock(&map.pm_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_spmi_suspend);
+
+int wcd9xxx_spmi_resume(void)
+{
+ int ret = 0;
+
+ pr_debug("%s: enter\n", __func__);
+ mutex_lock(&map.pm_lock);
+ if (map.pm_state == WCD9XXX_PM_ASLEEP) {
+ pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
+ map.pm_state,
+ map.wlock_holders);
+ map.pm_state = WCD9XXX_PM_SLEEPABLE;
+ } else {
+ pr_warn("%s: system is already awake, state %d wlock %d\n",
+ __func__, map.pm_state,
+ map.wlock_holders);
+ }
+ mutex_unlock(&map.pm_lock);
+ wake_up_all(&map.pm_wq);
+
+ return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_spmi_resume);
+
+bool wcd9xxx_spmi_lock_sleep(void)
+{
+ /*
+ * wcd9xxx_spmi_{lock/unlock}_sleep will be called by
+ * wcd9xxx_spmi_irq_thread
+ * and its subroutines only motly.
+ * but btn0_lpress_fn is not wcd9xxx_spmi_irq_thread's subroutine and
+ * It can race with wcd9xxx_spmi_irq_thread.
+ * So need to embrace wlock_holders with mutex.
+ */
+ mutex_lock(&map.pm_lock);
+ if (map.wlock_holders++ == 0) {
+ pr_debug("%s: holding wake lock\n", __func__);
+ pm_qos_update_request(&map.pm_qos_req,
+ msm_cpuidle_get_deep_idle_latency());
+ pm_stay_awake(&map.spmi[0]->dev);
+ }
+ mutex_unlock(&map.pm_lock);
+ pr_debug("%s: wake lock counter %d\n", __func__,
+ map.wlock_holders);
+ pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state);
+
+ if (!wait_event_timeout(map.pm_wq,
+ ((wcd9xxx_spmi_pm_cmpxchg(
+ WCD9XXX_PM_SLEEPABLE,
+ WCD9XXX_PM_AWAKE)) ==
+ WCD9XXX_PM_SLEEPABLE ||
+ (wcd9xxx_spmi_pm_cmpxchg(
+ WCD9XXX_PM_SLEEPABLE,
+ WCD9XXX_PM_AWAKE) ==
+ WCD9XXX_PM_AWAKE)),
+ msecs_to_jiffies(
+ WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) {
+ pr_warn("%s: system didn't resume within %dms, s %d, w %d\n",
+ __func__,
+ WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, map.pm_state,
+ map.wlock_holders);
+ wcd9xxx_spmi_unlock_sleep();
+ return false;
+ }
+ wake_up_all(&map.pm_wq);
+ pr_debug("%s: leaving pm_state = %d\n", __func__, map.pm_state);
+ return true;
+}
+EXPORT_SYMBOL(wcd9xxx_spmi_lock_sleep);
+
+void wcd9xxx_spmi_unlock_sleep(void)
+{
+ mutex_lock(&map.pm_lock);
+ if (--map.wlock_holders == 0) {
+ pr_debug("%s: releasing wake lock pm_state %d -> %d\n",
+ __func__, map.pm_state, WCD9XXX_PM_SLEEPABLE);
+ /*
+ * if wcd9xxx_spmi_lock_sleep failed, pm_state would be still
+ * WCD9XXX_PM_ASLEEP, don't overwrite
+ */
+ if (likely(map.pm_state == WCD9XXX_PM_AWAKE))
+ map.pm_state = WCD9XXX_PM_SLEEPABLE;
+ pm_qos_update_request(&map.pm_qos_req,
+ PM_QOS_DEFAULT_VALUE);
+ pm_relax(&map.spmi[0]->dev);
+ }
+ mutex_unlock(&map.pm_lock);
+ pr_debug("%s: wake lock counter %d\n", __func__,
+ map.wlock_holders);
+ pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state);
+ wake_up_all(&map.pm_wq);
+}
+EXPORT_SYMBOL(wcd9xxx_spmi_unlock_sleep);
+
+void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec)
+{
+ map.codec = codec;
+}
+
+void wcd9xxx_spmi_set_dev(struct platform_device *spmi, int i)
+{
+ if (i < NUM_IRQ_REGS)
+ map.spmi[i] = spmi;
+}
+
+int wcd9xxx_spmi_irq_init(void)
+{
+ int i = 0;
+
+ for (; i < MAX_NUM_IRQS; i++)
+ map.mask[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+ mutex_init(&map.pm_lock);
+ map.wlock_holders = 0;
+ map.pm_state = WCD9XXX_PM_SLEEPABLE;
+ init_waitqueue_head(&map.pm_wq);
+ pm_qos_add_request(&map.pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("MSM8x16 SPMI IRQ driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.h b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.h
new file mode 100644
index 000000000000..42596df0f65b
--- /dev/null
+++ b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_SPMI_IRQ_H__
+#define __WCD9XXX_SPMI_IRQ_H__
+
+#include <sound/soc.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/pm_qos.h>
+
+extern void wcd9xxx_spmi_enable_irq(int irq);
+extern void wcd9xxx_spmi_disable_irq(int irq);
+extern int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler,
+ const char *name, void *priv);
+extern int wcd9xxx_spmi_free_irq(int irq, void *priv);
+extern void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec);
+extern void wcd9xxx_spmi_set_dev(struct platform_device *spmi, int i);
+extern int wcd9xxx_spmi_irq_init(void);
+extern int wcd9xxx_spmi_suspend(pm_message_t);
+extern int wcd9xxx_spmi_resume(void);
+bool wcd9xxx_spmi_lock_sleep(void);
+void wcd9xxx_spmi_unlock_sleep(void);
+
+#endif
diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-cdc-registers.h b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-registers.h
new file mode 100644
index 000000000000..5e042e2d466a
--- /dev/null
+++ b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-registers.h
@@ -0,0 +1,603 @@
+ /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef SDM660_WCD_REGISTERS_H
+#define SDM660_WCD_REGISTERS_H
+
+#define CDC_DIG_BASE 0xF000
+#define CDC_ANA_BASE 0xF100
+
+#define MSM89XX_PMIC_DIGITAL_REVISION1 (CDC_DIG_BASE+0x000)
+#define MSM89XX_PMIC_DIGITAL_REVISION1__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_REVISION2 (CDC_DIG_BASE+0x001)
+#define MSM89XX_PMIC_DIGITAL_REVISION2__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_PERPH_TYPE (CDC_DIG_BASE+0x004)
+#define MSM89XX_PMIC_DIGITAL_PERPH_TYPE__POR (0x23)
+#define MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE (CDC_DIG_BASE+0x005)
+#define MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE__POR (0x01)
+#define MSM89XX_PMIC_DIGITAL_INT_RT_STS (CDC_DIG_BASE+0x010)
+#define MSM89XX_PMIC_DIGITAL_INT_RT_STS__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_SET_TYPE (CDC_DIG_BASE+0x011)
+#define MSM89XX_PMIC_DIGITAL_INT_SET_TYPE__POR (0xFF)
+#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH (CDC_DIG_BASE+0x012)
+#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH__POR (0xFF)
+#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW (CDC_DIG_BASE+0x013)
+#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR (CDC_DIG_BASE+0x014)
+#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_EN_SET (CDC_DIG_BASE+0x015)
+#define MSM89XX_PMIC_DIGITAL_INT_EN_SET__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_EN_CLR (CDC_DIG_BASE+0x016)
+#define MSM89XX_PMIC_DIGITAL_INT_EN_CLR__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS (CDC_DIG_BASE+0x018)
+#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_PENDING_STS (CDC_DIG_BASE+0x019)
+#define MSM89XX_PMIC_DIGITAL_INT_PENDING_STS__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_MID_SEL (CDC_DIG_BASE+0x01A)
+#define MSM89XX_PMIC_DIGITAL_INT_MID_SEL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_PRIORITY (CDC_DIG_BASE+0x01B)
+#define MSM89XX_PMIC_DIGITAL_INT_PRIORITY__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_GPIO_MODE (CDC_DIG_BASE+0x040)
+#define MSM89XX_PMIC_DIGITAL_GPIO_MODE__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_PIN_CTL_OE (CDC_DIG_BASE+0x041)
+#define MSM89XX_PMIC_DIGITAL_PIN_CTL_OE__POR (0x01)
+#define MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA (CDC_DIG_BASE+0x042)
+#define MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_PIN_STATUS (CDC_DIG_BASE+0x043)
+#define MSM89XX_PMIC_DIGITAL_PIN_STATUS__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_HDRIVE_CTL (CDC_DIG_BASE+0x044)
+#define MSM89XX_PMIC_DIGITAL_HDRIVE_CTL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_RST_CTL (CDC_DIG_BASE+0x046)
+#define MSM89XX_PMIC_DIGITAL_CDC_RST_CTL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL (CDC_DIG_BASE+0x048)
+#define MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL (CDC_DIG_BASE+0x049)
+#define MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL (CDC_DIG_BASE+0x04A)
+#define MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL (CDC_DIG_BASE+0x050)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL__POR (0x02)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL (CDC_DIG_BASE+0x051)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL__POR (0x02)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL (CDC_DIG_BASE+0x052)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL (CDC_DIG_BASE+0x053)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL (CDC_DIG_BASE+0x054)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL (CDC_DIG_BASE+0x055)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL (CDC_DIG_BASE+0x056)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1 (CDC_DIG_BASE+0x058)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1__POR (0x7C)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2 (CDC_DIG_BASE+0x059)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2__POR (0x7C)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3 (CDC_DIG_BASE+0x05A)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3__POR (0x7C)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0 (CDC_DIG_BASE+0x05B)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1 (CDC_DIG_BASE+0x05C)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2 (CDC_DIG_BASE+0x05D)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3 (CDC_DIG_BASE+0x05E)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL (CDC_DIG_BASE+0x068)
+#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN (CDC_DIG_BASE+0x069)
+#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_SPARE_0 (CDC_DIG_BASE+0x070)
+#define MSM89XX_PMIC_DIGITAL_SPARE_0__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_SPARE_1 (CDC_DIG_BASE+0x071)
+#define MSM89XX_PMIC_DIGITAL_SPARE_1__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_SPARE_2 (CDC_DIG_BASE+0x072)
+#define MSM89XX_PMIC_DIGITAL_SPARE_2__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_SEC_ACCESS (CDC_DIG_BASE+0x0D0)
+#define MSM89XX_PMIC_DIGITAL_SEC_ACCESS__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1 (CDC_DIG_BASE+0x0D8)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2 (CDC_DIG_BASE+0x0D9)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2__POR (0x01)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3 (CDC_DIG_BASE+0x0DA)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3__POR (0x05)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4 (CDC_DIG_BASE+0x0DB)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_TEST1 (CDC_DIG_BASE+0x0E0)
+#define MSM89XX_PMIC_DIGITAL_INT_TEST1__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_TEST_VAL (CDC_DIG_BASE+0x0E1)
+#define MSM89XX_PMIC_DIGITAL_INT_TEST_VAL__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_TRIM_NUM (CDC_DIG_BASE+0x0F0)
+#define MSM89XX_PMIC_DIGITAL_TRIM_NUM__POR (0x00)
+#define MSM89XX_PMIC_DIGITAL_TRIM_CTRL (CDC_DIG_BASE+0x0F1)
+#define MSM89XX_PMIC_DIGITAL_TRIM_CTRL__POR (0x00)
+
+#define MSM89XX_PMIC_ANALOG_REVISION1 (CDC_ANA_BASE+0x00)
+#define MSM89XX_PMIC_ANALOG_REVISION1__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_REVISION2 (CDC_ANA_BASE+0x01)
+#define MSM89XX_PMIC_ANALOG_REVISION2__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_REVISION3 (CDC_ANA_BASE+0x02)
+#define MSM89XX_PMIC_ANALOG_REVISION3__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_REVISION4 (CDC_ANA_BASE+0x03)
+#define MSM89XX_PMIC_ANALOG_REVISION4__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_PERPH_TYPE (CDC_ANA_BASE+0x04)
+#define MSM89XX_PMIC_ANALOG_PERPH_TYPE__POR (0x23)
+#define MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE (CDC_ANA_BASE+0x05)
+#define MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE__POR (0x09)
+#define MSM89XX_PMIC_ANALOG_INT_RT_STS (CDC_ANA_BASE+0x10)
+#define MSM89XX_PMIC_ANALOG_INT_RT_STS__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_INT_SET_TYPE (CDC_ANA_BASE+0x11)
+#define MSM89XX_PMIC_ANALOG_INT_SET_TYPE__POR (0x3F)
+#define MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH (CDC_ANA_BASE+0x12)
+#define MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH__POR (0x3F)
+#define MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW (CDC_ANA_BASE+0x13)
+#define MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR (CDC_ANA_BASE+0x14)
+#define MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_INT_EN_SET (CDC_ANA_BASE+0x15)
+#define MSM89XX_PMIC_ANALOG_INT_EN_SET__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_INT_EN_CLR (CDC_ANA_BASE+0x16)
+#define MSM89XX_PMIC_ANALOG_INT_EN_CLR__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_INT_LATCHED_STS (CDC_ANA_BASE+0x18)
+#define MSM89XX_PMIC_ANALOG_INT_LATCHED_STS__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_INT_PENDING_STS (CDC_ANA_BASE+0x19)
+#define MSM89XX_PMIC_ANALOG_INT_PENDING_STS__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_INT_MID_SEL (CDC_ANA_BASE+0x1A)
+#define MSM89XX_PMIC_ANALOG_INT_MID_SEL__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_INT_PRIORITY (CDC_ANA_BASE+0x1B)
+#define MSM89XX_PMIC_ANALOG_INT_PRIORITY__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_MICB_1_EN (CDC_ANA_BASE+0x40)
+#define MSM89XX_PMIC_ANALOG_MICB_1_EN__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_MICB_1_VAL (CDC_ANA_BASE+0x41)
+#define MSM89XX_PMIC_ANALOG_MICB_1_VAL__POR (0x20)
+#define MSM89XX_PMIC_ANALOG_MICB_1_CTL (CDC_ANA_BASE+0x42)
+#define MSM89XX_PMIC_ANALOG_MICB_1_CTL__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS (CDC_ANA_BASE+0x43)
+#define MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS__POR (0x49)
+#define MSM89XX_PMIC_ANALOG_MICB_2_EN (CDC_ANA_BASE+0x44)
+#define MSM89XX_PMIC_ANALOG_MICB_2_EN__POR (0x20)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2 (CDC_ANA_BASE+0x45)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL (CDC_ANA_BASE+0x46)
+#define MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1 (CDC_ANA_BASE+0x47)
+#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1__POR (0x35)
+#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2 (CDC_ANA_BASE+0x50)
+#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2__POR (0x08)
+#define MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL (CDC_ANA_BASE+0x51)
+#define MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER (CDC_ANA_BASE+0x52)
+#define MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER__POR (0x98)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL (CDC_ANA_BASE+0x53)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL (CDC_ANA_BASE+0x54)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL__POR (0x20)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL (CDC_ANA_BASE+0x55)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL__POR (0x40)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL (CDC_ANA_BASE+0x56)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL__POR (0x61)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL (CDC_ANA_BASE+0x57)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL__POR (0x80)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT (CDC_ANA_BASE+0x58)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT (CDC_ANA_BASE+0x59)
+#define MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_TX_1_EN (CDC_ANA_BASE+0x60)
+#define MSM89XX_PMIC_ANALOG_TX_1_EN__POR (0x03)
+#define MSM89XX_PMIC_ANALOG_TX_2_EN (CDC_ANA_BASE+0x61)
+#define MSM89XX_PMIC_ANALOG_TX_2_EN__POR (0x03)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1 (CDC_ANA_BASE+0x62)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1__POR (0xBF)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2 (CDC_ANA_BASE+0x63)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2__POR (0x8C)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL (CDC_ANA_BASE+0x64)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS (CDC_ANA_BASE+0x65)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS__POR (0x6B)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV (CDC_ANA_BASE+0x66)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV__POR (0x51)
+#define MSM89XX_PMIC_ANALOG_TX_3_EN (CDC_ANA_BASE+0x67)
+#define MSM89XX_PMIC_ANALOG_TX_3_EN__POR (0x02)
+#define MSM89XX_PMIC_ANALOG_NCP_EN (CDC_ANA_BASE+0x80)
+#define MSM89XX_PMIC_ANALOG_NCP_EN__POR (0x26)
+#define MSM89XX_PMIC_ANALOG_NCP_CLK (CDC_ANA_BASE+0x81)
+#define MSM89XX_PMIC_ANALOG_NCP_CLK__POR (0x23)
+#define MSM89XX_PMIC_ANALOG_NCP_DEGLITCH (CDC_ANA_BASE+0x82)
+#define MSM89XX_PMIC_ANALOG_NCP_DEGLITCH__POR (0x5B)
+#define MSM89XX_PMIC_ANALOG_NCP_FBCTRL (CDC_ANA_BASE+0x83)
+#define MSM89XX_PMIC_ANALOG_NCP_FBCTRL__POR (0x08)
+#define MSM89XX_PMIC_ANALOG_NCP_BIAS (CDC_ANA_BASE+0x84)
+#define MSM89XX_PMIC_ANALOG_NCP_BIAS__POR (0x29)
+#define MSM89XX_PMIC_ANALOG_NCP_VCTRL (CDC_ANA_BASE+0x85)
+#define MSM89XX_PMIC_ANALOG_NCP_VCTRL__POR (0x24)
+#define MSM89XX_PMIC_ANALOG_NCP_TEST (CDC_ANA_BASE+0x86)
+#define MSM89XX_PMIC_ANALOG_NCP_TEST__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR (CDC_ANA_BASE+0x87)
+#define MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR__POR (0xD5)
+#define MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER (CDC_ANA_BASE+0x90)
+#define MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER__POR (0xE8)
+#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL (CDC_ANA_BASE+0x91)
+#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL__POR (0xCF)
+#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT (CDC_ANA_BASE+0x92)
+#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT__POR (0x6E)
+#define MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC (CDC_ANA_BASE+0x93)
+#define MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC__POR (0x18)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA (CDC_ANA_BASE+0x94)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA__POR (0x5A)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP (CDC_ANA_BASE+0x95)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP__POR (0x69)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP (CDC_ANA_BASE+0x96)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP__POR (0x29)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN (CDC_ANA_BASE+0x97)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN__POR (0x80)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL (CDC_ANA_BASE+0x98)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL__POR (0xDA)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME (CDC_ANA_BASE+0x99)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME__POR (0x16)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST (CDC_ANA_BASE+0x9A)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL (CDC_ANA_BASE+0x9B)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL__POR (0x20)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST (CDC_ANA_BASE+0x9C)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL (CDC_ANA_BASE+0x9D)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL__POR (0x20)
+#define MSM89XX_PMIC_ANALOG_RX_EAR_CTL (CDC_ANA_BASE+0x9E)
+#define MSM89XX_PMIC_ANALOG_RX_EAR_CTL___POR (0x12)
+#define MSM89XX_PMIC_ANALOG_RX_ATEST (CDC_ANA_BASE+0x9F)
+#define MSM89XX_PMIC_ANALOG_RX_ATEST__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_STATUS (CDC_ANA_BASE+0xA0)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_STATUS__POR (0x0C)
+#define MSM89XX_PMIC_ANALOG_RX_EAR_STATUS (CDC_ANA_BASE+0xA1)
+#define MSM89XX_PMIC_ANALOG_RX_EAR_STATUS__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL (CDC_ANA_BASE+0xAC)
+#define MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL (CDC_ANA_BASE+0xAD)
+#define MSM89XX_PMIC_ANALOG_RX_RX_LO_EN_CTL__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL (CDC_ANA_BASE+0xB0)
+#define MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL__POR (0x83)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET (CDC_ANA_BASE+0xB1)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET__POR (0x91)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL (CDC_ANA_BASE+0xB2)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL__POR (0x29)
+#define MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET (CDC_ANA_BASE+0xB3)
+#define MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET__POR (0x4D)
+#define MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL (CDC_ANA_BASE+0xB4)
+#define MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL__POR (0xE1)
+#define MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL (CDC_ANA_BASE+0xB5)
+#define MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL__POR (0x1E)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC (CDC_ANA_BASE+0xB6)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC__POR (0xCB)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG (CDC_ANA_BASE+0xB7)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_CURRENT_LIMIT (CDC_ANA_BASE+0xC0)
+#define MSM89XX_PMIC_ANALOG_CURRENT_LIMIT__POR (0x02)
+#define MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE (CDC_ANA_BASE+0xC1)
+#define MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE__POR (0x14)
+#define MSM89XX_PMIC_ANALOG_BYPASS_MODE (CDC_ANA_BASE+0xC2)
+#define MSM89XX_PMIC_ANALOG_BYPASS_MODE__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_BOOST_EN_CTL (CDC_ANA_BASE+0xC3)
+#define MSM89XX_PMIC_ANALOG_BOOST_EN_CTL__POR (0x1F)
+#define MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO (CDC_ANA_BASE+0xC4)
+#define MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO__POR (0x8C)
+#define MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE (CDC_ANA_BASE+0xC5)
+#define MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE__POR (0xC0)
+#define MSM89XX_PMIC_ANALOG_BOOST_TEST1_1 (CDC_ANA_BASE+0xC6)
+#define MSM89XX_PMIC_ANALOG_BOOST_TEST1_1__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_BOOST_TEST_2 (CDC_ANA_BASE+0xC7)
+#define MSM89XX_PMIC_ANALOG_BOOST_TEST_2__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS (CDC_ANA_BASE+0xC8)
+#define MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS (CDC_ANA_BASE+0xC9)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR (CDC_ANA_BASE+0xCE)
+#define MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL (CDC_ANA_BASE+0xCF)
+#define MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_SEC_ACCESS (CDC_ANA_BASE+0xD0)
+#define MSM89XX_PMIC_ANALOG_SEC_ACCESS__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1 (CDC_ANA_BASE+0xD8)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2 (CDC_ANA_BASE+0xD9)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2__POR (0x01)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3 (CDC_ANA_BASE+0xDA)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3__POR (0x05)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4 (CDC_ANA_BASE+0xDB)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_INT_TEST1 (CDC_ANA_BASE+0xE0)
+#define MSM89XX_PMIC_ANALOG_INT_TEST1__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_INT_TEST_VAL (CDC_ANA_BASE+0xE1)
+#define MSM89XX_PMIC_ANALOG_INT_TEST_VAL__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_TRIM_NUM (CDC_ANA_BASE+0xF0)
+#define MSM89XX_PMIC_ANALOG_TRIM_NUM__POR (0x04)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL1 (CDC_ANA_BASE+0xF1)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL1__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL2 (CDC_ANA_BASE+0xF2)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL2__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL3 (CDC_ANA_BASE+0xF3)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL3__POR (0x00)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL4 (CDC_ANA_BASE+0xF4)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL4__POR (0x00)
+
+#define MSM89XX_PMIC_CDC_NUM_REGISTERS \
+ (MSM89XX_PMIC_ANALOG_TRIM_CTRL4+1)
+#define MSM89XX_PMIC_CDC_MAX_REGISTER \
+ (MSM89XX_PMIC_CDC_NUM_REGISTERS-1)
+#define MSM89XX_PMIC_CDC_CACHE_SIZE \
+ MSM89XX_PMIC_CDC_NUM_REGISTERS
+
+
+#define MSM89XX_CDC_CORE_CLK_RX_RESET_CTL (0x00)
+#define MSM89XX_CDC_CORE_CLK_RX_RESET_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL (0x04)
+#define MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL (0x08)
+#define MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CLK_RX_I2S_CTL (0x0C)
+#define MSM89XX_CDC_CORE_CLK_RX_I2S_CTL__POR (0x13)
+#define MSM89XX_CDC_CORE_CLK_TX_I2S_CTL (0x10)
+#define MSM89XX_CDC_CORE_CLK_TX_I2S_CTL__POR (0x13)
+#define MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL (0x14)
+#define MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL (0x18)
+#define MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CLK_OTHR_CTL (0x1C)
+#define MSM89XX_CDC_CORE_CLK_OTHR_CTL__POR (0x04)
+#define MSM89XX_CDC_CORE_CLK_RX_B1_CTL (0x20)
+#define MSM89XX_CDC_CORE_CLK_RX_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CLK_MCLK_CTL (0x24)
+#define MSM89XX_CDC_CORE_CLK_MCLK_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CLK_PDM_CTL (0x28)
+#define MSM89XX_CDC_CORE_CLK_PDM_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CLK_SD_CTL (0x2C)
+#define MSM89XX_CDC_CORE_CLK_SD_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL (0x30)
+#define MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CLK_RX_B2_CTL (0x34)
+#define MSM89XX_CDC_CORE_CLK_RX_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL (0x38)
+#define MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL__POR (0x13)
+#define MSM89XX_CDC_CORE_RX1_B1_CTL (0x40)
+#define MSM89XX_CDC_CORE_RX1_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX2_B1_CTL (0x60)
+#define MSM89XX_CDC_CORE_RX2_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX3_B1_CTL (0x80)
+#define MSM89XX_CDC_CORE_RX3_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX1_B2_CTL (0x44)
+#define MSM89XX_CDC_CORE_RX1_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX2_B2_CTL (0x64)
+#define MSM89XX_CDC_CORE_RX2_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX3_B2_CTL (0x84)
+#define MSM89XX_CDC_CORE_RX3_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX1_B3_CTL (0x48)
+#define MSM89XX_CDC_CORE_RX1_B3_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX2_B3_CTL (0x68)
+#define MSM89XX_CDC_CORE_RX2_B3_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX3_B3_CTL (0x88)
+#define MSM89XX_CDC_CORE_RX3_B3_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX1_B4_CTL (0x4C)
+#define MSM89XX_CDC_CORE_RX1_B4_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX2_B4_CTL (0x6C)
+#define MSM89XX_CDC_CORE_RX2_B4_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX3_B4_CTL (0x8C)
+#define MSM89XX_CDC_CORE_RX3_B4_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX1_B5_CTL (0x50)
+#define MSM89XX_CDC_CORE_RX1_B5_CTL__POR (0x68)
+#define MSM89XX_CDC_CORE_RX2_B5_CTL (0x70)
+#define MSM89XX_CDC_CORE_RX2_B5_CTL__POR (0x68)
+#define MSM89XX_CDC_CORE_RX3_B5_CTL (0x90)
+#define MSM89XX_CDC_CORE_RX3_B5_CTL__POR (0x68)
+#define MSM89XX_CDC_CORE_RX1_B6_CTL (0x54)
+#define MSM89XX_CDC_CORE_RX1_B6_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX2_B6_CTL (0x74)
+#define MSM89XX_CDC_CORE_RX2_B6_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX3_B6_CTL (0x94)
+#define MSM89XX_CDC_CORE_RX3_B6_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL (0x58)
+#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL (0x78)
+#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL (0x98)
+#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL (0x5C)
+#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL (0x7C)
+#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL (0x9C)
+#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_TOP_GAIN_UPDATE (0xA0)
+#define MSM89XX_CDC_CORE_TOP_GAIN_UPDATE__POR (0x00)
+#define MSM89XX_CDC_CORE_TOP_CTL (0xA4)
+#define MSM89XX_CDC_CORE_TOP_CTL__POR (0x01)
+#define MSM89XX_CDC_CORE_COMP0_B1_CTL (0xB0)
+#define MSM89XX_CDC_CORE_COMP0_B1_CTL__POR (0x30)
+#define MSM89XX_CDC_CORE_COMP0_B2_CTL (0xB4)
+#define MSM89XX_CDC_CORE_COMP0_B2_CTL__POR (0xB5)
+#define MSM89XX_CDC_CORE_COMP0_B3_CTL (0xB8)
+#define MSM89XX_CDC_CORE_COMP0_B3_CTL__POR (0x28)
+#define MSM89XX_CDC_CORE_COMP0_B4_CTL (0xBC)
+#define MSM89XX_CDC_CORE_COMP0_B4_CTL__POR (0x37)
+#define MSM89XX_CDC_CORE_COMP0_B5_CTL (0xC0)
+#define MSM89XX_CDC_CORE_COMP0_B5_CTL__POR (0x7F)
+#define MSM89XX_CDC_CORE_COMP0_B6_CTL (0xC4)
+#define MSM89XX_CDC_CORE_COMP0_B6_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS (0xC8)
+#define MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS__POR (0x03)
+#define MSM89XX_CDC_CORE_COMP0_FS_CFG (0xCC)
+#define MSM89XX_CDC_CORE_COMP0_FS_CFG__POR (0x03)
+#define MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL (0xD0)
+#define MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL__POR (0x02)
+#define MSM89XX_CDC_CORE_DEBUG_DESER1_CTL (0xE0)
+#define MSM89XX_CDC_CORE_DEBUG_DESER1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_DEBUG_DESER2_CTL (0xE4)
+#define MSM89XX_CDC_CORE_DEBUG_DESER2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG (0xE8)
+#define MSM89XX_CDC_CORE_DEBUG_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG (0xEC)
+#define MSM89XX_CDC_CORE_DEBUG_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG (0xF0)
+#define MSM89XX_CDC_CORE_DEBUG_B3_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL (0x100)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL (0x140)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL (0x104)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL (0x144)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL (0x108)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL (0x148)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL (0x10C)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL (0x14C)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL (0x110)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL (0x150)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL (0x114)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL (0x154)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL (0x118)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL (0x158)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL (0x11C)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL (0x15C)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR1_CTL (0x120)
+#define MSM89XX_CDC_CORE_IIR1_CTL__POR (0x40)
+#define MSM89XX_CDC_CORE_IIR2_CTL (0x160)
+#define MSM89XX_CDC_CORE_IIR2_CTL__POR (0x40)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL (0x124)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL (0x164)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL (0x128)
+#define MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL (0x168)
+#define MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL (0x12C)
+#define MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL (0x16C)
+#define MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_RX1_B1_CTL (0x180)
+#define MSM89XX_CDC_CORE_CONN_RX1_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_RX1_B2_CTL (0x184)
+#define MSM89XX_CDC_CORE_CONN_RX1_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_RX1_B3_CTL (0x188)
+#define MSM89XX_CDC_CORE_CONN_RX1_B3_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_RX2_B1_CTL (0x18C)
+#define MSM89XX_CDC_CORE_CONN_RX2_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_RX2_B2_CTL (0x190)
+#define MSM89XX_CDC_CORE_CONN_RX2_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_RX2_B3_CTL (0x194)
+#define MSM89XX_CDC_CORE_CONN_RX2_B3_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_RX3_B1_CTL (0x198)
+#define MSM89XX_CDC_CORE_CONN_RX3_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_RX3_B2_CTL (0x19C)
+#define MSM89XX_CDC_CORE_CONN_RX3_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_TX_B1_CTL (0x1A0)
+#define MSM89XX_CDC_CORE_CONN_TX_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_TX_B2_CTL (0x1A4)
+#define MSM89XX_CDC_CORE_CONN_TX_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL (0x1A8)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL (0x1AC)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL (0x1B0)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL (0x1B4)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL (0x1B8)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL (0x1BC)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL (0x1C0)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL (0x1C4)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL (0x1C8)
+#define MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_CONN_TX_B3_CTL (0x1CC)
+#define MSM89XX_CDC_CORE_CONN_TX_B3_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER (0x1E0)
+#define MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER__POR (0x00)
+#define MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN (0x1E4)
+#define MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN__POR (0x00)
+#define MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG (0x1E8)
+#define MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG__POR (0x00)
+#define MSM89XX_CDC_CORE_TX5_MUX_CTL (0x1EC)
+#define MSM89XX_CDC_CORE_TX5_MUX_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_TX5_CLK_FS_CTL (0x1F0)
+#define MSM89XX_CDC_CORE_TX5_CLK_FS_CTL__POR (0x03)
+#define MSM89XX_CDC_CORE_TX5_DMIC_CTL (0x1F4)
+#define MSM89XX_CDC_CORE_TX5_DMIC_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER (0x280)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER__POR (0x00)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER (0x2A0)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER__POR (0x00)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER (0x2C0)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER__POR (0x00)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER (0x2E0)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER__POR (0x00)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN (0x284)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN__POR (0x00)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN (0x2A4)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN__POR (0x00)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN (0x2C4)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN__POR (0x00)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN (0x2E4)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN__POR (0x00)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG (0x288)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG__POR (0x00)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG (0x2A8)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG__POR (0x00)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG (0x2C8)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG__POR (0x00)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG (0x2E8)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG__POR (0x00)
+#define MSM89XX_CDC_CORE_TX1_MUX_CTL (0x28C)
+#define MSM89XX_CDC_CORE_TX1_MUX_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_TX2_MUX_CTL (0x2AC)
+#define MSM89XX_CDC_CORE_TX2_MUX_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_TX3_MUX_CTL (0x2CC)
+#define MSM89XX_CDC_CORE_TX3_MUX_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_TX4_MUX_CTL (0x2EC)
+#define MSM89XX_CDC_CORE_TX4_MUX_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_TX1_CLK_FS_CTL (0x290)
+#define MSM89XX_CDC_CORE_TX1_CLK_FS_CTL__POR (0x03)
+#define MSM89XX_CDC_CORE_TX2_CLK_FS_CTL (0x2B0)
+#define MSM89XX_CDC_CORE_TX2_CLK_FS_CTL__POR (0x03)
+#define MSM89XX_CDC_CORE_TX3_CLK_FS_CTL (0x2D0)
+#define MSM89XX_CDC_CORE_TX3_CLK_FS_CTL__POR (0x03)
+#define MSM89XX_CDC_CORE_TX4_CLK_FS_CTL (0x2F0)
+#define MSM89XX_CDC_CORE_TX4_CLK_FS_CTL__POR (0x03)
+#define MSM89XX_CDC_CORE_TX1_DMIC_CTL (0x294)
+#define MSM89XX_CDC_CORE_TX1_DMIC_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_TX2_DMIC_CTL (0x2B4)
+#define MSM89XX_CDC_CORE_TX2_DMIC_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_TX3_DMIC_CTL (0x2D4)
+#define MSM89XX_CDC_CORE_TX3_DMIC_CTL__POR (0x00)
+#define MSM89XX_CDC_CORE_TX4_DMIC_CTL (0x2F4)
+#define MSM89XX_CDC_CORE_TX4_DMIC_CTL__POR (0x00)
+
+#define MSM89XX_CDC_CORE_NUM_REGISTERS \
+ (MSM89XX_CDC_CORE_TX4_DMIC_CTL+1)
+#define MSM89XX_CDC_CORE_MAX_REGISTER \
+ (MSM89XX_CDC_CORE_NUM_REGISTERS-1)
+#define MSM89XX_CDC_CORE_CACHE_SIZE \
+ MSM89XX_CDC_CORE_NUM_REGISTERS
+#endif
diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-regmap.c b/sound/soc/codecs/sdm660_cdc/sdm660-regmap.c
new file mode 100644
index 000000000000..7d8ac6df14bb
--- /dev/null
+++ b/sound/soc/codecs/sdm660_cdc/sdm660-regmap.c
@@ -0,0 +1,611 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include "msm-cdc-common.h"
+#include "sdm660-cdc-registers.h"
+
+/*
+ * Default register reset values that are common across different versions
+ * are defined here. If a register reset value is changed based on version
+ * then remove it from this structure and add it in version specific
+ * structures.
+ */
+struct reg_default
+ msm89xx_cdc_core_defaults[MSM89XX_CDC_CORE_CACHE_SIZE] = {
+ {MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x13},
+ {MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 0x13},
+ {MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CLK_OTHR_CTL, 0x04},
+ {MSM89XX_CDC_CORE_CLK_RX_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CLK_SD_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL, 0x13},
+ {MSM89XX_CDC_CORE_RX1_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX2_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX3_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX1_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX2_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX3_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX1_B3_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX2_B3_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX3_B3_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX1_B4_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX2_B4_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX3_B4_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX1_B5_CTL, 0x68},
+ {MSM89XX_CDC_CORE_RX2_B5_CTL, 0x68},
+ {MSM89XX_CDC_CORE_RX3_B5_CTL, 0x68},
+ {MSM89XX_CDC_CORE_RX1_B6_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX2_B6_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX3_B6_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_TOP_GAIN_UPDATE, 0x00},
+ {MSM89XX_CDC_CORE_TOP_CTL, 0x01},
+ {MSM89XX_CDC_CORE_COMP0_B1_CTL, 0x30},
+ {MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xB5},
+ {MSM89XX_CDC_CORE_COMP0_B3_CTL, 0x28},
+ {MSM89XX_CDC_CORE_COMP0_B4_CTL, 0x37},
+ {MSM89XX_CDC_CORE_COMP0_B5_CTL, 0x7F},
+ {MSM89XX_CDC_CORE_COMP0_B6_CTL, 0x00},
+ {MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS, 0x03},
+ {MSM89XX_CDC_CORE_COMP0_FS_CFG, 0x03},
+ {MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL, 0x02},
+ {MSM89XX_CDC_CORE_DEBUG_DESER1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_DEBUG_DESER2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG, 0x00},
+ {MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG, 0x00},
+ {MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG, 0x00},
+ {MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR1_CTL, 0x40},
+ {MSM89XX_CDC_CORE_IIR2_CTL, 0x40},
+ {MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_RX1_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_RX1_B3_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_RX2_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_RX2_B3_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_RX3_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_TX_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_TX_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, 0x00},
+ {MSM89XX_CDC_CORE_CONN_TX_B3_CTL, 0x00},
+ {MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER, 0x00},
+ {MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN, 0x00},
+ {MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG, 0x00},
+ {MSM89XX_CDC_CORE_TX5_MUX_CTL, 0x00},
+ {MSM89XX_CDC_CORE_TX5_CLK_FS_CTL, 0x03},
+ {MSM89XX_CDC_CORE_TX5_DMIC_CTL, 0x00},
+ {MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER, 0x00},
+ {MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER, 0x00},
+ {MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER, 0x00},
+ {MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER, 0x00},
+ {MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, 0x00},
+ {MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, 0x00},
+ {MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN, 0x00},
+ {MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN, 0x00},
+ {MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG, 0x00},
+ {MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG, 0x00},
+ {MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG, 0x00},
+ {MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG, 0x00},
+ {MSM89XX_CDC_CORE_TX1_MUX_CTL, 0x00},
+ {MSM89XX_CDC_CORE_TX2_MUX_CTL, 0x00},
+ {MSM89XX_CDC_CORE_TX3_MUX_CTL, 0x00},
+ {MSM89XX_CDC_CORE_TX4_MUX_CTL, 0x00},
+ {MSM89XX_CDC_CORE_TX1_CLK_FS_CTL, 0x03},
+ {MSM89XX_CDC_CORE_TX2_CLK_FS_CTL, 0x03},
+ {MSM89XX_CDC_CORE_TX3_CLK_FS_CTL, 0x03},
+ {MSM89XX_CDC_CORE_TX4_CLK_FS_CTL, 0x03},
+ {MSM89XX_CDC_CORE_TX1_DMIC_CTL, 0x00},
+ {MSM89XX_CDC_CORE_TX2_DMIC_CTL, 0x00},
+ {MSM89XX_CDC_CORE_TX3_DMIC_CTL, 0x00},
+ {MSM89XX_CDC_CORE_TX4_DMIC_CTL, 0x00},
+};
+
+struct reg_default
+ msm89xx_pmic_cdc_defaults[MSM89XX_PMIC_CDC_CACHE_SIZE] = {
+ {MSM89XX_PMIC_DIGITAL_REVISION1, 0x00},
+ {MSM89XX_PMIC_DIGITAL_REVISION2, 0x00},
+ {MSM89XX_PMIC_DIGITAL_PERPH_TYPE, 0x23},
+ {MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE, 0x01},
+ {MSM89XX_PMIC_DIGITAL_INT_RT_STS, 0x00},
+ {MSM89XX_PMIC_DIGITAL_INT_SET_TYPE, 0xFF},
+ {MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH, 0xFF},
+ {MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW, 0x00},
+ {MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR, 0x00},
+ {MSM89XX_PMIC_DIGITAL_INT_EN_SET, 0x00},
+ {MSM89XX_PMIC_DIGITAL_INT_EN_CLR, 0x00},
+ {MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS, 0x00},
+ {MSM89XX_PMIC_DIGITAL_INT_PENDING_STS, 0x00},
+ {MSM89XX_PMIC_DIGITAL_INT_MID_SEL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_INT_PRIORITY, 0x00},
+ {MSM89XX_PMIC_DIGITAL_GPIO_MODE, 0x00},
+ {MSM89XX_PMIC_DIGITAL_PIN_CTL_OE, 0x01},
+ {MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA, 0x00},
+ {MSM89XX_PMIC_DIGITAL_PIN_STATUS, 0x00},
+ {MSM89XX_PMIC_DIGITAL_HDRIVE_CTL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, 0x02},
+ {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, 0x02},
+ {MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1, 0x7C},
+ {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2, 0x7C},
+ {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3, 0x7C},
+ {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0, 0x00},
+ {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1, 0x00},
+ {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2, 0x00},
+ {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3, 0x00},
+ {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN, 0x00},
+ {MSM89XX_PMIC_DIGITAL_SPARE_0, 0x00},
+ {MSM89XX_PMIC_DIGITAL_SPARE_1, 0x00},
+ {MSM89XX_PMIC_DIGITAL_SPARE_2, 0x00},
+ {MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0x00},
+ {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1, 0x00},
+ {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2, 0x02},
+ {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x05},
+ {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00},
+ {MSM89XX_PMIC_DIGITAL_INT_TEST1, 0x00},
+ {MSM89XX_PMIC_DIGITAL_INT_TEST_VAL, 0x00},
+ {MSM89XX_PMIC_DIGITAL_TRIM_NUM, 0x00},
+ {MSM89XX_PMIC_DIGITAL_TRIM_CTRL, 0x00},
+ {MSM89XX_PMIC_ANALOG_REVISION1, 0x00},
+ {MSM89XX_PMIC_ANALOG_REVISION2, 0x00},
+ {MSM89XX_PMIC_ANALOG_REVISION3, 0x00},
+ {MSM89XX_PMIC_ANALOG_REVISION4, 0x00},
+ {MSM89XX_PMIC_ANALOG_PERPH_TYPE, 0x23},
+ {MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x09},
+ {MSM89XX_PMIC_ANALOG_INT_RT_STS, 0x00},
+ {MSM89XX_PMIC_ANALOG_INT_SET_TYPE, 0x3F},
+ {MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH, 0x3F},
+ {MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW, 0x00},
+ {MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR, 0x00},
+ {MSM89XX_PMIC_ANALOG_INT_EN_SET, 0x00},
+ {MSM89XX_PMIC_ANALOG_INT_EN_CLR, 0x00},
+ {MSM89XX_PMIC_ANALOG_INT_LATCHED_STS, 0x00},
+ {MSM89XX_PMIC_ANALOG_INT_PENDING_STS, 0x00},
+ {MSM89XX_PMIC_ANALOG_INT_MID_SEL, 0x00},
+ {MSM89XX_PMIC_ANALOG_INT_PRIORITY, 0x00},
+ {MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x00},
+ {MSM89XX_PMIC_ANALOG_MICB_1_VAL, 0x20},
+ {MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x00},
+ {MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, 0x49},
+ {MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20},
+ {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, 0x00},
+ {MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x00},
+ {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x35},
+ {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08},
+ {MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x00},
+ {MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x98},
+ {MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, 0x00},
+ {MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, 0x20},
+ {MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, 0x40},
+ {MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x61},
+ {MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL, 0x80},
+ {MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0x00},
+ {MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x00},
+ {MSM89XX_PMIC_ANALOG_TX_1_EN, 0x03},
+ {MSM89XX_PMIC_ANALOG_TX_2_EN, 0x03},
+ {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1, 0xBF},
+ {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2, 0x8C},
+ {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL, 0x00},
+ {MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x6B},
+ {MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, 0x51},
+ {MSM89XX_PMIC_ANALOG_TX_3_EN, 0x02},
+ {MSM89XX_PMIC_ANALOG_NCP_EN, 0x26},
+ {MSM89XX_PMIC_ANALOG_NCP_CLK, 0x23},
+ {MSM89XX_PMIC_ANALOG_NCP_DEGLITCH, 0x5B},
+ {MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x08},
+ {MSM89XX_PMIC_ANALOG_NCP_BIAS, 0x29},
+ {MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0x24},
+ {MSM89XX_PMIC_ANALOG_NCP_TEST, 0x00},
+ {MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR, 0xD5},
+ {MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER, 0xE8},
+ {MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xCF},
+ {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0x6E},
+ {MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x18},
+ {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0x5A},
+ {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP, 0x69},
+ {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, 0x29},
+ {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x80},
+ {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL, 0xDA},
+ {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0x16},
+ {MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x00},
+ {MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20},
+ {MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x00},
+ {MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20},
+ {MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12},
+ {MSM89XX_PMIC_ANALOG_RX_ATEST, 0x00},
+ {MSM89XX_PMIC_ANALOG_RX_HPH_STATUS, 0x0C},
+ {MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x00},
+ {MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x00},
+ {MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x00},
+ {MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x83},
+ {MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET, 0x91},
+ {MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x29},
+ {MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x4D},
+ {MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1},
+ {MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x1E},
+ {MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC, 0xCB},
+ {MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x00},
+ {MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x02},
+ {MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE, 0x14},
+ {MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x00},
+ {MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x1F},
+ {MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x8C},
+ {MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE, 0xC0},
+ {MSM89XX_PMIC_ANALOG_BOOST_TEST1_1, 0x00},
+ {MSM89XX_PMIC_ANALOG_BOOST_TEST_2, 0x00},
+ {MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS, 0x00},
+ {MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS, 0x00},
+ {MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR, 0x00},
+ {MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL, 0x00},
+ {MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0x00},
+ {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1, 0x00},
+ {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2, 0x01},
+ {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x05},
+ {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00},
+ {MSM89XX_PMIC_ANALOG_INT_TEST1, 0x00},
+ {MSM89XX_PMIC_ANALOG_INT_TEST_VAL, 0x00},
+ {MSM89XX_PMIC_ANALOG_TRIM_NUM, 0x04},
+ {MSM89XX_PMIC_ANALOG_TRIM_CTRL1, 0x00},
+ {MSM89XX_PMIC_ANALOG_TRIM_CTRL2, 0x00},
+ {MSM89XX_PMIC_ANALOG_TRIM_CTRL3, 0x00},
+ {MSM89XX_PMIC_ANALOG_TRIM_CTRL4, 0x00},
+};
+
+static const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE] = {
+ [MSM89XX_CDC_CORE_CLK_RX_RESET_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_RX_I2S_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_TX_I2S_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_OTHR_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_RX_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_MCLK_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_PDM_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_SD_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_RX_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_TOP_GAIN_UPDATE] = 1,
+ [MSM89XX_CDC_CORE_TOP_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS] = 1,
+ [MSM89XX_CDC_CORE_COMP0_FS_CFG] = 1,
+ [MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL] = 1,
+ [MSM89XX_CDC_CORE_DEBUG_DESER1_CTL] = 1,
+ [MSM89XX_CDC_CORE_DEBUG_DESER2_CTL] = 1,
+ [MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX1_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX1_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX1_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX2_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX2_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX2_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX3_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX3_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_TX_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_TX_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_TX_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER] = 1,
+ [MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER] = 1,
+ [MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER] = 1,
+ [MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER] = 1,
+ [MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN] = 1,
+ [MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN] = 1,
+ [MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN] = 1,
+ [MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN] = 1,
+ [MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_TX1_MUX_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX2_MUX_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX3_MUX_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX4_MUX_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX1_CLK_FS_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX2_CLK_FS_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX3_CLK_FS_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX4_CLK_FS_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER] = 1,
+ [MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN] = 1,
+ [MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_TX5_MUX_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX5_CLK_FS_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX5_DMIC_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX1_DMIC_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX2_DMIC_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX3_DMIC_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX4_DMIC_CTL] = 1,
+};
+
+static const u8 msm89xx_cdc_core_reg_writeable[MSM89XX_CDC_CORE_CACHE_SIZE] = {
+ [MSM89XX_CDC_CORE_CLK_RX_RESET_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_RX_I2S_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_TX_I2S_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_OTHR_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_RX_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_MCLK_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_PDM_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_SD_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_RX_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_TOP_GAIN_UPDATE] = 1,
+ [MSM89XX_CDC_CORE_TOP_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_COMP0_FS_CFG] = 1,
+ [MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL] = 1,
+ [MSM89XX_CDC_CORE_DEBUG_DESER1_CTL] = 1,
+ [MSM89XX_CDC_CORE_DEBUG_DESER2_CTL] = 1,
+ [MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX1_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX1_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX1_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX2_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX2_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX2_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX3_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_RX3_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_TX_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_TX_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL] = 1,
+ [MSM89XX_CDC_CORE_CONN_TX_B3_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER] = 1,
+ [MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER] = 1,
+ [MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER] = 1,
+ [MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER] = 1,
+ [MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN] = 1,
+ [MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN] = 1,
+ [MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN] = 1,
+ [MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN] = 1,
+ [MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_TX1_MUX_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX2_MUX_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX3_MUX_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX4_MUX_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX1_CLK_FS_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX2_CLK_FS_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX3_CLK_FS_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX4_CLK_FS_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER] = 1,
+ [MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN] = 1,
+ [MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG] = 1,
+ [MSM89XX_CDC_CORE_TX5_MUX_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX5_CLK_FS_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX5_DMIC_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX1_DMIC_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX2_DMIC_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX3_DMIC_CTL] = 1,
+ [MSM89XX_CDC_CORE_TX4_DMIC_CTL] = 1,
+};
+
+bool msm89xx_cdc_core_readable_reg(struct device *dev, unsigned int reg)
+{
+ return msm89xx_cdc_core_reg_readable[reg];
+}
+
+bool msm89xx_cdc_core_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return msm89xx_cdc_core_reg_writeable[reg];
+}
+
+bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MSM89XX_CDC_CORE_RX1_B1_CTL:
+ case MSM89XX_CDC_CORE_RX2_B1_CTL:
+ case MSM89XX_CDC_CORE_RX3_B1_CTL:
+ case MSM89XX_CDC_CORE_RX1_B6_CTL:
+ case MSM89XX_CDC_CORE_RX2_B6_CTL:
+ case MSM89XX_CDC_CORE_RX3_B6_CTL:
+ case MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG:
+ case MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG:
+ case MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG:
+ case MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG:
+ case MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG:
+ case MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL:
+ case MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL:
+ case MSM89XX_CDC_CORE_CLK_MCLK_CTL:
+ case MSM89XX_CDC_CORE_CLK_PDM_CTL:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c
new file mode 100644
index 000000000000..f995bf22c1c3
--- /dev/null
+++ b/sound/soc/codecs/wcd-dsp-mgr.c
@@ -0,0 +1,1231 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/stringify.h>
+#include <linux/of.h>
+#include <linux/debugfs.h>
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <soc/qcom/ramdump.h>
+#include <sound/wcd-dsp-mgr.h>
+#include "wcd-dsp-utils.h"
+
+/* Forward declarations */
+static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type);
+
+/* Component related macros */
+#define WDSP_GET_COMPONENT(wdsp, x) ((x >= WDSP_CMPNT_TYPE_MAX || x < 0) ? \
+ NULL : (&(wdsp->cmpnts[x])))
+#define WDSP_GET_CMPNT_TYPE_STR(x) wdsp_get_cmpnt_type_string(x)
+
+/*
+ * These #defines indicate the bit number in status field
+ * for each of the status. If bit is set, it indicates
+ * the status as done, else if bit is not set, it indicates
+ * the status is either failed or not done.
+ */
+#define WDSP_STATUS_INITIALIZED BIT(0)
+#define WDSP_STATUS_CODE_DLOADED BIT(1)
+#define WDSP_STATUS_DATA_DLOADED BIT(2)
+#define WDSP_STATUS_BOOTED BIT(3)
+
+/* Helper macros for printing wdsp messages */
+#define WDSP_ERR(wdsp, fmt, ...) \
+ dev_err(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
+#define WDSP_DBG(wdsp, fmt, ...) \
+ dev_dbg(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
+
+/* Helper macros for locking */
+#define WDSP_MGR_MUTEX_LOCK(wdsp, lock) \
+{ \
+ WDSP_DBG(wdsp, "mutex_lock(%s)", \
+ __stringify_1(lock)); \
+ mutex_lock(&lock); \
+}
+
+#define WDSP_MGR_MUTEX_UNLOCK(wdsp, lock) \
+{ \
+ WDSP_DBG(wdsp, "mutex_unlock(%s)", \
+ __stringify_1(lock)); \
+ mutex_unlock(&lock); \
+}
+
+/* Helper macros for using status mask */
+#define WDSP_SET_STATUS(wdsp, state) \
+{ \
+ wdsp->status |= state; \
+ WDSP_DBG(wdsp, "set 0x%lx, new_state = 0x%x", \
+ state, wdsp->status); \
+}
+
+#define WDSP_CLEAR_STATUS(wdsp, state) \
+{ \
+ wdsp->status &= (~state); \
+ WDSP_DBG(wdsp, "clear 0x%lx, new_state = 0x%x", \
+ state, wdsp->status); \
+}
+
+#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state)
+
+/* SSR relate status macros */
+#define WDSP_SSR_STATUS_WDSP_READY BIT(0)
+#define WDSP_SSR_STATUS_CDC_READY BIT(1)
+#define WDSP_SSR_STATUS_READY \
+ (WDSP_SSR_STATUS_WDSP_READY | WDSP_SSR_STATUS_CDC_READY)
+#define WDSP_SSR_READY_WAIT_TIMEOUT (10 * HZ)
+
+enum wdsp_ssr_type {
+
+ /* Init value, indicates there is no SSR in progress */
+ WDSP_SSR_TYPE_NO_SSR = 0,
+
+ /*
+ * Indicates WDSP crashed. The manager driver internally
+ * decides when to perform WDSP restart based on the
+ * users of wdsp. Hence there is no explicit WDSP_UP.
+ */
+ WDSP_SSR_TYPE_WDSP_DOWN,
+
+ /* Indicates codec hardware is down */
+ WDSP_SSR_TYPE_CDC_DOWN,
+
+ /* Indicates codec hardware is up, trigger to restart WDSP */
+ WDSP_SSR_TYPE_CDC_UP,
+};
+
+struct wdsp_cmpnt {
+
+ /* OF node of the phandle */
+ struct device_node *np;
+
+ /*
+ * Child component's dev_name, should be set in DT for the child's
+ * phandle if child's dev->of_node does not match the phandle->of_node
+ */
+ const char *cdev_name;
+
+ /* Child component's device node */
+ struct device *cdev;
+
+ /* Private data that component may want back on callbacks */
+ void *priv_data;
+
+ /* Child ops */
+ struct wdsp_cmpnt_ops *ops;
+};
+
+struct wdsp_ramdump_data {
+
+ /* Ramdump device */
+ void *rd_dev;
+
+ /* DMA address of the dump */
+ dma_addr_t rd_addr;
+
+ /* Virtual address of the dump */
+ void *rd_v_addr;
+
+ /* Data provided through error interrupt */
+ struct wdsp_err_signal_arg err_data;
+};
+
+struct wdsp_mgr_priv {
+
+ /* Manager driver's struct device pointer */
+ struct device *mdev;
+
+ /* Match struct for component framework */
+ struct component_match *match;
+
+ /* Manager's ops/function callbacks */
+ struct wdsp_mgr_ops *ops;
+
+ /* Array to store information for all expected components */
+ struct wdsp_cmpnt cmpnts[WDSP_CMPNT_TYPE_MAX];
+
+ /* The filename of image to be downloaded */
+ const char *img_fname;
+
+ /* Keeps track of current state of manager driver */
+ u32 status;
+
+ /* Work to load the firmware image after component binding */
+ struct work_struct load_fw_work;
+
+ /* List of segments in image to be downloaded */
+ struct list_head *seg_list;
+
+ /* Base address of the image in memory */
+ u32 base_addr;
+
+ /* Instances using dsp */
+ int dsp_users;
+
+ /* Lock for serializing ops called by components */
+ struct mutex api_mutex;
+
+ struct wdsp_ramdump_data dump_data;
+
+ /* SSR related */
+ enum wdsp_ssr_type ssr_type;
+ struct mutex ssr_mutex;
+ struct work_struct ssr_work;
+ u16 ready_status;
+ struct completion ready_compl;
+
+ /* Debugfs related */
+ struct dentry *entry;
+ bool panic_on_error;
+};
+
+static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type)
+{
+ switch (type) {
+ case WDSP_SSR_TYPE_NO_SSR:
+ return "NO_SSR";
+ case WDSP_SSR_TYPE_WDSP_DOWN:
+ return "WDSP_DOWN";
+ case WDSP_SSR_TYPE_CDC_DOWN:
+ return "CDC_DOWN";
+ case WDSP_SSR_TYPE_CDC_UP:
+ return "CDC_UP";
+ default:
+ pr_err("%s: Invalid ssr_type %d\n",
+ __func__, type);
+ return "Invalid";
+ }
+}
+
+static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type)
+{
+ switch (type) {
+ case WDSP_CMPNT_CONTROL:
+ return "control";
+ case WDSP_CMPNT_IPC:
+ return "ipc";
+ case WDSP_CMPNT_TRANSPORT:
+ return "transport";
+ default:
+ pr_err("%s: Invalid component type %d\n",
+ __func__, type);
+ return "Invalid";
+ }
+}
+
+static void __wdsp_clr_ready_locked(struct wdsp_mgr_priv *wdsp,
+ u16 value)
+{
+ wdsp->ready_status &= ~(value);
+ WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status);
+}
+
+static void __wdsp_set_ready_locked(struct wdsp_mgr_priv *wdsp,
+ u16 value, bool mark_complete)
+{
+ wdsp->ready_status |= value;
+ WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status);
+
+ if (mark_complete &&
+ wdsp->ready_status == WDSP_SSR_STATUS_READY) {
+ WDSP_DBG(wdsp, "marking ready completion");
+ complete(&wdsp->ready_compl);
+ }
+}
+
+static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp,
+ enum wdsp_event_type event,
+ void *data)
+{
+ struct wdsp_cmpnt *cmpnt;
+ int i;
+
+ for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+ if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler)
+ cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
+ event, data);
+ }
+}
+
+static void wdsp_broadcast_event_downseq(struct wdsp_mgr_priv *wdsp,
+ enum wdsp_event_type event,
+ void *data)
+{
+ struct wdsp_cmpnt *cmpnt;
+ int i;
+
+ for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+ if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler)
+ cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
+ event, data);
+ }
+}
+
+static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp,
+ enum wdsp_cmpnt_type type,
+ enum wdsp_event_type event,
+ void *data)
+{
+ struct wdsp_cmpnt *cmpnt;
+ int ret;
+
+ cmpnt = WDSP_GET_COMPONENT(wdsp, type);
+ if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) {
+ ret = cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
+ event, data);
+ } else {
+ WDSP_ERR(wdsp, "not valid event_handler for %s",
+ WDSP_GET_CMPNT_TYPE_STR(type));
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void wdsp_deinit_components(struct wdsp_mgr_priv *wdsp)
+{
+ struct wdsp_cmpnt *cmpnt;
+ int i;
+
+ for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+ if (cmpnt && cmpnt->ops && cmpnt->ops->deinit)
+ cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data);
+ }
+}
+
+static int wdsp_init_components(struct wdsp_mgr_priv *wdsp)
+{
+ struct wdsp_cmpnt *cmpnt;
+ int fail_idx = WDSP_CMPNT_TYPE_MAX;
+ int i, ret = 0;
+
+ for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
+
+ cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+
+ /* Init is allowed to be NULL */
+ if (!cmpnt->ops || !cmpnt->ops->init)
+ continue;
+ ret = cmpnt->ops->init(cmpnt->cdev, cmpnt->priv_data);
+ if (ret) {
+ WDSP_ERR(wdsp, "Init failed (%d) for component %s",
+ ret, WDSP_GET_CMPNT_TYPE_STR(i));
+ fail_idx = i;
+ break;
+ }
+ }
+
+ if (fail_idx < WDSP_CMPNT_TYPE_MAX) {
+ /* Undo init for already initialized components */
+ for (i = fail_idx - 1; i >= 0; i--) {
+ struct wdsp_cmpnt *cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+
+ if (cmpnt->ops && cmpnt->ops->deinit)
+ cmpnt->ops->deinit(cmpnt->cdev,
+ cmpnt->priv_data);
+ }
+ } else {
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_INIT, NULL);
+ }
+
+ return ret;
+}
+
+static int wdsp_load_each_segment(struct wdsp_mgr_priv *wdsp,
+ struct wdsp_img_segment *seg)
+{
+ struct wdsp_img_section img_section;
+ int ret;
+
+ WDSP_DBG(wdsp,
+ "base_addr 0x%x, split_fname %s, load_addr 0x%x, size 0x%zx",
+ wdsp->base_addr, seg->split_fname, seg->load_addr, seg->size);
+
+ if (seg->load_addr < wdsp->base_addr) {
+ WDSP_ERR(wdsp, "Invalid addr 0x%x, base_addr = 0x%x",
+ seg->load_addr, wdsp->base_addr);
+ return -EINVAL;
+ }
+
+ img_section.addr = seg->load_addr - wdsp->base_addr;
+ img_section.size = seg->size;
+ img_section.data = seg->data;
+
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT,
+ WDSP_EVENT_DLOAD_SECTION,
+ &img_section);
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp,
+ "Failed, err = %d for base_addr = 0x%x split_fname = %s, load_addr = 0x%x, size = 0x%zx",
+ ret, wdsp->base_addr, seg->split_fname,
+ seg->load_addr, seg->size);
+ return ret;
+}
+
+static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp,
+ unsigned int type)
+{
+ struct wdsp_cmpnt *ctl;
+ struct wdsp_img_segment *seg = NULL;
+ enum wdsp_event_type pre, post;
+ long status;
+ int ret;
+
+ ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL);
+
+ if (type == WDSP_ELF_FLAG_RE) {
+ pre = WDSP_EVENT_PRE_DLOAD_CODE;
+ post = WDSP_EVENT_POST_DLOAD_CODE;
+ status = WDSP_STATUS_CODE_DLOADED;
+ } else if (type == WDSP_ELF_FLAG_WRITE) {
+ pre = WDSP_EVENT_PRE_DLOAD_DATA;
+ post = WDSP_EVENT_POST_DLOAD_DATA;
+ status = WDSP_STATUS_DATA_DLOADED;
+ } else {
+ WDSP_ERR(wdsp, "Invalid type %u", type);
+ return -EINVAL;
+ }
+
+ ret = wdsp_get_segment_list(ctl->cdev, wdsp->img_fname,
+ type, wdsp->seg_list, &wdsp->base_addr);
+ if (IS_ERR_VALUE(ret) ||
+ list_empty(wdsp->seg_list)) {
+ WDSP_ERR(wdsp, "Error %d to get image segments for type %d",
+ ret, type);
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED,
+ NULL);
+ goto done;
+ }
+
+ /* Notify all components that image is about to be downloaded */
+ wdsp_broadcast_event_upseq(wdsp, pre, NULL);
+
+ /* Go through the list of segments and download one by one */
+ list_for_each_entry(seg, wdsp->seg_list, list) {
+ ret = wdsp_load_each_segment(wdsp, seg);
+ if (ret)
+ goto dload_error;
+ }
+
+ /* Flush the list before setting status and notifying components */
+ wdsp_flush_segment_list(wdsp->seg_list);
+
+ WDSP_SET_STATUS(wdsp, status);
+
+ /* Notify all components that image is downloaded */
+ wdsp_broadcast_event_downseq(wdsp, post, NULL);
+done:
+ return ret;
+
+dload_error:
+ wdsp_flush_segment_list(wdsp->seg_list);
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED, NULL);
+
+ return ret;
+}
+
+static int wdsp_init_and_dload_code_sections(struct wdsp_mgr_priv *wdsp)
+{
+ int ret;
+ bool is_initialized;
+
+ is_initialized = WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_INITIALIZED);
+
+ if (!is_initialized) {
+ /* Components are not initialized yet, initialize them */
+ ret = wdsp_init_components(wdsp);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "INIT failed, err = %d", ret);
+ goto done;
+ }
+ WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
+ }
+
+ /* Download the read-execute sections of image */
+ ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Error %d to download code sections", ret);
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static void wdsp_load_fw_image(struct work_struct *work)
+{
+ struct wdsp_mgr_priv *wdsp;
+ int ret;
+
+ wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work);
+ if (!wdsp) {
+ pr_err("%s: Invalid private_data\n", __func__);
+ return;
+ }
+
+ ret = wdsp_init_and_dload_code_sections(wdsp);
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp, "dload code sections failed, err = %d", ret);
+}
+
+static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp)
+{
+ int ret;
+
+ /* Make sure wdsp is in good state */
+ if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) {
+ WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status);
+ return -EINVAL;
+ }
+
+ /*
+ * Acquire SSR mutex lock to make sure enablement of DSP
+ * does not race with SSR handling.
+ */
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+ /* Download the read-write sections of image */
+ ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Data section download failed, err = %d", ret);
+ goto done;
+ }
+
+ wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL);
+
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
+ WDSP_EVENT_DO_BOOT, NULL);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Failed to boot dsp, err = %d", ret);
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
+ goto done;
+ }
+
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL);
+ WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED);
+done:
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+ return ret;
+}
+
+static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp)
+{
+ int ret;
+
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+
+ /*
+ * If Disable happened while SSR is in progress, then set the SSR
+ * ready status indicating WDSP is now ready. Ignore the disable
+ * event here and let the SSR handler go through shutdown.
+ */
+ if (wdsp->ssr_type != WDSP_SSR_TYPE_NO_SSR) {
+ __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY, true);
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+ return 0;
+ }
+
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+
+ /* Make sure wdsp is in good state */
+ if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
+ WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, NULL);
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
+ WDSP_EVENT_DO_SHUTDOWN, NULL);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Failed to shutdown dsp, err = %d", ret);
+ goto done;
+ }
+
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL);
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED);
+
+ /* Data sections are to be downloaded per boot */
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
+done:
+ return ret;
+}
+
+static int wdsp_register_cmpnt_ops(struct device *wdsp_dev,
+ struct device *cdev,
+ void *priv_data,
+ struct wdsp_cmpnt_ops *ops)
+{
+ struct wdsp_mgr_priv *wdsp;
+ struct wdsp_cmpnt *cmpnt;
+ int i, ret;
+
+ if (!wdsp_dev || !cdev || !ops)
+ return -EINVAL;
+
+ wdsp = dev_get_drvdata(wdsp_dev);
+
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
+
+ for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+ if ((cdev->of_node && cdev->of_node == cmpnt->np) ||
+ (cmpnt->cdev_name &&
+ !strcmp(dev_name(cdev), cmpnt->cdev_name))) {
+ break;
+ }
+ }
+
+ if (i == WDSP_CMPNT_TYPE_MAX) {
+ WDSP_ERR(wdsp, "Failed to register component dev %s",
+ dev_name(cdev));
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cmpnt->cdev = cdev;
+ cmpnt->ops = ops;
+ cmpnt->priv_data = priv_data;
+done:
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
+ return 0;
+}
+
+static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev,
+ enum wdsp_cmpnt_type type)
+{
+ struct wdsp_mgr_priv *wdsp;
+ struct wdsp_cmpnt *cmpnt;
+
+ if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX)
+ return NULL;
+
+ wdsp = dev_get_drvdata(wdsp_dev);
+ cmpnt = WDSP_GET_COMPONENT(wdsp, type);
+
+ return cmpnt->cdev;
+}
+
+static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp)
+{
+ struct wdsp_img_section img_section;
+ struct wdsp_err_signal_arg *data = &wdsp->dump_data.err_data;
+ struct ramdump_segment rd_seg;
+ int ret = 0;
+
+ if (wdsp->ssr_type != WDSP_SSR_TYPE_WDSP_DOWN ||
+ !data->mem_dumps_enabled) {
+ WDSP_DBG(wdsp, "cannot dump memory, ssr_type %s, dumps %s",
+ wdsp_get_ssr_type_string(wdsp->ssr_type),
+ !(data->mem_dumps_enabled) ? "disabled" : "enabled");
+ goto done;
+ }
+
+ if (data->dump_size == 0 ||
+ data->remote_start_addr < wdsp->base_addr) {
+ WDSP_ERR(wdsp, "Invalid start addr 0x%x or dump_size 0x%zx",
+ data->remote_start_addr, data->dump_size);
+ goto done;
+ }
+
+ if (!wdsp->dump_data.rd_dev) {
+ WDSP_ERR(wdsp, "Ramdump device is not setup");
+ goto done;
+ }
+
+ WDSP_DBG(wdsp, "base_addr 0x%x, dump_start_addr 0x%x, dump_size 0x%zx",
+ wdsp->base_addr, data->remote_start_addr, data->dump_size);
+
+ /* Allocate memory for dumps */
+ wdsp->dump_data.rd_v_addr = dma_alloc_coherent(wdsp->mdev,
+ data->dump_size,
+ &wdsp->dump_data.rd_addr,
+ GFP_KERNEL);
+ if (!wdsp->dump_data.rd_v_addr)
+ goto done;
+
+ img_section.addr = data->remote_start_addr - wdsp->base_addr;
+ img_section.size = data->dump_size;
+ img_section.data = wdsp->dump_data.rd_v_addr;
+
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT,
+ WDSP_EVENT_READ_SECTION,
+ &img_section);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Failed to read dumps, size 0x%zx at addr 0x%x",
+ img_section.size, img_section.addr);
+ goto err_read_dumps;
+ }
+
+ /*
+ * If panic_on_error flag is explicitly set through the debugfs,
+ * then cause a BUG here to aid debugging.
+ */
+ BUG_ON(wdsp->panic_on_error);
+
+ rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr;
+ rd_seg.size = img_section.size;
+ rd_seg.v_address = wdsp->dump_data.rd_v_addr;
+
+ ret = do_ramdump(wdsp->dump_data.rd_dev, &rd_seg, 1);
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp, "do_ramdump failed with error %d", ret);
+
+err_read_dumps:
+ dma_free_coherent(wdsp->mdev, data->dump_size,
+ wdsp->dump_data.rd_v_addr, wdsp->dump_data.rd_addr);
+done:
+ return;
+}
+
+static void wdsp_ssr_work_fn(struct work_struct *work)
+{
+ struct wdsp_mgr_priv *wdsp;
+ int ret;
+
+ wdsp = container_of(work, struct wdsp_mgr_priv, ssr_work);
+ if (!wdsp) {
+ pr_err("%s: Invalid private_data\n", __func__);
+ return;
+ }
+
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+
+ /* Issue ramdumps and shutdown only if DSP is currently booted */
+ if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
+ wdsp_collect_ramdumps(wdsp);
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
+ WDSP_EVENT_DO_SHUTDOWN, NULL);
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp, "Failed WDSP shutdown, err = %d", ret);
+
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN,
+ NULL);
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED);
+ }
+
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+ ret = wait_for_completion_timeout(&wdsp->ready_compl,
+ WDSP_SSR_READY_WAIT_TIMEOUT);
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+ if (ret == 0) {
+ WDSP_ERR(wdsp, "wait_for_ready timed out, status = 0x%x",
+ wdsp->ready_status);
+ goto done;
+ }
+
+ /* Data sections are to downloaded per WDSP boot */
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
+
+ /*
+ * Even though code section could possible be retained on DSP
+ * crash, go ahead and still re-download just to avoid any
+ * memory corruption from previous crash.
+ */
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED);
+
+ /* If codec restarted, then all components must be re-initialized */
+ if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_UP) {
+ wdsp_deinit_components(wdsp);
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
+ }
+
+ ret = wdsp_init_and_dload_code_sections(wdsp);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Failed to dload code sections err = %d",
+ ret);
+ goto done;
+ }
+
+ /* SSR handling is finished, mark SSR type as NO_SSR */
+ wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR;
+done:
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+}
+
+static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg,
+ enum wdsp_ssr_type ssr_type)
+{
+ enum wdsp_ssr_type current_ssr_type;
+ struct wdsp_err_signal_arg *err_data;
+
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+
+ current_ssr_type = wdsp->ssr_type;
+ WDSP_DBG(wdsp, "Current ssr_type %s, handling ssr_type %s",
+ wdsp_get_ssr_type_string(current_ssr_type),
+ wdsp_get_ssr_type_string(ssr_type));
+ wdsp->ssr_type = ssr_type;
+
+ if (arg) {
+ err_data = (struct wdsp_err_signal_arg *) arg;
+ memcpy(&wdsp->dump_data.err_data, err_data,
+ sizeof(*err_data));
+ } else {
+ memset(&wdsp->dump_data.err_data, 0,
+ sizeof(wdsp->dump_data.err_data));
+ }
+
+ switch (ssr_type) {
+
+ case WDSP_SSR_TYPE_WDSP_DOWN:
+ __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY);
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN,
+ NULL);
+ schedule_work(&wdsp->ssr_work);
+ break;
+
+ case WDSP_SSR_TYPE_CDC_DOWN:
+ __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY);
+ /*
+ * If DSP is booted when CDC_DOWN is received, it needs
+ * to be shutdown.
+ */
+ if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
+ __wdsp_clr_ready_locked(wdsp,
+ WDSP_SSR_STATUS_WDSP_READY);
+ wdsp_broadcast_event_downseq(wdsp,
+ WDSP_EVENT_PRE_SHUTDOWN,
+ NULL);
+ }
+
+ schedule_work(&wdsp->ssr_work);
+ break;
+
+ case WDSP_SSR_TYPE_CDC_UP:
+ __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY, true);
+ break;
+
+ default:
+ WDSP_ERR(wdsp, "undefined ssr_type %d\n", ssr_type);
+ /* Revert back the ssr_type for undefined events */
+ wdsp->ssr_type = current_ssr_type;
+ break;
+ }
+
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+
+ return 0;
+}
+
+static int wdsp_signal_handler(struct device *wdsp_dev,
+ enum wdsp_signal signal, void *arg)
+{
+ struct wdsp_mgr_priv *wdsp;
+ int ret;
+
+ if (!wdsp_dev)
+ return -EINVAL;
+
+ wdsp = dev_get_drvdata(wdsp_dev);
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
+
+ WDSP_DBG(wdsp, "Raised signal %d", signal);
+
+ switch (signal) {
+ case WDSP_IPC1_INTR:
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC,
+ WDSP_EVENT_IPC1_INTR, NULL);
+ break;
+ case WDSP_ERR_INTR:
+ ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_WDSP_DOWN);
+ break;
+ case WDSP_CDC_DOWN_SIGNAL:
+ ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_DOWN);
+ break;
+ case WDSP_CDC_UP_SIGNAL:
+ ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_UP);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp, "handling signal %d failed with error %d",
+ signal, ret);
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
+
+ return ret;
+}
+
+static int wdsp_vote_for_dsp(struct device *wdsp_dev,
+ bool vote)
+{
+ struct wdsp_mgr_priv *wdsp;
+ int ret = 0;
+
+ if (!wdsp_dev)
+ return -EINVAL;
+
+ wdsp = dev_get_drvdata(wdsp_dev);
+
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
+ WDSP_DBG(wdsp, "request %s, current users = %d",
+ vote ? "enable" : "disable", wdsp->dsp_users);
+
+ if (vote) {
+ wdsp->dsp_users++;
+ if (wdsp->dsp_users == 1)
+ ret = wdsp_enable_dsp(wdsp);
+ } else {
+ if (wdsp->dsp_users == 0)
+ goto done;
+
+ wdsp->dsp_users--;
+ if (wdsp->dsp_users == 0)
+ ret = wdsp_disable_dsp(wdsp);
+ }
+
+ if (IS_ERR_VALUE(ret))
+ WDSP_DBG(wdsp, "wdsp %s failed, err = %d",
+ vote ? "enable" : "disable", ret);
+
+done:
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
+ return ret;
+}
+
+static int wdsp_suspend(struct device *wdsp_dev)
+{
+ struct wdsp_mgr_priv *wdsp;
+ int rc = 0, i;
+
+ if (!wdsp_dev) {
+ pr_err("%s: Invalid handle to device\n", __func__);
+ return -EINVAL;
+ }
+
+ wdsp = dev_get_drvdata(wdsp_dev);
+
+ for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) {
+ rc = wdsp_unicast_event(wdsp, i, WDSP_EVENT_SUSPEND, NULL);
+ if (rc < 0) {
+ WDSP_ERR(wdsp, "component %s failed to suspend\n",
+ WDSP_GET_CMPNT_TYPE_STR(i));
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int wdsp_resume(struct device *wdsp_dev)
+{
+ struct wdsp_mgr_priv *wdsp;
+ int rc = 0, i;
+
+ if (!wdsp_dev) {
+ pr_err("%s: Invalid handle to device\n", __func__);
+ return -EINVAL;
+ }
+
+ wdsp = dev_get_drvdata(wdsp_dev);
+
+ for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
+ rc = wdsp_unicast_event(wdsp, i, WDSP_EVENT_RESUME, NULL);
+ if (rc < 0) {
+ WDSP_ERR(wdsp, "component %s failed to resume\n",
+ WDSP_GET_CMPNT_TYPE_STR(i));
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static struct wdsp_mgr_ops wdsp_ops = {
+ .register_cmpnt_ops = wdsp_register_cmpnt_ops,
+ .get_dev_for_cmpnt = wdsp_get_dev_for_cmpnt,
+ .signal_handler = wdsp_signal_handler,
+ .vote_for_dsp = wdsp_vote_for_dsp,
+ .suspend = wdsp_suspend,
+ .resume = wdsp_resume,
+};
+
+static int wdsp_mgr_compare_of(struct device *dev, void *data)
+{
+ struct wdsp_cmpnt *cmpnt = data;
+
+ /*
+ * First try to match based on of_node, if of_node is not
+ * present, try to match on the dev_name
+ */
+ return ((dev->of_node && dev->of_node == cmpnt->np) ||
+ (cmpnt->cdev_name &&
+ !strcmp(dev_name(dev), cmpnt->cdev_name)));
+}
+
+static void wdsp_mgr_debugfs_init(struct wdsp_mgr_priv *wdsp)
+{
+ wdsp->entry = debugfs_create_dir("wdsp_mgr", NULL);
+ if (IS_ERR_OR_NULL(wdsp->entry))
+ return;
+
+ debugfs_create_bool("panic_on_error", S_IRUGO | S_IWUSR,
+ wdsp->entry, &wdsp->panic_on_error);
+}
+
+static void wdsp_mgr_debugfs_remove(struct wdsp_mgr_priv *wdsp)
+{
+ debugfs_remove_recursive(wdsp->entry);
+ wdsp->entry = NULL;
+}
+
+static int wdsp_mgr_bind(struct device *dev)
+{
+ struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev);
+ struct wdsp_cmpnt *cmpnt;
+ int ret, idx;
+
+ wdsp->ops = &wdsp_ops;
+
+ /* Setup ramdump device */
+ wdsp->dump_data.rd_dev = create_ramdump_device("wdsp", dev);
+ if (!wdsp->dump_data.rd_dev)
+ dev_info(dev, "%s: create_ramdump_device failed\n", __func__);
+
+ ret = component_bind_all(dev, wdsp->ops);
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret);
+
+ /* Make sure all components registered ops */
+ for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, idx);
+ if (!cmpnt->cdev || !cmpnt->ops) {
+ WDSP_ERR(wdsp, "%s did not register ops\n",
+ WDSP_GET_CMPNT_TYPE_STR(idx));
+ ret = -EINVAL;
+ component_unbind_all(dev, wdsp->ops);
+ break;
+ }
+ }
+
+ wdsp_mgr_debugfs_init(wdsp);
+
+ /* Schedule the work to download image if binding was successful. */
+ if (!ret)
+ schedule_work(&wdsp->load_fw_work);
+
+ return ret;
+}
+
+static void wdsp_mgr_unbind(struct device *dev)
+{
+ struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev);
+ struct wdsp_cmpnt *cmpnt;
+ int idx;
+
+ component_unbind_all(dev, wdsp->ops);
+
+ wdsp_mgr_debugfs_remove(wdsp);
+
+ if (wdsp->dump_data.rd_dev) {
+ destroy_ramdump_device(wdsp->dump_data.rd_dev);
+ wdsp->dump_data.rd_dev = NULL;
+ }
+
+ /* Clear all status bits */
+ wdsp->status = 0x00;
+
+ /* clean up the components */
+ for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, idx);
+ cmpnt->cdev = NULL;
+ cmpnt->ops = NULL;
+ cmpnt->priv_data = NULL;
+ }
+}
+
+static const struct component_master_ops wdsp_master_ops = {
+ .bind = wdsp_mgr_bind,
+ .unbind = wdsp_mgr_unbind,
+};
+
+static void *wdsp_mgr_parse_phandle(struct wdsp_mgr_priv *wdsp,
+ int index)
+{
+ struct device *mdev = wdsp->mdev;
+ struct device_node *np;
+ struct wdsp_cmpnt *cmpnt = NULL;
+ struct of_phandle_args pargs;
+ u32 value;
+ int ret;
+
+ ret = of_parse_phandle_with_fixed_args(mdev->of_node,
+ "qcom,wdsp-components", 1,
+ index, &pargs);
+ if (ret) {
+ WDSP_ERR(wdsp, "parse_phandle at index %d failed %d",
+ index, ret);
+ return NULL;
+ }
+
+ np = pargs.np;
+ value = pargs.args[0];
+
+ if (value >= WDSP_CMPNT_TYPE_MAX) {
+ WDSP_ERR(wdsp, "invalid phandle_arg to of_node %s", np->name);
+ goto done;
+ }
+
+ cmpnt = WDSP_GET_COMPONENT(wdsp, value);
+ if (cmpnt->np || cmpnt->cdev_name) {
+ WDSP_ERR(wdsp, "cmpnt %d already added", value);
+ cmpnt = NULL;
+ goto done;
+ }
+
+ cmpnt->np = np;
+ of_property_read_string(np, "qcom,wdsp-cmpnt-dev-name",
+ &cmpnt->cdev_name);
+done:
+ of_node_put(np);
+ return cmpnt;
+}
+
+static int wdsp_mgr_parse_dt_entries(struct wdsp_mgr_priv *wdsp)
+{
+ struct device *dev = wdsp->mdev;
+ void *match_data;
+ int ph_idx, ret;
+
+ ret = of_property_read_string(dev->of_node, "qcom,img-filename",
+ &wdsp->img_fname);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Reading property %s failed, error = %d",
+ "qcom,img-filename", ret);
+ return ret;
+ }
+
+ ret = of_count_phandle_with_args(dev->of_node,
+ "qcom,wdsp-components",
+ NULL);
+ if (ret == -ENOENT) {
+ WDSP_ERR(wdsp, "Property %s not defined in DT",
+ "qcom,wdsp-components");
+ goto done;
+ } else if (ret != WDSP_CMPNT_TYPE_MAX * 2) {
+ WDSP_ERR(wdsp, "Invalid phandle + arg count %d, expected %d",
+ ret, WDSP_CMPNT_TYPE_MAX * 2);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = 0;
+
+ for (ph_idx = 0; ph_idx < WDSP_CMPNT_TYPE_MAX; ph_idx++) {
+
+ match_data = wdsp_mgr_parse_phandle(wdsp, ph_idx);
+ if (!match_data) {
+ WDSP_ERR(wdsp, "component not found at idx %d", ph_idx);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ component_match_add(dev, &wdsp->match,
+ wdsp_mgr_compare_of, match_data);
+ }
+
+done:
+ return ret;
+}
+
+static int wdsp_mgr_probe(struct platform_device *pdev)
+{
+ struct wdsp_mgr_priv *wdsp;
+ struct device *mdev = &pdev->dev;
+ int ret;
+
+ wdsp = devm_kzalloc(mdev, sizeof(*wdsp), GFP_KERNEL);
+ if (!wdsp)
+ return -ENOMEM;
+ wdsp->mdev = mdev;
+ wdsp->seg_list = devm_kzalloc(mdev, sizeof(struct list_head),
+ GFP_KERNEL);
+ if (!wdsp->seg_list) {
+ devm_kfree(mdev, wdsp);
+ return -ENOMEM;
+ }
+
+ ret = wdsp_mgr_parse_dt_entries(wdsp);
+ if (ret)
+ goto err_dt_parse;
+
+ INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image);
+ INIT_LIST_HEAD(wdsp->seg_list);
+ mutex_init(&wdsp->api_mutex);
+ mutex_init(&wdsp->ssr_mutex);
+ wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR;
+ wdsp->ready_status = WDSP_SSR_STATUS_READY;
+ INIT_WORK(&wdsp->ssr_work, wdsp_ssr_work_fn);
+ init_completion(&wdsp->ready_compl);
+ arch_setup_dma_ops(wdsp->mdev, 0, 0, NULL, 0);
+ dev_set_drvdata(mdev, wdsp);
+
+ ret = component_master_add_with_match(mdev, &wdsp_master_ops,
+ wdsp->match);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Failed to add master, err = %d", ret);
+ goto err_master_add;
+ }
+
+ return 0;
+
+err_master_add:
+ mutex_destroy(&wdsp->api_mutex);
+ mutex_destroy(&wdsp->ssr_mutex);
+err_dt_parse:
+ devm_kfree(mdev, wdsp->seg_list);
+ devm_kfree(mdev, wdsp);
+ dev_set_drvdata(mdev, NULL);
+
+ return ret;
+}
+
+static int wdsp_mgr_remove(struct platform_device *pdev)
+{
+ struct device *mdev = &pdev->dev;
+ struct wdsp_mgr_priv *wdsp = dev_get_drvdata(mdev);
+
+ component_master_del(mdev, &wdsp_master_ops);
+
+ mutex_destroy(&wdsp->api_mutex);
+ mutex_destroy(&wdsp->ssr_mutex);
+ devm_kfree(mdev, wdsp->seg_list);
+ devm_kfree(mdev, wdsp);
+ dev_set_drvdata(mdev, NULL);
+
+ return 0;
+};
+
+static const struct of_device_id wdsp_mgr_dt_match[] = {
+ {.compatible = "qcom,wcd-dsp-mgr" },
+ { }
+};
+
+static struct platform_driver wdsp_mgr_driver = {
+ .driver = {
+ .name = "wcd-dsp-mgr",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(wdsp_mgr_dt_match),
+ },
+ .probe = wdsp_mgr_probe,
+ .remove = wdsp_mgr_remove,
+};
+module_platform_driver(wdsp_mgr_driver);
+
+MODULE_DESCRIPTION("WCD DSP manager driver");
+MODULE_DEVICE_TABLE(of, wdsp_mgr_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd-dsp-utils.c b/sound/soc/codecs/wcd-dsp-utils.c
new file mode 100644
index 000000000000..1f048917a6c8
--- /dev/null
+++ b/sound/soc/codecs/wcd-dsp-utils.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/elf.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include "wcd-dsp-utils.h"
+
+static bool wdsp_is_valid_elf_hdr(const struct elf32_hdr *ehdr,
+ size_t fw_size)
+{
+ if (fw_size < sizeof(*ehdr)) {
+ pr_err("%s: Firmware too small\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+ pr_err("%s: Not an ELF file\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
+ pr_err("%s: Not an executable image\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (ehdr->e_phnum == 0) {
+ pr_err("%s: no segments to load\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
+ sizeof(struct elf32_hdr) > fw_size) {
+ pr_err("%s: Too small MDT file\n", __func__);
+ goto elf_check_fail;
+ }
+
+ return true;
+
+elf_check_fail:
+ return false;
+}
+
+static int wdsp_add_segment_to_list(struct device *dev,
+ const char *img_fname,
+ const struct elf32_phdr *phdr,
+ int phdr_idx,
+ struct list_head *seg_list)
+{
+ struct wdsp_img_segment *seg;
+ int ret = 0;
+
+ /* Do not load segments with zero size */
+ if (phdr->p_filesz == 0 || phdr->p_memsz == 0)
+ goto done;
+
+ seg = kzalloc(sizeof(*seg), GFP_KERNEL);
+ if (!seg) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(seg->split_fname, sizeof(seg->split_fname),
+ "%s.b%02d", img_fname, phdr_idx);
+ ret = request_firmware(&seg->split_fw, seg->split_fname, dev);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(dev, "%s: firmware %s not found\n",
+ __func__, seg->split_fname);
+ goto bad_seg;
+ }
+
+ seg->load_addr = phdr->p_paddr;
+ seg->size = phdr->p_filesz;
+ seg->data = (u8 *) seg->split_fw->data;
+
+ list_add_tail(&seg->list, seg_list);
+done:
+ return ret;
+bad_seg:
+ kfree(seg);
+ return ret;
+}
+
+/*
+ * wdsp_flush_segment_list: Flush the list of segments
+ * @seg_list: List of segments to be flushed
+ * This API will traverse through the list of segments provided in
+ * seg_list, release the firmware for each segment and delete the
+ * segment from the list.
+ */
+void wdsp_flush_segment_list(struct list_head *seg_list)
+{
+ struct wdsp_img_segment *seg, *next;
+
+ list_for_each_entry_safe(seg, next, seg_list, list) {
+ release_firmware(seg->split_fw);
+ list_del(&seg->list);
+ kfree(seg);
+ }
+}
+EXPORT_SYMBOL(wdsp_flush_segment_list);
+
+/*
+ * wdsp_get_segment_list: Get the list of requested segments
+ * @dev: struct device pointer of caller
+ * @img_fname: Image name for the mdt and split firmware files
+ * @segment_type: Requested segment type, should be either
+ * WDSP_ELF_FLAG_RE or WDSP_ELF_FLAG_WRITE
+ * @seg_list: An initialized head for list of segmented to be returned
+ * @entry_point: Pointer to return the entry point of the image
+ * This API will parse the mdt file for img_fname and create
+ * an struct wdsp_img_segment for each segment that matches segment_type
+ * and add this structure to list pointed by seg_list
+ */
+int wdsp_get_segment_list(struct device *dev,
+ const char *img_fname,
+ unsigned int segment_type,
+ struct list_head *seg_list,
+ u32 *entry_point)
+{
+ const struct firmware *fw;
+ const struct elf32_hdr *ehdr;
+ const struct elf32_phdr *phdr;
+ const u8 *elf_ptr;
+ char mdt_name[WDSP_IMG_NAME_LEN_MAX];
+ int ret, phdr_idx;
+ bool segment_match;
+
+ if (!dev) {
+ ret = -EINVAL;
+ pr_err("%s: Invalid device handle\n", __func__);
+ goto done;
+ }
+
+ if (!img_fname || !seg_list || !entry_point) {
+ ret = -EINVAL;
+ dev_err(dev, "%s: Invalid input params\n",
+ __func__);
+ goto done;
+ }
+
+ if (segment_type != WDSP_ELF_FLAG_RE &&
+ segment_type != WDSP_ELF_FLAG_WRITE) {
+ dev_err(dev, "%s: Invalid request for segment_type %d\n",
+ __func__, segment_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", img_fname);
+ ret = request_firmware(&fw, mdt_name, dev);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(dev, "%s: firmware %s not found\n",
+ __func__, mdt_name);
+ goto done;
+ }
+
+ ehdr = (struct elf32_hdr *) fw->data;
+ *entry_point = ehdr->e_entry;
+ if (!wdsp_is_valid_elf_hdr(ehdr, fw->size)) {
+ dev_err(dev, "%s: fw mdt %s is invalid\n",
+ __func__, mdt_name);
+ ret = -EINVAL;
+ goto bad_elf;
+ }
+
+ elf_ptr = fw->data + sizeof(*ehdr);
+ for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) {
+ phdr = (struct elf32_phdr *) elf_ptr;
+ segment_match = false;
+
+ switch (segment_type) {
+ case WDSP_ELF_FLAG_RE:
+ /*
+ * Flag can be READ or EXECUTE or both but
+ * WRITE flag should not be set.
+ */
+ if ((phdr->p_flags & segment_type) &&
+ !(phdr->p_flags & WDSP_ELF_FLAG_WRITE))
+ segment_match = true;
+ break;
+ case WDSP_ELF_FLAG_WRITE:
+ /*
+ * If WRITE flag is set, other flags do not
+ * matter.
+ */
+ if (phdr->p_flags & segment_type)
+ segment_match = true;
+ break;
+ }
+
+ if (segment_match) {
+ ret = wdsp_add_segment_to_list(dev, img_fname, phdr,
+ phdr_idx, seg_list);
+ if (IS_ERR_VALUE(ret)) {
+ wdsp_flush_segment_list(seg_list);
+ goto bad_elf;
+ }
+ }
+ elf_ptr = elf_ptr + sizeof(*phdr);
+ }
+
+bad_elf:
+ release_firmware(fw);
+done:
+ return ret;
+}
+EXPORT_SYMBOL(wdsp_get_segment_list);
diff --git a/sound/soc/codecs/wcd-dsp-utils.h b/sound/soc/codecs/wcd-dsp-utils.h
new file mode 100644
index 000000000000..81842f77260e
--- /dev/null
+++ b/sound/soc/codecs/wcd-dsp-utils.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD_DSP_UTILS_H__
+#define __WCD_DSP_UTILS_H__
+
+#define WDSP_IMG_NAME_LEN_MAX 64
+
+#define WDSP_ELF_FLAG_EXECUTE (1 << 0)
+#define WDSP_ELF_FLAG_WRITE (1 << 1)
+#define WDSP_ELF_FLAG_READ (1 << 2)
+
+#define WDSP_ELF_FLAG_RE (WDSP_ELF_FLAG_READ | WDSP_ELF_FLAG_EXECUTE)
+
+struct wdsp_img_segment {
+
+ /* Firmware for the slit image */
+ const struct firmware *split_fw;
+
+ /* Name of the split firmware file */
+ char split_fname[WDSP_IMG_NAME_LEN_MAX];
+
+ /* Address where the segment is to be loaded */
+ u32 load_addr;
+
+ /* Buffer to hold the data to be loaded */
+ u8 *data;
+
+ /* Size of the data to be loaded */
+ size_t size;
+
+ /* List node pointing to next segment */
+ struct list_head list;
+};
+
+int wdsp_get_segment_list(struct device *, const char *,
+ unsigned int, struct list_head *,
+ u32 *);
+void wdsp_flush_segment_list(struct list_head *);
+
+#endif /* __WCD_DSP_UTILS_H__ */
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
new file mode 100644
index 000000000000..997a623033fb
--- /dev/null
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -0,0 +1,3034 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/firmware.h>
+#include <linux/completion.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "wcd-mbhc-v2.h"
+#include "wcdcal-hwdep.h"
+
+#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
+ SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
+ SND_JACK_MECHANICAL | SND_JACK_MICROPHONE2 | \
+ SND_JACK_UNSUPPORTED)
+
+#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+ SND_JACK_BTN_4 | SND_JACK_BTN_5 )
+#define OCP_ATTEMPT 20
+#define HS_DETECT_PLUG_TIME_MS (3 * 1000)
+#define SPECIAL_HS_DETECT_TIME_MS (2 * 1000)
+#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250
+#define GND_MIC_SWAP_THRESHOLD 4
+#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100
+#define HS_VREF_MIN_VAL 1400
+#define FW_READ_ATTEMPTS 15
+#define FW_READ_TIMEOUT 4000000
+#define FAKE_REM_RETRY_ATTEMPTS 3
+#define MAX_IMPED 60000
+
+#define WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS 50
+#define ANC_DETECT_RETRY_CNT 7
+#define WCD_MBHC_SPL_HS_CNT 1
+
+static int det_extn_cable_en;
+module_param(det_extn_cable_en, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(det_extn_cable_en, "enable/disable extn cable detect");
+
+enum wcd_mbhc_cs_mb_en_flag {
+ WCD_MBHC_EN_CS = 0,
+ WCD_MBHC_EN_MB,
+ WCD_MBHC_EN_PULLUP,
+ WCD_MBHC_EN_NONE,
+};
+
+static void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc,
+ struct snd_soc_jack *jack, int status, int mask)
+{
+ snd_soc_jack_report(jack, status, mask);
+}
+
+static void __hphocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status,
+ int irq)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ dev_dbg(codec->dev, "%s: clear ocp status %x\n",
+ __func__, jack_status);
+
+ if (mbhc->hph_status & jack_status) {
+ mbhc->hph_status &= ~jack_status;
+ wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+ mbhc->hph_status, WCD_MBHC_JACK_MASK);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
+ /*
+ * reset retry counter as PA is turned off signifying
+ * start of new OCP detection session
+ */
+ if (mbhc->intr_ids->hph_left_ocp)
+ mbhc->hphlocp_cnt = 0;
+ else
+ mbhc->hphrocp_cnt = 0;
+ mbhc->mbhc_cb->irq_control(codec, irq, true);
+ }
+}
+
+static void hphrocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status)
+{
+ __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
+ mbhc->intr_ids->hph_right_ocp);
+}
+
+static void hphlocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status)
+{
+ __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
+ mbhc->intr_ids->hph_left_ocp);
+}
+
+static void wcd_program_hs_vref(struct wcd_mbhc *mbhc)
+{
+ struct wcd_mbhc_plug_type_cfg *plug_type_cfg;
+ struct snd_soc_codec *codec = mbhc->codec;
+ u32 reg_val;
+
+ plug_type_cfg = WCD_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ reg_val = ((plug_type_cfg->v_hs_max - HS_VREF_MIN_VAL) / 100);
+
+ dev_dbg(codec->dev, "%s: reg_val = %x\n", __func__, reg_val);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_VREF, reg_val);
+}
+
+static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias)
+{
+ struct wcd_mbhc_btn_detect_cfg *btn_det;
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct snd_soc_card *card = codec->component.card;
+ s16 *btn_low, *btn_high;
+
+ if (mbhc->mbhc_cfg->calibration == NULL) {
+ dev_err(card->dev, "%s: calibration data is NULL\n", __func__);
+ return;
+ }
+
+ btn_det = WCD_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ btn_low = btn_det->_v_btn_low;
+ btn_high = ((void *)&btn_det->_v_btn_low) +
+ (sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn);
+
+ mbhc->mbhc_cb->set_btn_thr(codec, btn_low, btn_high, btn_det->num_btn,
+ micbias);
+}
+
+static void wcd_enable_curr_micbias(const struct wcd_mbhc *mbhc,
+ const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)
+{
+
+ /*
+ * Some codecs handle micbias/pullup enablement in codec
+ * drivers itself and micbias is not needed for regular
+ * plug type detection. So if micbias_control callback function
+ * is defined, just return.
+ */
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ return;
+
+ pr_debug("%s: enter, cs_mb_en: %d\n", __func__, cs_mb_en);
+
+ switch (cs_mb_en) {
+ case WCD_MBHC_EN_CS:
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
+ /* Program Button threshold registers as per CS */
+ wcd_program_btn_threshold(mbhc, false);
+ break;
+ case WCD_MBHC_EN_MB:
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+
+ /* Disable PULL_UP_EN & enable MICBIAS */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 2);
+ /* Program Button threshold registers as per MICBIAS */
+ wcd_program_btn_threshold(mbhc, true);
+ break;
+ case WCD_MBHC_EN_PULLUP:
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 1);
+ /* Program Button threshold registers as per MICBIAS */
+ wcd_program_btn_threshold(mbhc, true);
+ break;
+ case WCD_MBHC_EN_NONE:
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
+ break;
+ default:
+ pr_debug("%s: Invalid parameter", __func__);
+ break;
+ }
+
+ pr_debug("%s: exit\n", __func__);
+}
+
+static const char *wcd_mbhc_get_event_string(int event)
+{
+ switch (event) {
+ case WCD_EVENT_PRE_MICBIAS_2_OFF:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_OFF);
+ case WCD_EVENT_POST_MICBIAS_2_OFF:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_OFF);
+ case WCD_EVENT_PRE_MICBIAS_2_ON:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_ON);
+ case WCD_EVENT_POST_MICBIAS_2_ON:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_ON);
+ case WCD_EVENT_PRE_HPHL_PA_ON:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_ON);
+ case WCD_EVENT_POST_HPHL_PA_OFF:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHL_PA_OFF);
+ case WCD_EVENT_PRE_HPHR_PA_ON:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_ON);
+ case WCD_EVENT_POST_HPHR_PA_OFF:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHR_PA_OFF);
+ case WCD_EVENT_PRE_HPHR_PA_OFF:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_OFF);
+ case WCD_EVENT_PRE_HPHL_PA_OFF:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_OFF);
+ case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_ON);
+ case WCD_EVENT_PRE_DAPM_MICBIAS_2_ON:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_ON);
+ case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
+ case WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF);
+ case WCD_EVENT_OCP_OFF:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_OFF);
+ case WCD_EVENT_OCP_ON:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_ON);
+ case WCD_EVENT_INVALID:
+ default:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_INVALID);
+ }
+}
+
+static int wcd_event_notify(struct notifier_block *self, unsigned long val,
+ void *data)
+{
+ struct wcd_mbhc *mbhc = (struct wcd_mbhc *)data;
+ enum wcd_notify_event event = (enum wcd_notify_event)val;
+ struct snd_soc_codec *codec = mbhc->codec;
+ bool micbias2 = false;
+ bool micbias1 = false;
+ u8 fsm_en = 0;
+
+ pr_debug("%s: event %s (%d)\n", __func__,
+ wcd_mbhc_get_event_string(event), event);
+ if (mbhc->mbhc_cb->micbias_enable_status) {
+ micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+ MIC_BIAS_2);
+ micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+ MIC_BIAS_1);
+ }
+ switch (event) {
+ /* MICBIAS usage change */
+ case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
+ mbhc->is_hs_recording = true;
+ pr_debug("%s: is_capture: %d\n", __func__,
+ mbhc->is_hs_recording);
+ break;
+ case WCD_EVENT_POST_MICBIAS_2_ON:
+ if (!mbhc->micbias_enable)
+ goto out_micb_en;
+ if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) {
+ mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+ MBHC_COMMON_MICB_PRECHARGE,
+ true);
+ mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+ MBHC_COMMON_MICB_SET_VAL,
+ true);
+ /*
+ * Special headset needs MICBIAS as 2.7V so wait for
+ * 50 msec for the MICBIAS to reach 2.7 volts.
+ */
+ msleep(50);
+ }
+ if (mbhc->mbhc_cb->set_auto_zeroing)
+ mbhc->mbhc_cb->set_auto_zeroing(codec, true);
+ if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
+ mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+ MBHC_COMMON_MICB_PRECHARGE,
+ false);
+out_micb_en:
+ /* Disable current source if micbias enabled */
+ if (mbhc->mbhc_cb->mbhc_micbias_control) {
+ WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en);
+ if (fsm_en)
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL,
+ 0);
+ } else {
+ mbhc->is_hs_recording = true;
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+ }
+ /* configure cap settings properly when micbias is enabled */
+ if (mbhc->mbhc_cb->set_cap_mode)
+ mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true);
+ break;
+ case WCD_EVENT_PRE_MICBIAS_2_OFF:
+ /*
+ * Before MICBIAS_2 is turned off, if FSM is enabled,
+ * make sure current source is enabled so as to detect
+ * button press/release events
+ */
+ if (mbhc->mbhc_cb->mbhc_micbias_control &&
+ !mbhc->micbias_enable) {
+ WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en);
+ if (fsm_en)
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL,
+ 3);
+ }
+ break;
+ /* MICBIAS usage change */
+ case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
+ mbhc->is_hs_recording = false;
+ pr_debug("%s: is_capture: %d\n", __func__,
+ mbhc->is_hs_recording);
+ break;
+ case WCD_EVENT_POST_MICBIAS_2_OFF:
+ if (!mbhc->mbhc_cb->mbhc_micbias_control)
+ mbhc->is_hs_recording = false;
+ if (mbhc->micbias_enable) {
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+ break;
+ }
+
+ if (mbhc->mbhc_cb->set_auto_zeroing)
+ mbhc->mbhc_cb->set_auto_zeroing(codec, false);
+ if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable)
+ mbhc->mbhc_cb->set_micbias_value(codec);
+ /* Enable PULL UP if PA's are enabled */
+ if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) ||
+ (test_bit(WCD_MBHC_EVENT_PA_HPHR,
+ &mbhc->event_state)))
+ /* enable pullup and cs, disable mb */
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP);
+ else
+ /* enable current source and disable mb, pullup*/
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
+
+ /* configure cap settings properly when micbias is disabled */
+ if (mbhc->mbhc_cb->set_cap_mode)
+ mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false);
+ break;
+ case WCD_EVENT_PRE_HPHL_PA_OFF:
+ mutex_lock(&mbhc->hphl_pa_lock);
+ break;
+ case WCD_EVENT_POST_HPHL_PA_OFF:
+ clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (mbhc->hph_status & SND_JACK_OC_HPHL)
+ hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, pullup & enable cs */
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
+ mutex_unlock(&mbhc->hphl_pa_lock);
+ clear_bit(WCD_MBHC_ANC0_OFF_ACK, &mbhc->hph_anc_state);
+ break;
+ case WCD_EVENT_PRE_HPHR_PA_OFF:
+ mutex_lock(&mbhc->hphr_pa_lock);
+ break;
+ case WCD_EVENT_POST_HPHR_PA_OFF:
+ clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (mbhc->hph_status & SND_JACK_OC_HPHR)
+ hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
+ clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, pullup & enable cs */
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
+ mutex_unlock(&mbhc->hphr_pa_lock);
+ clear_bit(WCD_MBHC_ANC1_OFF_ACK, &mbhc->hph_anc_state);
+ break;
+ case WCD_EVENT_PRE_HPHL_PA_ON:
+ set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, enable pullup & cs */
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP);
+ break;
+ case WCD_EVENT_PRE_HPHR_PA_ON:
+ set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, enable pullup & cs */
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP);
+ break;
+ case WCD_EVENT_OCP_OFF:
+ mbhc->mbhc_cb->irq_control(mbhc->codec,
+ mbhc->intr_ids->hph_left_ocp,
+ false);
+ break;
+ case WCD_EVENT_OCP_ON:
+ mbhc->mbhc_cb->irq_control(mbhc->codec,
+ mbhc->intr_ids->hph_left_ocp,
+ true);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wcd_cancel_btn_work(struct wcd_mbhc *mbhc)
+{
+ int r;
+
+ r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
+ /*
+ * if scheduled mbhc.mbhc_btn_dwork is canceled from here,
+ * we have to unlock from here instead btn_work
+ */
+ if (r)
+ mbhc->mbhc_cb->lock_sleep(mbhc, false);
+ return r;
+}
+
+static bool wcd_swch_level_remove(struct wcd_mbhc *mbhc)
+{
+ u16 result2 = 0;
+
+ WCD_MBHC_REG_READ(WCD_MBHC_SWCH_LEVEL_REMOVE, result2);
+ return (result2) ? true : false;
+}
+
+/* should be called under interrupt context that hold suspend */
+static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc,
+ struct work_struct *work)
+{
+ pr_debug("%s: scheduling correct_swch_plug\n", __func__);
+ WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+ mbhc->hs_detect_work_stop = false;
+ mbhc->mbhc_cb->lock_sleep(mbhc, true);
+ schedule_work(work);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc,
+ struct work_struct *work)
+{
+ pr_debug("%s: Canceling correct_plug_swch\n", __func__);
+ mbhc->hs_detect_work_stop = true;
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ if (cancel_work_sync(work)) {
+ pr_debug("%s: correct_plug_swch is canceled\n",
+ __func__);
+ mbhc->mbhc_cb->lock_sleep(mbhc, false);
+ }
+ WCD_MBHC_RSC_LOCK(mbhc);
+}
+
+static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc)
+{
+ bool pa_turned_on = false;
+ u8 wg_time = 0;
+
+ WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time);
+ wg_time += 1;
+
+ mutex_lock(&mbhc->hphr_pa_lock);
+ if (test_and_clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_PA_EN, 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 1);
+ pa_turned_on = true;
+ }
+ mutex_unlock(&mbhc->hphr_pa_lock);
+ mutex_lock(&mbhc->hphl_pa_lock);
+ if (test_and_clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 1);
+ pa_turned_on = true;
+ }
+ mutex_unlock(&mbhc->hphl_pa_lock);
+
+ if (pa_turned_on) {
+ pr_debug("%s: PA was turned on by MBHC and not by DAPM\n",
+ __func__);
+ usleep_range(wg_time * 1000, wg_time * 1000 + 50);
+ }
+
+ if (test_and_clear_bit(WCD_MBHC_ANC0_OFF_ACK,
+ &mbhc->hph_anc_state)) {
+ usleep_range(20000, 20100);
+ pr_debug("%s: HPHL ANC clear flag and enable ANC_EN\n",
+ __func__);
+ if (mbhc->mbhc_cb->update_anc_state)
+ mbhc->mbhc_cb->update_anc_state(mbhc->codec, true, 0);
+ }
+
+ if (test_and_clear_bit(WCD_MBHC_ANC1_OFF_ACK,
+ &mbhc->hph_anc_state)) {
+ usleep_range(20000, 20100);
+ pr_debug("%s: HPHR ANC clear flag and enable ANC_EN\n",
+ __func__);
+ if (mbhc->mbhc_cb->update_anc_state)
+ mbhc->mbhc_cb->update_anc_state(mbhc->codec, true, 1);
+ }
+
+}
+
+static bool wcd_mbhc_is_hph_pa_on(struct wcd_mbhc *mbhc)
+{
+ bool hph_pa_on = false;
+
+ WCD_MBHC_REG_READ(WCD_MBHC_HPH_PA_EN, hph_pa_on);
+
+ return (hph_pa_on) ? true : false;
+}
+
+static void wcd_mbhc_set_and_turnoff_hph_padac(struct wcd_mbhc *mbhc)
+{
+ u8 wg_time = 0;
+
+ WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time);
+ wg_time += 1;
+
+ /* If headphone PA is on, check if userspace receives
+ * removal event to sync-up PA's state */
+ if (wcd_mbhc_is_hph_pa_on(mbhc)) {
+ pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
+ set_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ set_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 0);
+ } else {
+ pr_debug("%s PA is off\n", __func__);
+ }
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 0);
+ usleep_range(wg_time * 1000, wg_time * 1000 + 50);
+
+
+ if (mbhc->mbhc_cb->is_anc_on && mbhc->mbhc_cb->is_anc_on(mbhc)) {
+ usleep_range(20000, 20100);
+ pr_debug("%s ANC is on, setting ANC_OFF_ACK\n", __func__);
+ set_bit(WCD_MBHC_ANC0_OFF_ACK, &mbhc->hph_anc_state);
+ set_bit(WCD_MBHC_ANC1_OFF_ACK, &mbhc->hph_anc_state);
+ if (mbhc->mbhc_cb->update_anc_state) {
+ mbhc->mbhc_cb->update_anc_state(mbhc->codec, false, 0);
+ mbhc->mbhc_cb->update_anc_state(mbhc->codec, false, 1);
+ } else {
+ pr_debug("%s ANC is off\n", __func__);
+ }
+ }
+}
+
+int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr)
+{
+ *zl = mbhc->zl;
+ *zr = mbhc->zr;
+
+ if (*zl && *zr)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static void wcd_mbhc_hs_elec_irq(struct wcd_mbhc *mbhc, int irq_type,
+ bool enable)
+{
+ int irq;
+
+ WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+ if (irq_type == WCD_MBHC_ELEC_HS_INS)
+ irq = mbhc->intr_ids->mbhc_hs_ins_intr;
+ else if (irq_type == WCD_MBHC_ELEC_HS_REM)
+ irq = mbhc->intr_ids->mbhc_hs_rem_intr;
+ else {
+ pr_debug("%s: irq_type: %d, enable: %d\n",
+ __func__, irq_type, enable);
+ return;
+ }
+
+ pr_debug("%s: irq: %d, enable: %d, intr_status:%lu\n",
+ __func__, irq, enable, mbhc->intr_status);
+ if ((test_bit(irq_type, &mbhc->intr_status)) != enable) {
+ mbhc->mbhc_cb->irq_control(mbhc->codec, irq, enable);
+ if (enable)
+ set_bit(irq_type, &mbhc->intr_status);
+ else
+ clear_bit(irq_type, &mbhc->intr_status);
+ }
+}
+
+static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
+ enum snd_jack_types jack_type)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ bool is_pa_on = false;
+
+ WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+ pr_debug("%s: enter insertion %d hph_status %x\n",
+ __func__, insertion, mbhc->hph_status);
+ if (!insertion) {
+ /* Report removal */
+ mbhc->hph_status &= ~jack_type;
+ /*
+ * cancel possibly scheduled btn work and
+ * report release if we reported button press
+ */
+ if (wcd_cancel_btn_work(mbhc)) {
+ pr_debug("%s: button press is canceled\n", __func__);
+ } else if (mbhc->buttons_pressed) {
+ pr_debug("%s: release of button press%d\n",
+ __func__, jack_type);
+ wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, 0,
+ mbhc->buttons_pressed);
+ mbhc->buttons_pressed &=
+ ~WCD_MBHC_JACK_BUTTON_MASK;
+ }
+
+ if (mbhc->micbias_enable) {
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ mbhc->mbhc_cb->mbhc_micbias_control(
+ codec, MIC_BIAS_2,
+ MICB_DISABLE);
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
+ codec,
+ MIC_BIAS_2, false);
+ if (mbhc->mbhc_cb->set_micbias_value) {
+ mbhc->mbhc_cb->set_micbias_value(codec);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
+ }
+ mbhc->micbias_enable = false;
+ }
+
+ mbhc->hph_type = WCD_MBHC_HPH_NONE;
+ mbhc->zl = mbhc->zr = 0;
+ pr_debug("%s: Reporting removal %d(%x)\n", __func__,
+ jack_type, mbhc->hph_status);
+ wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+ mbhc->hph_status, WCD_MBHC_JACK_MASK);
+ wcd_mbhc_set_and_turnoff_hph_padac(mbhc);
+ hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
+ hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
+ } else {
+ /*
+ * Report removal of current jack type.
+ * Headphone to headset shouldn't report headphone
+ * removal.
+ */
+ if (mbhc->mbhc_cfg->detect_extn_cable &&
+ (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH ||
+ jack_type == SND_JACK_LINEOUT) &&
+ (mbhc->hph_status && mbhc->hph_status != jack_type)) {
+
+ if (mbhc->micbias_enable &&
+ mbhc->hph_status == SND_JACK_HEADSET) {
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ mbhc->mbhc_cb->mbhc_micbias_control(
+ codec, MIC_BIAS_2,
+ MICB_DISABLE);
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
+ codec,
+ MIC_BIAS_2, false);
+ if (mbhc->mbhc_cb->set_micbias_value) {
+ mbhc->mbhc_cb->set_micbias_value(
+ codec);
+ WCD_MBHC_REG_UPDATE_BITS(
+ WCD_MBHC_MICB_CTRL, 0);
+ }
+ mbhc->micbias_enable = false;
+ }
+ mbhc->hph_type = WCD_MBHC_HPH_NONE;
+ mbhc->zl = mbhc->zr = 0;
+ pr_debug("%s: Reporting removal (%x)\n",
+ __func__, mbhc->hph_status);
+ wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+ 0, WCD_MBHC_JACK_MASK);
+
+ if (mbhc->hph_status == SND_JACK_LINEOUT) {
+
+ pr_debug("%s: Enable micbias\n", __func__);
+ /* Disable current source and enable micbias */
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+ pr_debug("%s: set up elec removal detection\n",
+ __func__);
+ WCD_MBHC_REG_UPDATE_BITS(
+ WCD_MBHC_ELECT_DETECTION_TYPE,
+ 0);
+ usleep_range(200, 210);
+ wcd_mbhc_hs_elec_irq(mbhc,
+ WCD_MBHC_ELEC_HS_REM,
+ true);
+ }
+ mbhc->hph_status &= ~(SND_JACK_HEADSET |
+ SND_JACK_LINEOUT |
+ SND_JACK_ANC_HEADPHONE |
+ SND_JACK_UNSUPPORTED);
+ }
+
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
+ jack_type == SND_JACK_HEADPHONE)
+ mbhc->hph_status &= ~SND_JACK_HEADSET;
+
+ /* Report insertion */
+ if (jack_type == SND_JACK_HEADPHONE)
+ mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
+ else if (jack_type == SND_JACK_UNSUPPORTED)
+ mbhc->current_plug = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+ else if (jack_type == SND_JACK_HEADSET) {
+ mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
+ mbhc->jiffies_atreport = jiffies;
+ } else if (jack_type == SND_JACK_LINEOUT) {
+ mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
+ } else if (jack_type == SND_JACK_ANC_HEADPHONE)
+ mbhc->current_plug = MBHC_PLUG_TYPE_ANC_HEADPHONE;
+
+ if (mbhc->mbhc_cb->hph_pa_on_status)
+ is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(codec);
+
+ if (mbhc->impedance_detect &&
+ mbhc->mbhc_cb->compute_impedance &&
+ (mbhc->mbhc_cfg->linein_th != 0) &&
+ (!is_pa_on)) {
+ mbhc->mbhc_cb->compute_impedance(mbhc,
+ &mbhc->zl, &mbhc->zr);
+ if ((mbhc->zl > mbhc->mbhc_cfg->linein_th &&
+ mbhc->zl < MAX_IMPED) &&
+ (mbhc->zr > mbhc->mbhc_cfg->linein_th &&
+ mbhc->zr < MAX_IMPED) &&
+ (jack_type == SND_JACK_HEADPHONE)) {
+ jack_type = SND_JACK_LINEOUT;
+ mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
+ if (mbhc->hph_status) {
+ mbhc->hph_status &= ~(SND_JACK_HEADSET |
+ SND_JACK_LINEOUT |
+ SND_JACK_UNSUPPORTED);
+ wcd_mbhc_jack_report(mbhc,
+ &mbhc->headset_jack,
+ mbhc->hph_status,
+ WCD_MBHC_JACK_MASK);
+ }
+ pr_debug("%s: Marking jack type as SND_JACK_LINEOUT\n",
+ __func__);
+ }
+ }
+
+ mbhc->hph_status |= jack_type;
+
+ pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
+ jack_type, mbhc->hph_status);
+ wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+ (mbhc->hph_status | SND_JACK_MECHANICAL),
+ WCD_MBHC_JACK_MASK);
+ wcd_mbhc_clr_and_turnon_hph_padac(mbhc);
+ }
+ pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
+}
+
+static bool wcd_mbhc_detect_anc_plug_type(struct wcd_mbhc *mbhc)
+{
+ bool anc_mic_found = false;
+ u16 val, hs_comp_res, btn_status = 0;
+ unsigned long retry = 0;
+ int valid_plug_cnt = 0, invalid_plug_cnt = 0;
+ int btn_status_cnt = 0;
+ bool is_check_btn_press = false;
+
+
+ if (mbhc->mbhc_cfg->anc_micbias < MIC_BIAS_1 ||
+ mbhc->mbhc_cfg->anc_micbias > MIC_BIAS_4)
+ return false;
+
+ if (!mbhc->mbhc_cb->mbhc_micbias_control)
+ return false;
+
+ WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, val);
+
+ if (val)
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+
+ mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec,
+ mbhc->mbhc_cfg->anc_micbias,
+ MICB_ENABLE);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x2);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+ /*
+ * wait for button debounce time 20ms. If 4-pole plug is inserted
+ * into 5-pole jack, then there will be a button press interrupt
+ * during anc plug detection. In that case though Hs_comp_res is 0,
+ * it should not be declared as ANC plug type
+ */
+ usleep_range(20000, 20100);
+
+ /*
+ * After enabling FSM, to handle slow insertion scenarios,
+ * check hs_comp_result for few times to see if the IN3 voltage
+ * is below the Vref
+ */
+ do {
+ if (wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low\n", __func__);
+ goto exit;
+ }
+ pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1);
+ WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
+
+ if (!hs_comp_res) {
+ valid_plug_cnt++;
+ is_check_btn_press = true;
+ } else
+ invalid_plug_cnt++;
+ /* Wait 1ms before taking another reading */
+ usleep_range(1000, 1100);
+
+ WCD_MBHC_REG_READ(WCD_MBHC_FSM_STATUS, btn_status);
+ if (btn_status)
+ btn_status_cnt++;
+
+ retry++;
+ } while (retry < ANC_DETECT_RETRY_CNT);
+
+ pr_debug("%s: valid: %d, invalid: %d, btn_status_cnt: %d\n",
+ __func__, valid_plug_cnt, invalid_plug_cnt, btn_status_cnt);
+
+ /* decision logic */
+ if ((valid_plug_cnt > invalid_plug_cnt) && is_check_btn_press &&
+ (btn_status_cnt == 0))
+ anc_mic_found = true;
+exit:
+ if (!val)
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 0);
+
+ mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec,
+ mbhc->mbhc_cfg->anc_micbias,
+ MICB_DISABLE);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x0);
+ pr_debug("%s: anc mic %sfound\n", __func__,
+ anc_mic_found ? "" : "not ");
+ return anc_mic_found;
+}
+
+static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
+ enum wcd_mbhc_plug_type plug_type)
+{
+ bool anc_mic_found = false;
+ enum snd_jack_types jack_type;
+
+ pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
+ __func__, mbhc->current_plug, plug_type);
+
+ WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+ if (mbhc->current_plug == plug_type) {
+ pr_debug("%s: cable already reported, exit\n", __func__);
+ goto exit;
+ }
+
+ if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
+ /*
+ * Nothing was reported previously
+ * report a headphone or unsupported
+ */
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ } else if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
+ wcd_mbhc_report_plug(mbhc, 0,
+ SND_JACK_HEADPHONE);
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
+ wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
+ } else if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
+ if (mbhc->mbhc_cfg->enable_anc_mic_detect)
+ anc_mic_found = wcd_mbhc_detect_anc_plug_type(mbhc);
+
+ jack_type = SND_JACK_HEADSET;
+ if (anc_mic_found)
+ jack_type = SND_JACK_ANC_HEADPHONE;
+
+ /*
+ * If Headphone was reported previously, this will
+ * only report the mic line
+ */
+ wcd_mbhc_report_plug(mbhc, 1, jack_type);
+ } else if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ /* High impedance device found. Report as LINEOUT */
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ pr_debug("%s: setup mic trigger for further detection\n",
+ __func__);
+
+ /* Disable HW FSM and current source */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+ /* Setup for insertion detection */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+ 1);
+ /*
+ * Enable HPHL trigger and MIC Schmitt triggers
+ * and request for elec insertion interrupts
+ */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC,
+ 3);
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+ true);
+ } else {
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ }
+ } else {
+ WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
+ mbhc->current_plug, plug_type);
+ }
+exit:
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* To determine if cross connection occured */
+static int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
+{
+ u16 swap_res = 0;
+ enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_NONE;
+ s16 reg1 = 0;
+ bool hphl_sch_res = 0, hphr_sch_res = 0;
+
+ if (wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low\n", __func__);
+ return -EINVAL;
+ }
+
+ /* If PA is enabled, dont check for cross-connection */
+ if (mbhc->mbhc_cb->hph_pa_on_status)
+ if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec))
+ return false;
+
+ WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, reg1);
+ /*
+ * Check if there is any cross connection,
+ * Micbias and schmitt trigger (HPHL-HPHR)
+ * needs to be enabled. For some codecs like wcd9335,
+ * pull-up will already be enabled when this function
+ * is called for cross-connection identification. No
+ * need to enable micbias in that case.
+ */
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 2);
+
+ WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, swap_res);
+ pr_debug("%s: swap_res%x\n", __func__, swap_res);
+
+ /*
+ * Read reg hphl and hphr schmitt result with cross connection
+ * bit. These bits will both be "0" in case of cross connection
+ * otherwise, they stay at 1
+ */
+ WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch_res);
+ WCD_MBHC_REG_READ(WCD_MBHC_HPHR_SCHMT_RESULT, hphr_sch_res);
+ if (!(hphl_sch_res || hphr_sch_res)) {
+ plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+ pr_debug("%s: Cross connection identified\n", __func__);
+ } else {
+ pr_debug("%s: No Cross connection found\n", __func__);
+ }
+
+ /* Disable schmitt trigger and restore micbias */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, reg1);
+ pr_debug("%s: leave, plug type: %d\n", __func__, plug_type);
+
+ return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false;
+}
+
+static bool wcd_is_special_headset(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ int delay = 0, rc;
+ bool ret = false;
+ u16 hs_comp_res;
+ bool is_spl_hs = false;
+
+ /*
+ * Increase micbias to 2.7V to detect headsets with
+ * threshold on microphone
+ */
+ if (mbhc->mbhc_cb->mbhc_micbias_control &&
+ !mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) {
+ pr_debug("%s: callback fn micb_ctrl_thr_mic not defined\n",
+ __func__);
+ return false;
+ } else if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) {
+ rc = mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec,
+ MIC_BIAS_2, true);
+ if (rc) {
+ pr_err("%s: Micbias control for thr mic failed, rc: %d\n",
+ __func__, rc);
+ return false;
+ }
+ }
+
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+
+ pr_debug("%s: special headset, start register writes\n", __func__);
+
+ WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
+ while (!is_spl_hs) {
+ if (mbhc->hs_detect_work_stop) {
+ pr_debug("%s: stop requested: %d\n", __func__,
+ mbhc->hs_detect_work_stop);
+ break;
+ }
+ delay = delay + 50;
+ if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) {
+ mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+ MBHC_COMMON_MICB_PRECHARGE,
+ true);
+ mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+ MBHC_COMMON_MICB_SET_VAL,
+ true);
+ }
+ /* Wait for 50msec for MICBIAS to settle down */
+ msleep(50);
+ if (mbhc->mbhc_cb->set_auto_zeroing)
+ mbhc->mbhc_cb->set_auto_zeroing(codec, true);
+ /* Wait for 50msec for FSM to update result values */
+ msleep(50);
+ WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
+ if (!(hs_comp_res)) {
+ pr_debug("%s: Special headset detected in %d msecs\n",
+ __func__, (delay * 2));
+ is_spl_hs = true;
+ }
+ if (delay == SPECIAL_HS_DETECT_TIME_MS) {
+ pr_debug("%s: Spl headset didnt get detect in 4 sec\n",
+ __func__);
+ break;
+ }
+ }
+ if (is_spl_hs) {
+ pr_debug("%s: Headset with threshold found\n", __func__);
+ mbhc->micbias_enable = true;
+ ret = true;
+ }
+ if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
+ mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+ MBHC_COMMON_MICB_PRECHARGE,
+ false);
+ if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable)
+ mbhc->mbhc_cb->set_micbias_value(codec);
+ if (mbhc->mbhc_cb->set_auto_zeroing)
+ mbhc->mbhc_cb->set_auto_zeroing(codec, false);
+
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic &&
+ !mbhc->micbias_enable)
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec, MIC_BIAS_2,
+ false);
+
+ pr_debug("%s: leave, micb_enable: %d\n", __func__,
+ mbhc->micbias_enable);
+ return ret;
+}
+
+static void wcd_mbhc_update_fsm_source(struct wcd_mbhc *mbhc,
+ enum wcd_mbhc_plug_type plug_type)
+{
+ bool micbias2;
+
+ micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+ MIC_BIAS_2);
+ switch (plug_type) {
+ case MBHC_PLUG_TYPE_HEADPHONE:
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
+ break;
+ case MBHC_PLUG_TYPE_HEADSET:
+ case MBHC_PLUG_TYPE_ANC_HEADPHONE:
+ if (!mbhc->is_hs_recording && !micbias2)
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
+ break;
+ default:
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+ break;
+
+ };
+}
+
+static void wcd_enable_mbhc_supply(struct wcd_mbhc *mbhc,
+ enum wcd_mbhc_plug_type plug_type)
+{
+
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ /*
+ * Do not disable micbias if recording is going on or
+ * headset is inserted on the other side of the extn
+ * cable. If headset has been detected current source
+ * needs to be kept enabled for button detection to work.
+ * If the accessory type is invalid or unsupported, we
+ * dont need to enable either of them.
+ */
+ if (det_extn_cable_en && mbhc->is_extn_cable &&
+ mbhc->mbhc_cb && mbhc->mbhc_cb->extn_use_mb &&
+ mbhc->mbhc_cb->extn_use_mb(codec)) {
+ if (plug_type == MBHC_PLUG_TYPE_HEADPHONE ||
+ plug_type == MBHC_PLUG_TYPE_HEADSET)
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+ } else {
+ if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
+ if (mbhc->is_hs_recording || mbhc->micbias_enable)
+ wcd_enable_curr_micbias(mbhc,
+ WCD_MBHC_EN_MB);
+ else if ((test_bit(WCD_MBHC_EVENT_PA_HPHL,
+ &mbhc->event_state)) ||
+ (test_bit(WCD_MBHC_EVENT_PA_HPHR,
+ &mbhc->event_state)))
+ wcd_enable_curr_micbias(mbhc,
+ WCD_MBHC_EN_PULLUP);
+ else
+ wcd_enable_curr_micbias(mbhc,
+ WCD_MBHC_EN_CS);
+ } else if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
+ } else {
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
+ }
+ }
+}
+
+static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc,
+ int *spl_hs_cnt)
+{
+ u16 hs_comp_res_1_8v = 0, hs_comp_res_2_7v = 0;
+ bool spl_hs = false;
+
+ if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+ goto exit;
+
+ /* Read back hs_comp_res @ 1.8v Micbias */
+ WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_1_8v);
+ if (!hs_comp_res_1_8v) {
+ spl_hs = false;
+ goto exit;
+ }
+
+ /* Bump up MB2 to 2.7v */
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec,
+ mbhc->mbhc_cfg->mbhc_micbias, true);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+ usleep_range(10000, 10100);
+
+ /* Read back HS_COMP_RESULT */
+ WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_2_7v);
+ if (!hs_comp_res_2_7v && hs_comp_res_1_8v)
+ spl_hs = true;
+
+ if (spl_hs && spl_hs_cnt)
+ *spl_hs_cnt += 1;
+
+ /* MB2 back to 1.8v */
+ if (*spl_hs_cnt != WCD_MBHC_SPL_HS_CNT) {
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec,
+ mbhc->mbhc_cfg->mbhc_micbias, false);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+ usleep_range(10000, 10100);
+ }
+
+ if (spl_hs)
+ pr_debug("%s: Detected special HS (%d)\n", __func__, spl_hs);
+
+exit:
+ return spl_hs;
+}
+
+static void wcd_correct_swch_plug(struct work_struct *work)
+{
+ struct wcd_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
+ unsigned long timeout;
+ u16 hs_comp_res = 0, hphl_sch = 0, mic_sch = 0, btn_result = 0;
+ bool wrk_complete = false;
+ int pt_gnd_mic_swap_cnt = 0;
+ int no_gnd_mic_swap_cnt = 0;
+ bool is_pa_on = false, spl_hs = false, spl_hs_reported = false;
+ bool micbias2 = false;
+ bool micbias1 = false;
+ int ret = 0;
+ int rc, spl_hs_count = 0;
+ int cross_conn;
+ int try = 0;
+
+ pr_debug("%s: enter\n", __func__);
+
+ mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
+ codec = mbhc->codec;
+
+ /*
+ * Enable micbias/pullup for detection in correct work.
+ * This work will get scheduled from detect_plug_type which
+ * will already request for pullup/micbias. If the pullup/micbias
+ * is handled with ref-counts by individual codec drivers, there is
+ * no need to enabale micbias/pullup here
+ */
+
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+
+
+ /* Enable HW FSM */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+ /*
+ * Check for any button press interrupts before starting 3-sec
+ * loop.
+ */
+ rc = wait_for_completion_timeout(&mbhc->btn_press_compl,
+ msecs_to_jiffies(WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS));
+
+ WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result);
+ WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
+
+ if (!rc) {
+ pr_debug("%s No btn press interrupt\n", __func__);
+ if (!btn_result && !hs_comp_res)
+ plug_type = MBHC_PLUG_TYPE_HEADSET;
+ else if (!btn_result && hs_comp_res)
+ plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
+ else
+ plug_type = MBHC_PLUG_TYPE_INVALID;
+ } else {
+ if (!btn_result && !hs_comp_res)
+ plug_type = MBHC_PLUG_TYPE_HEADPHONE;
+ else
+ plug_type = MBHC_PLUG_TYPE_INVALID;
+ }
+
+ do {
+ cross_conn = wcd_check_cross_conn(mbhc);
+ try++;
+ } while (try < GND_MIC_SWAP_THRESHOLD);
+ /*
+ * check for cross coneection 4 times.
+ * conisder the result of the fourth iteration.
+ */
+ if (cross_conn > 0) {
+ pr_debug("%s: cross con found, start polling\n",
+ __func__);
+ plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+ pr_debug("%s: Plug found, plug type is %d\n",
+ __func__, plug_type);
+ goto correct_plug_type;
+ }
+
+ if ((plug_type == MBHC_PLUG_TYPE_HEADSET ||
+ plug_type == MBHC_PLUG_TYPE_HEADPHONE) &&
+ (!wcd_swch_level_remove(mbhc))) {
+ WCD_MBHC_RSC_LOCK(mbhc);
+ wcd_mbhc_find_plug_and_report(mbhc, plug_type);
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ }
+
+correct_plug_type:
+
+ timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+ while (!time_after(jiffies, timeout)) {
+ if (mbhc->hs_detect_work_stop) {
+ pr_debug("%s: stop requested: %d\n", __func__,
+ mbhc->hs_detect_work_stop);
+ wcd_enable_curr_micbias(mbhc,
+ WCD_MBHC_EN_NONE);
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic &&
+ mbhc->micbias_enable) {
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
+ mbhc->codec, MIC_BIAS_2, false);
+ if (mbhc->mbhc_cb->set_micbias_value)
+ mbhc->mbhc_cb->set_micbias_value(
+ mbhc->codec);
+ mbhc->micbias_enable = false;
+ }
+ goto exit;
+ }
+ if (mbhc->btn_press_intr) {
+ wcd_cancel_btn_work(mbhc);
+ mbhc->btn_press_intr = false;
+ }
+ /* Toggle FSM */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+
+ /* allow sometime and re-check stop requested again */
+ msleep(20);
+ if (mbhc->hs_detect_work_stop) {
+ pr_debug("%s: stop requested: %d\n", __func__,
+ mbhc->hs_detect_work_stop);
+ wcd_enable_curr_micbias(mbhc,
+ WCD_MBHC_EN_NONE);
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic &&
+ mbhc->micbias_enable) {
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
+ mbhc->codec, MIC_BIAS_2, false);
+ if (mbhc->mbhc_cb->set_micbias_value)
+ mbhc->mbhc_cb->set_micbias_value(
+ mbhc->codec);
+ mbhc->micbias_enable = false;
+ }
+ goto exit;
+ }
+ WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
+
+ pr_debug("%s: hs_comp_res: %x\n", __func__, hs_comp_res);
+ if (mbhc->mbhc_cb->hph_pa_on_status)
+ is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(codec);
+
+ /*
+ * instead of hogging system by contineous polling, wait for
+ * sometime and re-check stop request again.
+ */
+ msleep(180);
+ if (hs_comp_res && (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) {
+ spl_hs = wcd_mbhc_check_for_spl_headset(mbhc,
+ &spl_hs_count);
+
+ if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
+ hs_comp_res = 0;
+ spl_hs = true;
+ mbhc->micbias_enable = true;
+ }
+ }
+
+ if ((!hs_comp_res) && (!is_pa_on)) {
+ /* Check for cross connection*/
+ ret = wcd_check_cross_conn(mbhc);
+ if (ret < 0) {
+ continue;
+ } else if (ret > 0) {
+ pt_gnd_mic_swap_cnt++;
+ no_gnd_mic_swap_cnt = 0;
+ if (pt_gnd_mic_swap_cnt <
+ GND_MIC_SWAP_THRESHOLD) {
+ continue;
+ } else if (pt_gnd_mic_swap_cnt >
+ GND_MIC_SWAP_THRESHOLD) {
+ /*
+ * This is due to GND/MIC switch didn't
+ * work, Report unsupported plug.
+ */
+ pr_debug("%s: switch didnt work\n",
+ __func__);
+ plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+ goto report;
+ } else {
+ plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+ }
+ } else {
+ no_gnd_mic_swap_cnt++;
+ pt_gnd_mic_swap_cnt = 0;
+ plug_type = MBHC_PLUG_TYPE_HEADSET;
+ if ((no_gnd_mic_swap_cnt <
+ GND_MIC_SWAP_THRESHOLD) &&
+ (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) {
+ continue;
+ } else {
+ no_gnd_mic_swap_cnt = 0;
+ }
+ }
+ if ((pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) &&
+ (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
+ /*
+ * if switch is toggled, check again,
+ * otherwise report unsupported plug
+ */
+ if (mbhc->mbhc_cfg->swap_gnd_mic &&
+ mbhc->mbhc_cfg->swap_gnd_mic(codec)) {
+ pr_debug("%s: US_EU gpio present,flip switch\n"
+ , __func__);
+ continue;
+ }
+ }
+ }
+
+ WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
+ WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
+ if (hs_comp_res && !(hphl_sch || mic_sch)) {
+ pr_debug("%s: cable is extension cable\n", __func__);
+ plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
+ wrk_complete = true;
+ } else {
+ pr_debug("%s: cable might be headset: %d\n", __func__,
+ plug_type);
+ if (!(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
+ plug_type = MBHC_PLUG_TYPE_HEADSET;
+ if (!spl_hs_reported &&
+ spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
+ spl_hs_reported = true;
+ WCD_MBHC_RSC_LOCK(mbhc);
+ wcd_mbhc_find_plug_and_report(mbhc,
+ plug_type);
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ continue;
+ } else if (spl_hs_reported)
+ continue;
+ /*
+ * Report headset only if not already reported
+ * and if there is not button press without
+ * release
+ */
+ if (((mbhc->current_plug !=
+ MBHC_PLUG_TYPE_HEADSET) &&
+ (mbhc->current_plug !=
+ MBHC_PLUG_TYPE_ANC_HEADPHONE)) &&
+ !wcd_swch_level_remove(mbhc) &&
+ !mbhc->btn_press_intr) {
+ pr_debug("%s: cable is %sheadset\n",
+ __func__,
+ ((spl_hs_count ==
+ WCD_MBHC_SPL_HS_CNT) ?
+ "special ":""));
+ goto report;
+ }
+ }
+ wrk_complete = false;
+ }
+ }
+ if (!wrk_complete && mbhc->btn_press_intr) {
+ pr_debug("%s: Can be slow insertion of headphone\n", __func__);
+ wcd_cancel_btn_work(mbhc);
+ plug_type = MBHC_PLUG_TYPE_HEADPHONE;
+ }
+ /*
+ * If plug_tye is headset, we might have already reported either in
+ * detect_plug-type or in above while loop, no need to report again
+ */
+ if (!wrk_complete && ((plug_type == MBHC_PLUG_TYPE_HEADSET) ||
+ (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE))) {
+ pr_debug("%s: plug_type:0x%x already reported\n",
+ __func__, mbhc->current_plug);
+ goto enable_supply;
+ }
+
+ if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH &&
+ (!det_extn_cable_en)) {
+ if (wcd_is_special_headset(mbhc)) {
+ pr_debug("%s: Special headset found %d\n",
+ __func__, plug_type);
+ plug_type = MBHC_PLUG_TYPE_HEADSET;
+ goto report;
+ }
+ }
+
+report:
+ if (wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low\n", __func__);
+ goto exit;
+ }
+ if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP && mbhc->btn_press_intr) {
+ pr_debug("%s: insertion of headphone with swap\n", __func__);
+ wcd_cancel_btn_work(mbhc);
+ plug_type = MBHC_PLUG_TYPE_HEADPHONE;
+ }
+ pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n",
+ __func__, plug_type, wrk_complete,
+ mbhc->btn_press_intr);
+ WCD_MBHC_RSC_LOCK(mbhc);
+ wcd_mbhc_find_plug_and_report(mbhc, plug_type);
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+enable_supply:
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ wcd_mbhc_update_fsm_source(mbhc, plug_type);
+ else
+ wcd_enable_mbhc_supply(mbhc, plug_type);
+exit:
+ if (mbhc->mbhc_cb->mbhc_micbias_control &&
+ !mbhc->micbias_enable)
+ mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
+ MICB_DISABLE);
+
+ /*
+ * If plug type is corrected from special headset to headphone,
+ * clear the micbias enable flag, set micbias back to 1.8V and
+ * disable micbias.
+ */
+ if (plug_type == MBHC_PLUG_TYPE_HEADPHONE &&
+ mbhc->micbias_enable) {
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ mbhc->mbhc_cb->mbhc_micbias_control(
+ codec, MIC_BIAS_2,
+ MICB_DISABLE);
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
+ codec,
+ MIC_BIAS_2, false);
+ if (mbhc->mbhc_cb->set_micbias_value) {
+ mbhc->mbhc_cb->set_micbias_value(codec);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
+ }
+ mbhc->micbias_enable = false;
+ }
+
+ if (mbhc->mbhc_cb->micbias_enable_status) {
+ micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+ MIC_BIAS_1);
+ micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+ MIC_BIAS_2);
+ }
+
+ if (mbhc->mbhc_cfg->detect_extn_cable &&
+ ((plug_type == MBHC_PLUG_TYPE_HEADPHONE) ||
+ (plug_type == MBHC_PLUG_TYPE_HEADSET)) &&
+ !mbhc->hs_detect_work_stop) {
+ WCD_MBHC_RSC_LOCK(mbhc);
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, true);
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ }
+ if (mbhc->mbhc_cb->set_cap_mode)
+ mbhc->mbhc_cb->set_cap_mode(codec, micbias1, micbias2);
+
+ if (mbhc->mbhc_cb->hph_pull_down_ctrl)
+ mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true);
+
+ mbhc->mbhc_cb->lock_sleep(mbhc, false);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ bool micbias1 = false;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+ if (mbhc->mbhc_cb->hph_pull_down_ctrl)
+ mbhc->mbhc_cb->hph_pull_down_ctrl(codec, false);
+
+ if (mbhc->mbhc_cb->micbias_enable_status)
+ micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+ MIC_BIAS_1);
+
+ if (mbhc->mbhc_cb->set_cap_mode)
+ mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true);
+
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
+ MICB_ENABLE);
+ else
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+
+ /* Re-initialize button press completion object */
+ reinit_completion(&mbhc->btn_press_compl);
+ wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
+{
+ bool detection_type = 0;
+ bool micbias1 = false;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ dev_dbg(codec->dev, "%s: enter\n", __func__);
+
+ WCD_MBHC_RSC_LOCK(mbhc);
+
+ mbhc->in_swch_irq_handler = true;
+
+ /* cancel pending button press */
+ if (wcd_cancel_btn_work(mbhc))
+ pr_debug("%s: button press is canceled\n", __func__);
+
+ WCD_MBHC_REG_READ(WCD_MBHC_MECH_DETECTION_TYPE, detection_type);
+
+ /* Set the detection type appropriately */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE,
+ !detection_type);
+
+ pr_debug("%s: mbhc->current_plug: %d detection_type: %d\n", __func__,
+ mbhc->current_plug, detection_type);
+ wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+
+ if (mbhc->mbhc_cb->micbias_enable_status)
+ micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+ MIC_BIAS_1);
+
+ if ((mbhc->current_plug == MBHC_PLUG_TYPE_NONE) &&
+ detection_type) {
+ /* Make sure MASTER_BIAS_CTL is enabled */
+ mbhc->mbhc_cb->mbhc_bias(codec, true);
+
+ if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
+ mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+ MBHC_COMMON_MICB_TAIL_CURR, true);
+
+ if (!mbhc->mbhc_cfg->hs_ext_micbias &&
+ mbhc->mbhc_cb->micb_internal)
+ /*
+ * Enable Tx2 RBias if the headset
+ * is using internal micbias
+ */
+ mbhc->mbhc_cb->micb_internal(codec, 1, true);
+
+ /* Remove micbias pulldown */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 0);
+ /* Apply trim if needed on the device */
+ if (mbhc->mbhc_cb->trim_btn_reg)
+ mbhc->mbhc_cb->trim_btn_reg(codec);
+ /* Enable external voltage source to micbias if present */
+ if (mbhc->mbhc_cb->enable_mb_source)
+ mbhc->mbhc_cb->enable_mb_source(mbhc, true);
+ mbhc->btn_press_intr = false;
+ mbhc->is_btn_press = false;
+ wcd_mbhc_detect_plug_type(mbhc);
+ } else if ((mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
+ && !detection_type) {
+ /* Disable external voltage source to micbias if present */
+ if (mbhc->mbhc_cb->enable_mb_source)
+ mbhc->mbhc_cb->enable_mb_source(mbhc, false);
+ /* Disable HW FSM */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+ if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
+ mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+ MBHC_COMMON_MICB_TAIL_CURR, false);
+
+ if (mbhc->mbhc_cb->set_cap_mode)
+ mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false);
+
+ mbhc->btn_press_intr = false;
+ mbhc->is_btn_press = false;
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM,
+ false);
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+ false);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+ 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+ wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+ } else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
+ wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
+ } else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
+ /* make sure to turn off Rbias */
+ if (mbhc->mbhc_cb->micb_internal)
+ mbhc->mbhc_cb->micb_internal(codec, 1, false);
+
+ /* Pulldown micbias */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 1);
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM,
+ false);
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+ false);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+ 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+ wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ } else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) {
+ mbhc->is_extn_cable = false;
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM,
+ false);
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+ false);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+ 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+ wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT);
+ } else if (mbhc->current_plug == MBHC_PLUG_TYPE_ANC_HEADPHONE) {
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, false);
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+ 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+ wcd_mbhc_report_plug(mbhc, 0, SND_JACK_ANC_HEADPHONE);
+ }
+ } else if (!detection_type) {
+ /* Disable external voltage source to micbias if present */
+ if (mbhc->mbhc_cb->enable_mb_source)
+ mbhc->mbhc_cb->enable_mb_source(mbhc, false);
+ /* Disable HW FSM */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+ }
+
+ mbhc->in_swch_irq_handler = false;
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
+{
+ int r = IRQ_HANDLED;
+ struct wcd_mbhc *mbhc = data;
+
+ pr_debug("%s: enter\n", __func__);
+ if (unlikely((mbhc->mbhc_cb->lock_sleep(mbhc, true)) == false)) {
+ pr_warn("%s: failed to hold suspend\n", __func__);
+ r = IRQ_NONE;
+ } else {
+ /* Call handler */
+ wcd_mbhc_swch_irq_handler(mbhc);
+ mbhc->mbhc_cb->lock_sleep(mbhc, false);
+ }
+ pr_debug("%s: leave %d\n", __func__, r);
+ return r;
+}
+
+static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
+{
+ int mask = 0;
+ int btn;
+
+ btn = mbhc->mbhc_cb->map_btn_code_to_num(mbhc->codec);
+
+ switch (btn) {
+ case 0:
+ mask = SND_JACK_BTN_0;
+ break;
+ case 1:
+ mask = SND_JACK_BTN_1;
+ break;
+ case 2:
+ mask = SND_JACK_BTN_2;
+ break;
+ case 3:
+ mask = SND_JACK_BTN_3;
+ break;
+ case 4:
+ mask = SND_JACK_BTN_4;
+ break;
+ case 5:
+ mask = SND_JACK_BTN_5;
+ break;
+ default:
+ break;
+ }
+
+ return mask;
+}
+
+static irqreturn_t wcd_mbhc_hs_ins_irq(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ bool detection_type = 0, hphl_sch = 0, mic_sch = 0;
+ u16 elect_result = 0;
+ static u16 hphl_trigerred;
+ static u16 mic_trigerred;
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->mbhc_cfg->detect_extn_cable) {
+ pr_debug("%s: Returning as Extension cable feature not enabled\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+ WCD_MBHC_RSC_LOCK(mbhc);
+
+ WCD_MBHC_REG_READ(WCD_MBHC_ELECT_DETECTION_TYPE, detection_type);
+ WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, elect_result);
+
+ pr_debug("%s: detection_type %d, elect_result %x\n", __func__,
+ detection_type, elect_result);
+ if (detection_type) {
+ /* check if both Left and MIC Schmitt triggers are triggered */
+ WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
+ WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
+ if (hphl_sch && mic_sch) {
+ /* Go for plug type determination */
+ pr_debug("%s: Go for plug type determination\n",
+ __func__);
+ goto determine_plug;
+
+ } else {
+ if (mic_sch) {
+ mic_trigerred++;
+ pr_debug("%s: Insertion MIC trigerred %d\n",
+ __func__, mic_trigerred);
+ WCD_MBHC_REG_UPDATE_BITS(
+ WCD_MBHC_ELECT_SCHMT_ISRC,
+ 0);
+ msleep(20);
+ WCD_MBHC_REG_UPDATE_BITS(
+ WCD_MBHC_ELECT_SCHMT_ISRC,
+ 1);
+ }
+ if (hphl_sch) {
+ hphl_trigerred++;
+ pr_debug("%s: Insertion HPHL trigerred %d\n",
+ __func__, hphl_trigerred);
+ }
+ if (mic_trigerred && hphl_trigerred) {
+ /* Go for plug type determination */
+ pr_debug("%s: Go for plug type determination\n",
+ __func__);
+ goto determine_plug;
+ }
+ }
+ }
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ pr_debug("%s: leave\n", __func__);
+ return IRQ_HANDLED;
+
+determine_plug:
+ /*
+ * Disable HPHL trigger and MIC Schmitt triggers.
+ * Setup for insertion detection.
+ */
+ pr_debug("%s: Disable insertion interrupt\n", __func__);
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+ false);
+
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+ hphl_trigerred = 0;
+ mic_trigerred = 0;
+ mbhc->is_extn_cable = true;
+ mbhc->btn_press_intr = false;
+ mbhc->is_btn_press = false;
+ wcd_mbhc_detect_plug_type(mbhc);
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ pr_debug("%s: leave\n", __func__);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ u8 hs_comp_result = 0, hphl_sch = 0, mic_sch = 0;
+ static u16 hphl_trigerred;
+ static u16 mic_trigerred;
+ unsigned long timeout;
+ bool removed = true;
+ int retry = 0;
+
+ pr_debug("%s: enter\n", __func__);
+
+ WCD_MBHC_RSC_LOCK(mbhc);
+
+ timeout = jiffies +
+ msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
+ do {
+ retry++;
+ /*
+ * read the result register every 10ms to look for
+ * any change in HS_COMP_RESULT bit
+ */
+ usleep_range(10000, 10100);
+ WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result);
+ pr_debug("%s: Check result reg for fake removal: hs_comp_res %x\n",
+ __func__, hs_comp_result);
+ if ((!hs_comp_result) &&
+ retry > FAKE_REM_RETRY_ATTEMPTS) {
+ removed = false;
+ break;
+ }
+ } while (!time_after(jiffies, timeout));
+
+ if (wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low ", __func__);
+ goto exit;
+ }
+ pr_debug("%s: headset %s actually removed\n", __func__,
+ removed ? "" : "not ");
+
+ WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
+ WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
+ WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result);
+
+ if (removed) {
+ if (!(hphl_sch && mic_sch && hs_comp_result)) {
+ /*
+ * extension cable is still plugged in
+ * report it as LINEOUT device
+ */
+ goto report_unplug;
+ } else {
+ if (!mic_sch) {
+ mic_trigerred++;
+ pr_debug("%s: Removal MIC trigerred %d\n",
+ __func__, mic_trigerred);
+ }
+ if (!hphl_sch) {
+ hphl_trigerred++;
+ pr_debug("%s: Removal HPHL trigerred %d\n",
+ __func__, hphl_trigerred);
+ }
+ if (mic_trigerred && hphl_trigerred) {
+ /*
+ * extension cable is still plugged in
+ * report it as LINEOUT device
+ */
+ goto report_unplug;
+ }
+ }
+ }
+exit:
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ pr_debug("%s: leave\n", __func__);
+ return IRQ_HANDLED;
+
+report_unplug:
+
+ /* cancel pending button press */
+ if (wcd_cancel_btn_work(mbhc))
+ pr_debug("%s: button press is canceled\n", __func__);
+ /* cancel correct work function */
+ wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+
+ pr_debug("%s: Report extension cable\n", __func__);
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ /*
+ * If PA is enabled HPHL schmitt trigger can
+ * be unreliable, make sure to disable it
+ */
+ if (test_bit(WCD_MBHC_EVENT_PA_HPHL,
+ &mbhc->event_state))
+ wcd_mbhc_set_and_turnoff_hph_padac(mbhc);
+ /*
+ * Disable HPHL trigger and MIC Schmitt triggers.
+ * Setup for insertion detection.
+ */
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM,
+ false);
+ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
+ /* Disable HW FSM */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 3);
+
+ /* Set the detection type appropriately */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, 1);
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+ true);
+ hphl_trigerred = 0;
+ mic_trigerred = 0;
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ pr_debug("%s: leave\n", __func__);
+ return IRQ_HANDLED;
+}
+
+static void wcd_btn_lpress_fn(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd_mbhc *mbhc;
+ s16 btn_result = 0;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork);
+
+ WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result);
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
+ pr_debug("%s: Reporting long button press event, btn_result: %d\n",
+ __func__, btn_result);
+ wcd_mbhc_jack_report(mbhc, &mbhc->button_jack,
+ mbhc->buttons_pressed, mbhc->buttons_pressed);
+ }
+ pr_debug("%s: leave\n", __func__);
+ mbhc->mbhc_cb->lock_sleep(mbhc, false);
+}
+
+static bool wcd_mbhc_fw_validate(const void *data, size_t size)
+{
+ u32 cfg_offset;
+ struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+ struct firmware_cal fw;
+
+ fw.data = (void *)data;
+ fw.size = size;
+
+ if (fw.size < WCD_MBHC_CAL_MIN_SIZE)
+ return false;
+
+ /*
+ * Previous check guarantees that there is enough fw data up
+ * to num_btn
+ */
+ btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(fw.data);
+ cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data);
+ if (fw.size < (cfg_offset + WCD_MBHC_CAL_BTN_SZ(btn_cfg)))
+ return false;
+
+ return true;
+}
+
+static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ int mask;
+ unsigned long msec_val;
+
+ pr_debug("%s: enter\n", __func__);
+ complete(&mbhc->btn_press_compl);
+ WCD_MBHC_RSC_LOCK(mbhc);
+ wcd_cancel_btn_work(mbhc);
+ if (wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low ", __func__);
+ goto done;
+ }
+
+ mbhc->is_btn_press = true;
+ msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport);
+ pr_debug("%s: msec_val = %ld\n", __func__, msec_val);
+ if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN) {
+ pr_debug("%s: Too short, ignore button press\n", __func__);
+ goto done;
+ }
+
+ /* If switch interrupt already kicked in, ignore button press */
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug("%s: Swtich level changed, ignore button press\n",
+ __func__);
+ goto done;
+ }
+ mask = wcd_mbhc_get_button_mask(mbhc);
+ if (mask == SND_JACK_BTN_0)
+ mbhc->btn_press_intr = true;
+
+ if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET) {
+ pr_debug("%s: Plug isn't headset, ignore button press\n",
+ __func__);
+ goto done;
+ }
+ mbhc->buttons_pressed |= mask;
+ mbhc->mbhc_cb->lock_sleep(mbhc, true);
+ if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
+ msecs_to_jiffies(400)) == 0) {
+ WARN(1, "Button pressed twice without release event\n");
+ mbhc->mbhc_cb->lock_sleep(mbhc, false);
+ }
+done:
+ pr_debug("%s: leave\n", __func__);
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_release_handler(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ int ret;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD_MBHC_RSC_LOCK(mbhc);
+ if (wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low ", __func__);
+ goto exit;
+ }
+
+ if (mbhc->is_btn_press) {
+ mbhc->is_btn_press = false;
+ } else {
+ pr_debug("%s: This release is for fake btn press\n", __func__);
+ goto exit;
+ }
+
+ /*
+ * If current plug is headphone then there is no chance to
+ * get btn release interrupt, so connected cable should be
+ * headset not headphone.
+ */
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
+ wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
+ goto exit;
+
+ }
+ if (mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK) {
+ ret = wcd_cancel_btn_work(mbhc);
+ if (ret == 0) {
+ pr_debug("%s: Reporting long button release event\n",
+ __func__);
+ wcd_mbhc_jack_report(mbhc, &mbhc->button_jack,
+ 0, mbhc->buttons_pressed);
+ } else {
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug("%s: Switch irq kicked in, ignore\n",
+ __func__);
+ } else {
+ pr_debug("%s: Reporting btn press\n",
+ __func__);
+ wcd_mbhc_jack_report(mbhc,
+ &mbhc->button_jack,
+ mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+ pr_debug("%s: Reporting btn release\n",
+ __func__);
+ wcd_mbhc_jack_report(mbhc,
+ &mbhc->button_jack,
+ 0, mbhc->buttons_pressed);
+ }
+ }
+ mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
+ }
+exit:
+ pr_debug("%s: leave\n", __func__);
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ int val;
+
+ pr_debug("%s: received HPHL OCP irq\n", __func__);
+ if (mbhc) {
+ if (mbhc->mbhc_cb->hph_register_recovery) {
+ if (mbhc->mbhc_cb->hph_register_recovery(mbhc)) {
+ WCD_MBHC_REG_READ(WCD_MBHC_HPHR_OCP_STATUS,
+ val);
+ if ((val != -EINVAL) && val)
+ mbhc->is_hph_ocp_pending = true;
+ goto done;
+ }
+ }
+
+ if (mbhc->hphlocp_cnt < OCP_ATTEMPT) {
+ mbhc->hphlocp_cnt++;
+ pr_debug("%s: retry, hphlocp_cnt: %d\n", __func__,
+ mbhc->hphlocp_cnt);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
+ } else {
+ mbhc->mbhc_cb->irq_control(mbhc->codec,
+ mbhc->intr_ids->hph_left_ocp,
+ false);
+ mbhc->hph_status |= SND_JACK_OC_HPHL;
+ wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+ mbhc->hph_status,
+ WCD_MBHC_JACK_MASK);
+ }
+ } else {
+ pr_err("%s: Bad wcd9xxx_spmi private data\n", __func__);
+ }
+done:
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+
+ pr_debug("%s: received HPHR OCP irq\n", __func__);
+
+ if (!mbhc) {
+ pr_err("%s: Bad mbhc private data\n", __func__);
+ goto done;
+ }
+
+ if (mbhc->is_hph_ocp_pending) {
+ mbhc->is_hph_ocp_pending = false;
+ goto done;
+ }
+
+ if (mbhc->mbhc_cb->hph_register_recovery) {
+ if (mbhc->mbhc_cb->hph_register_recovery(mbhc))
+ /* register corruption, hence reset registers */
+ goto done;
+ }
+ if (mbhc->hphrocp_cnt < OCP_ATTEMPT) {
+ mbhc->hphrocp_cnt++;
+ pr_debug("%s: retry, hphrocp_cnt: %d\n", __func__,
+ mbhc->hphrocp_cnt);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
+ } else {
+ mbhc->mbhc_cb->irq_control(mbhc->codec,
+ mbhc->intr_ids->hph_right_ocp,
+ false);
+ mbhc->hph_status |= SND_JACK_OC_HPHR;
+ wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+ mbhc->hph_status, WCD_MBHC_JACK_MASK);
+ }
+done:
+ return IRQ_HANDLED;
+}
+
+static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD_MBHC_RSC_LOCK(mbhc);
+
+ /* enable HS detection */
+ if (mbhc->mbhc_cb->hph_pull_up_control)
+ mbhc->mbhc_cb->hph_pull_up_control(codec, I_DEFAULT);
+ else
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
+
+ if (mbhc->mbhc_cfg->moisture_en && mbhc->mbhc_cb->mbhc_moisture_config)
+ mbhc->mbhc_cb->mbhc_moisture_config(mbhc);
+
+ /*
+ * For USB analog we need to override the switch configuration.
+ * Also, disable hph_l pull-up current source as HS_DET_L is driven
+ * by an external source
+ */
+ if (mbhc->mbhc_cfg->enable_usbc_analog) {
+ mbhc->hphl_swh = 1;
+ mbhc->gnd_swh = 1;
+
+ if (mbhc->mbhc_cb->hph_pull_up_control)
+ mbhc->mbhc_cb->hph_pull_up_control(codec, I_OFF);
+ else
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_CTRL,
+ 0);
+ }
+
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PLUG_TYPE, mbhc->hphl_swh);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_GND_PLUG_TYPE, mbhc->gnd_swh);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1);
+ if (mbhc->mbhc_cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl)
+ mbhc->mbhc_cb->mbhc_gnd_det_ctrl(codec, true);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1);
+
+ if (mbhc->mbhc_cfg->enable_usbc_analog) {
+ /* Insertion debounce set to 48ms */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_INSREM_DBNC, 4);
+ } else {
+ /* Insertion debounce set to 96ms */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_INSREM_DBNC, 6);
+ }
+
+ /* Button Debounce set to 16ms */
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_DBNC, 2);
+
+ /* Enable micbias ramp */
+ if (mbhc->mbhc_cb->mbhc_micb_ramp_control)
+ mbhc->mbhc_cb->mbhc_micb_ramp_control(codec, true);
+ /* enable bias */
+ mbhc->mbhc_cb->mbhc_bias(codec, true);
+ /* enable MBHC clock */
+ if (mbhc->mbhc_cb->clk_setup)
+ mbhc->mbhc_cb->clk_setup(codec, true);
+
+ /* program HS_VREF value */
+ wcd_program_hs_vref(mbhc);
+
+ wcd_program_btn_threshold(mbhc, false);
+
+ INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
+
+ reinit_completion(&mbhc->btn_press_compl);
+
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ pr_debug("%s: leave\n", __func__);
+ return ret;
+}
+
+static void wcd_mbhc_fw_read(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ const struct firmware *fw;
+ struct firmware_cal *fw_data = NULL;
+ int ret = -1, retry = 0;
+ bool use_default_cal = false;
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd_mbhc, mbhc_firmware_dwork);
+ codec = mbhc->codec;
+
+ while (retry < FW_READ_ATTEMPTS) {
+ retry++;
+ pr_debug("%s:Attempt %d to request MBHC firmware\n",
+ __func__, retry);
+ if (mbhc->mbhc_cb->get_hwdep_fw_cal)
+ fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(mbhc,
+ WCD9XXX_MBHC_CAL);
+ if (!fw_data)
+ ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
+ codec->dev);
+ /*
+ * if request_firmware and hwdep cal both fail then
+ * sleep for 4sec for the userspace to send data to kernel
+ * retry for few times before bailing out
+ */
+ if ((ret != 0) && !fw_data) {
+ usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT +
+ WCD_MBHC_USLEEP_RANGE_MARGIN_US);
+ } else {
+ pr_debug("%s: MBHC Firmware read succesful\n",
+ __func__);
+ break;
+ }
+ }
+ if (!fw_data)
+ pr_debug("%s: using request_firmware\n", __func__);
+ else
+ pr_debug("%s: using hwdep cal\n", __func__);
+
+ if (ret != 0 && !fw_data) {
+ pr_err("%s: Cannot load MBHC firmware use default cal\n",
+ __func__);
+ use_default_cal = true;
+ }
+ if (!use_default_cal) {
+ const void *data;
+ size_t size;
+
+ if (fw_data) {
+ data = fw_data->data;
+ size = fw_data->size;
+ } else {
+ data = fw->data;
+ size = fw->size;
+ }
+ if (wcd_mbhc_fw_validate(data, size) == false) {
+ pr_err("%s: Invalid MBHC cal data size use default cal\n",
+ __func__);
+ if (!fw_data)
+ release_firmware(fw);
+ } else {
+ if (fw_data) {
+ mbhc->mbhc_cfg->calibration =
+ (void *)fw_data->data;
+ mbhc->mbhc_cal = fw_data;
+ } else {
+ mbhc->mbhc_cfg->calibration =
+ (void *)fw->data;
+ mbhc->mbhc_fw = fw;
+ }
+ }
+
+ }
+
+ (void) wcd_mbhc_initialise(mbhc);
+}
+
+int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc)
+{
+ enum snd_jack_types type;
+ int i, ret, result = 0;
+ int *btn_key_code;
+
+ btn_key_code = mbhc->mbhc_cfg->key_code;
+
+ for (i = 0 ; i < WCD_MBHC_KEYCODE_NUM ; i++) {
+ if (btn_key_code[i] != 0) {
+ switch (i) {
+ case 0:
+ type = SND_JACK_BTN_0;
+ break;
+ case 1:
+ type = SND_JACK_BTN_1;
+ break;
+ case 2:
+ type = SND_JACK_BTN_2;
+ break;
+ case 3:
+ type = SND_JACK_BTN_3;
+ break;
+ case 4:
+ type = SND_JACK_BTN_4;
+ break;
+ case 5:
+ type = SND_JACK_BTN_5;
+ break;
+ default:
+ WARN_ONCE(1, "Wrong button number:%d\n", i);
+ result = -1;
+ return result;
+ }
+ ret = snd_jack_set_key(mbhc->button_jack.jack,
+ type,
+ btn_key_code[i]);
+ if (ret) {
+ pr_err("%s: Failed to set code for %d\n",
+ __func__, btn_key_code[i]);
+ result = -1;
+ return result;
+ }
+ input_set_capability(
+ mbhc->button_jack.jack->input_dev,
+ EV_KEY, btn_key_code[i]);
+ pr_debug("%s: set btn%d key code:%d\n", __func__,
+ i, btn_key_code[i]);
+ }
+ }
+ if (btn_key_code[0])
+ mbhc->is_btn_already_regd = true;
+ return result;
+}
+
+static int wcd_mbhc_usb_c_analog_setup_gpios(struct wcd_mbhc *mbhc,
+ bool active)
+{
+ int rc = 0;
+ struct usbc_ana_audio_config *config =
+ &mbhc->mbhc_cfg->usbc_analog_cfg;
+ union power_supply_propval pval;
+
+ dev_dbg(mbhc->codec->dev, "%s: setting GPIOs active = %d\n",
+ __func__, active);
+
+ memset(&pval, 0, sizeof(pval));
+
+ if (active) {
+ pval.intval = POWER_SUPPLY_TYPEC_PR_SOURCE;
+ if (power_supply_set_property(mbhc->usb_psy,
+ POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval))
+ dev_info(mbhc->codec->dev, "%s: force PR_SOURCE mode unsuccessful\n",
+ __func__);
+ else
+ mbhc->usbc_force_pr_mode = true;
+
+ if (config->usbc_en1_gpio_p)
+ rc = msm_cdc_pinctrl_select_active_state(
+ config->usbc_en1_gpio_p);
+ if (rc == 0 && config->usbc_en2n_gpio_p)
+ rc = msm_cdc_pinctrl_select_active_state(
+ config->usbc_en2n_gpio_p);
+ if (rc == 0 && config->usbc_force_gpio_p)
+ rc = msm_cdc_pinctrl_select_active_state(
+ config->usbc_force_gpio_p);
+ mbhc->usbc_mode = POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER;
+ } else {
+ /* no delay is required when disabling GPIOs */
+ if (config->usbc_en2n_gpio_p)
+ msm_cdc_pinctrl_select_sleep_state(
+ config->usbc_en2n_gpio_p);
+ if (config->usbc_en1_gpio_p)
+ msm_cdc_pinctrl_select_sleep_state(
+ config->usbc_en1_gpio_p);
+ if (config->usbc_force_gpio_p)
+ msm_cdc_pinctrl_select_sleep_state(
+ config->usbc_force_gpio_p);
+
+ if (mbhc->usbc_force_pr_mode) {
+ pval.intval = POWER_SUPPLY_TYPEC_PR_DUAL;
+ if (power_supply_set_property(mbhc->usb_psy,
+ POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval))
+ dev_info(mbhc->codec->dev, "%s: force PR_DUAL mode unsuccessful\n",
+ __func__);
+
+ mbhc->usbc_force_pr_mode = false;
+ }
+
+ mbhc->usbc_mode = POWER_SUPPLY_TYPEC_NONE;
+ }
+
+ return rc;
+}
+
+/* workqueue */
+static void wcd_mbhc_usbc_analog_work_fn(struct work_struct *work)
+{
+ struct wcd_mbhc *mbhc =
+ container_of(work, struct wcd_mbhc, usbc_analog_work);
+
+ wcd_mbhc_usb_c_analog_setup_gpios(mbhc,
+ mbhc->usbc_mode != POWER_SUPPLY_TYPEC_NONE);
+}
+
+/* this callback function is used to process PMI notification */
+static int wcd_mbhc_usb_c_event_changed(struct notifier_block *nb,
+ unsigned long evt, void *ptr)
+{
+ int ret;
+ union power_supply_propval mode;
+ struct wcd_mbhc *mbhc = container_of(nb, struct wcd_mbhc, psy_nb);
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ if (ptr != mbhc->usb_psy || evt != PSY_EVENT_PROP_CHANGED)
+ return 0;
+
+ ret = power_supply_get_property(mbhc->usb_psy,
+ POWER_SUPPLY_PROP_TYPEC_MODE, &mode);
+ if (ret) {
+ dev_err(codec->dev, "%s: Unable to read USB TYPEC_MODE: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ dev_dbg(codec->dev, "%s: USB change event received\n",
+ __func__);
+ dev_dbg(codec->dev, "%s: supply mode %d, expected %d\n", __func__,
+ mode.intval, POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER);
+
+ switch (mode.intval) {
+ case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER:
+ case POWER_SUPPLY_TYPEC_NONE:
+ dev_dbg(codec->dev, "%s: usbc_mode: %d; mode.intval: %d\n",
+ __func__, mbhc->usbc_mode, mode.intval);
+
+ if (mbhc->usbc_mode == mode.intval)
+ break; /* filter notifications received before */
+ mbhc->usbc_mode = mode.intval;
+
+ dev_dbg(codec->dev, "%s: queueing usbc_analog_work\n",
+ __func__);
+ schedule_work(&mbhc->usbc_analog_work);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+/* PMI registration code */
+static int wcd_mbhc_usb_c_analog_init(struct wcd_mbhc *mbhc)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ dev_dbg(mbhc->codec->dev, "%s: usb-c analog setup start\n", __func__);
+ INIT_WORK(&mbhc->usbc_analog_work, wcd_mbhc_usbc_analog_work_fn);
+
+ mbhc->usb_psy = power_supply_get_by_name("usb");
+ if (IS_ERR_OR_NULL(mbhc->usb_psy)) {
+ dev_err(codec->dev, "%s: could not get USB psy info\n",
+ __func__);
+ ret = -EPROBE_DEFER;
+ if (IS_ERR(mbhc->usb_psy))
+ ret = PTR_ERR(mbhc->usb_psy);
+ mbhc->usb_psy = NULL;
+ goto err;
+ }
+
+ ret = wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false);
+ if (ret) {
+ dev_err(codec->dev, "%s: error while setting USBC ana gpios\n",
+ __func__);
+ goto err;
+ }
+
+ mbhc->psy_nb.notifier_call = wcd_mbhc_usb_c_event_changed;
+ mbhc->psy_nb.priority = 0;
+ ret = power_supply_reg_notifier(&mbhc->psy_nb);
+ if (ret) {
+ dev_err(codec->dev, "%s: power supply registration failed\n",
+ __func__);
+ goto err;
+ }
+
+ /*
+ * as part of the init sequence check if there is a connected
+ * USB C analog adapter
+ */
+ dev_dbg(mbhc->codec->dev, "%s: verify if USB adapter is already inserted\n",
+ __func__);
+ ret = wcd_mbhc_usb_c_event_changed(&mbhc->psy_nb,
+ PSY_EVENT_PROP_CHANGED,
+ mbhc->usb_psy);
+
+err:
+ return ret;
+}
+
+static int wcd_mbhc_usb_c_analog_deinit(struct wcd_mbhc *mbhc)
+{
+ wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false);
+
+ /* deregister from PMI */
+ power_supply_unreg_notifier(&mbhc->psy_nb);
+
+ return 0;
+}
+
+static int wcd_mbhc_init_gpio(struct wcd_mbhc *mbhc,
+ struct wcd_mbhc_config *mbhc_cfg,
+ const char *gpio_dt_str,
+ int *gpio, struct device_node **gpio_dn)
+{
+ int rc = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct snd_soc_card *card = codec->component.card;
+
+ dev_dbg(mbhc->codec->dev, "%s: gpio %s\n", __func__, gpio_dt_str);
+
+ *gpio_dn = of_parse_phandle(card->dev->of_node, gpio_dt_str, 0);
+
+ if (!(*gpio_dn)) {
+ *gpio = of_get_named_gpio(card->dev->of_node, gpio_dt_str, 0);
+ if (!gpio_is_valid(*gpio)) {
+ dev_err(card->dev, "%s, property %s not in node %s",
+ __func__, gpio_dt_str,
+ card->dev->of_node->full_name);
+ rc = -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg)
+{
+ int rc = 0;
+ struct usbc_ana_audio_config *config;
+ struct snd_soc_codec *codec;
+ struct snd_soc_card *card;
+ const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported";
+
+ if (!mbhc || !mbhc_cfg)
+ return -EINVAL;
+
+ config = &mbhc_cfg->usbc_analog_cfg;
+ codec = mbhc->codec;
+ card = codec->component.card;
+
+ /* update the mbhc config */
+ mbhc->mbhc_cfg = mbhc_cfg;
+
+ dev_dbg(mbhc->codec->dev, "%s: enter\n", __func__);
+
+ /* check if USB C analog is defined on device tree */
+ mbhc_cfg->enable_usbc_analog = 0;
+ if (of_find_property(card->dev->of_node, usb_c_dt, NULL)) {
+ rc = of_property_read_u32(card->dev->of_node, usb_c_dt,
+ &mbhc_cfg->enable_usbc_analog);
+ }
+ if (mbhc_cfg->enable_usbc_analog == 0 || rc != 0) {
+ dev_info(card->dev,
+ "%s: %s in dt node is missing or false\n",
+ __func__, usb_c_dt);
+ dev_info(card->dev,
+ "%s: skipping USB c analog configuration\n", __func__);
+ }
+
+ /* initialize GPIOs */
+ if (mbhc_cfg->enable_usbc_analog) {
+ dev_dbg(mbhc->codec->dev, "%s: usbc analog enabled\n",
+ __func__);
+ rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg,
+ "qcom,usbc-analog-en1_gpio",
+ &config->usbc_en1_gpio,
+ &config->usbc_en1_gpio_p);
+ if (rc)
+ goto err;
+
+ rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg,
+ "qcom,usbc-analog-en2_n_gpio",
+ &config->usbc_en2n_gpio,
+ &config->usbc_en2n_gpio_p);
+ if (rc)
+ goto err;
+
+ if (of_find_property(card->dev->of_node,
+ "qcom,usbc-analog-force_detect_gpio",
+ NULL)) {
+ rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg,
+ "qcom,usbc-analog-force_detect_gpio",
+ &config->usbc_force_gpio,
+ &config->usbc_force_gpio_p);
+ if (rc)
+ goto err;
+ }
+
+ dev_dbg(mbhc->codec->dev, "%s: calling usb_c_analog_init\n",
+ __func__);
+ /* init PMI notifier */
+ rc = wcd_mbhc_usb_c_analog_init(mbhc);
+ if (rc) {
+ rc = EPROBE_DEFER;
+ goto err;
+ }
+ }
+
+ /* Set btn key code */
+ if ((!mbhc->is_btn_already_regd) && wcd_mbhc_set_keycode(mbhc))
+ pr_err("Set btn key code error!!!\n");
+
+ if (!mbhc->mbhc_cfg->read_fw_bin ||
+ (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) ||
+ (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) {
+ rc = wcd_mbhc_initialise(mbhc);
+ } else {
+ if (!mbhc->mbhc_fw || !mbhc->mbhc_cal)
+ schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
+ usecs_to_jiffies(FW_READ_TIMEOUT));
+ else
+ pr_err("%s: Skipping to read mbhc fw, 0x%pK %pK\n",
+ __func__, mbhc->mbhc_fw, mbhc->mbhc_cal);
+ }
+
+ return rc;
+err:
+ if (config->usbc_en1_gpio > 0) {
+ dev_dbg(card->dev, "%s free usb en1 gpio %d\n",
+ __func__, config->usbc_en1_gpio);
+ gpio_free(config->usbc_en1_gpio);
+ config->usbc_en1_gpio = 0;
+ }
+ if (config->usbc_en2n_gpio > 0) {
+ dev_dbg(card->dev, "%s free usb_en2 gpio %d\n",
+ __func__, config->usbc_en2n_gpio);
+ gpio_free(config->usbc_en2n_gpio);
+ config->usbc_en2n_gpio = 0;
+ }
+ if (config->usbc_force_gpio > 0) {
+ dev_dbg(card->dev, "%s free usb_force gpio %d\n",
+ __func__, config->usbc_force_gpio);
+ gpio_free(config->usbc_force_gpio);
+ config->usbc_force_gpio = 0;
+ }
+ if (config->usbc_en1_gpio_p)
+ of_node_put(config->usbc_en1_gpio_p);
+ if (config->usbc_en2n_gpio_p)
+ of_node_put(config->usbc_en2n_gpio_p);
+ if (config->usbc_force_gpio_p)
+ of_node_put(config->usbc_force_gpio_p);
+ dev_dbg(mbhc->codec->dev, "%s: leave %d\n", __func__, rc);
+ return rc;
+}
+EXPORT_SYMBOL(wcd_mbhc_start);
+
+void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
+{
+ struct usbc_ana_audio_config *config = &mbhc->mbhc_cfg->usbc_analog_cfg;
+
+ pr_debug("%s: enter\n", __func__);
+
+ if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE) {
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->skip_imped_detect)
+ mbhc->mbhc_cb->skip_imped_detect(mbhc->codec);
+ }
+ mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
+ mbhc->hph_status = 0;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->irq_control) {
+ mbhc->mbhc_cb->irq_control(mbhc->codec,
+ mbhc->intr_ids->hph_left_ocp,
+ false);
+ mbhc->mbhc_cb->irq_control(mbhc->codec,
+ mbhc->intr_ids->hph_right_ocp,
+ false);
+ }
+ if (mbhc->mbhc_fw || mbhc->mbhc_cal) {
+ cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
+ if (!mbhc->mbhc_cal)
+ release_firmware(mbhc->mbhc_fw);
+ mbhc->mbhc_fw = NULL;
+ mbhc->mbhc_cal = NULL;
+ }
+
+ if (mbhc->mbhc_cfg->enable_usbc_analog) {
+ wcd_mbhc_usb_c_analog_deinit(mbhc);
+ /* free GPIOs */
+ if (config->usbc_en1_gpio > 0)
+ gpio_free(config->usbc_en1_gpio);
+ if (config->usbc_en2n_gpio > 0)
+ gpio_free(config->usbc_en2n_gpio);
+ if (config->usbc_force_gpio)
+ gpio_free(config->usbc_force_gpio);
+
+ if (config->usbc_en1_gpio_p)
+ of_node_put(config->usbc_en1_gpio_p);
+ if (config->usbc_en2n_gpio_p)
+ of_node_put(config->usbc_en2n_gpio_p);
+ if (config->usbc_force_gpio_p)
+ of_node_put(config->usbc_force_gpio_p);
+ }
+
+ pr_debug("%s: leave\n", __func__);
+}
+EXPORT_SYMBOL(wcd_mbhc_stop);
+
+/*
+ * wcd_mbhc_init : initialize MBHC internal structures.
+ *
+ * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
+ */
+int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec,
+ const struct wcd_mbhc_cb *mbhc_cb,
+ const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
+ struct wcd_mbhc_register *wcd_mbhc_regs,
+ bool impedance_det_en)
+{
+ int ret = 0;
+ int hph_swh = 0;
+ int gnd_swh = 0;
+ u32 hph_moist_config[3];
+ struct snd_soc_card *card = codec->component.card;
+ const char *hph_switch = "qcom,msm-mbhc-hphl-swh";
+ const char *gnd_switch = "qcom,msm-mbhc-gnd-swh";
+
+ pr_debug("%s: enter\n", __func__);
+
+ ret = of_property_read_u32(card->dev->of_node, hph_switch, &hph_swh);
+ if (ret) {
+ dev_err(card->dev,
+ "%s: missing %s in dt node\n", __func__, hph_switch);
+ goto err;
+ }
+
+ ret = of_property_read_u32(card->dev->of_node, gnd_switch, &gnd_swh);
+ if (ret) {
+ dev_err(card->dev,
+ "%s: missing %s in dt node\n", __func__, gnd_switch);
+ goto err;
+ }
+
+ ret = of_property_read_u32_array(card->dev->of_node,
+ "qcom,msm-mbhc-moist-cfg",
+ hph_moist_config, 3);
+ if (ret) {
+ dev_dbg(card->dev, "%s: no qcom,msm-mbhc-moist-cfg in DT\n",
+ __func__);
+ mbhc->moist_vref = V_45_MV;
+ mbhc->moist_iref = I_3P0_UA;
+ mbhc->moist_rref = R_24_KOHM;
+ } else {
+ mbhc->moist_vref = hph_moist_config[0];
+ mbhc->moist_iref = hph_moist_config[1];
+ mbhc->moist_rref = hph_moist_config[2];
+ }
+
+ mbhc->in_swch_irq_handler = false;
+ mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
+ mbhc->is_btn_press = false;
+ mbhc->codec = codec;
+ mbhc->intr_ids = mbhc_cdc_intr_ids;
+ mbhc->impedance_detect = impedance_det_en;
+ mbhc->hphl_swh = hph_swh;
+ mbhc->gnd_swh = gnd_swh;
+ mbhc->micbias_enable = false;
+ mbhc->mbhc_cb = mbhc_cb;
+ mbhc->btn_press_intr = false;
+ mbhc->is_hs_recording = false;
+ mbhc->is_extn_cable = false;
+ mbhc->hph_type = WCD_MBHC_HPH_NONE;
+ mbhc->wcd_mbhc_regs = wcd_mbhc_regs;
+
+ if (mbhc->intr_ids == NULL) {
+ pr_err("%s: Interrupt mapping not provided\n", __func__);
+ return -EINVAL;
+ }
+ if (!mbhc->wcd_mbhc_regs) {
+ dev_err(codec->dev, "%s: mbhc registers are not defined\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Check if IRQ and other required callbacks are defined or not */
+ if (!mbhc_cb || !mbhc_cb->request_irq || !mbhc_cb->irq_control ||
+ !mbhc_cb->free_irq || !mbhc_cb->map_btn_code_to_num ||
+ !mbhc_cb->lock_sleep || !mbhc_cb->mbhc_bias ||
+ !mbhc_cb->set_btn_thr) {
+ dev_err(codec->dev, "%s: required mbhc callbacks are not defined\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (mbhc->headset_jack.jack == NULL) {
+ ret = snd_soc_card_jack_new(codec->component.card,
+ "Headset Jack", WCD_MBHC_JACK_MASK,
+ &mbhc->headset_jack, NULL, 0);
+ if (ret) {
+ pr_err("%s: Failed to create new jack\n", __func__);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new(codec->component.card,
+ "Button Jack",
+ WCD_MBHC_JACK_BUTTON_MASK,
+ &mbhc->button_jack, NULL, 0);
+ if (ret) {
+ pr_err("Failed to create new jack\n");
+ return ret;
+ }
+
+ ret = snd_jack_set_key(mbhc->button_jack.jack,
+ SND_JACK_BTN_0,
+ KEY_MEDIA);
+ if (ret) {
+ pr_err("%s: Failed to set code for btn-0\n",
+ __func__);
+ return ret;
+ }
+
+ set_bit(INPUT_PROP_NO_DUMMY_RELEASE,
+ mbhc->button_jack.jack->input_dev->propbit);
+
+ INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
+ wcd_mbhc_fw_read);
+ INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn);
+ }
+ mutex_init(&mbhc->hphl_pa_lock);
+ mutex_init(&mbhc->hphr_pa_lock);
+ init_completion(&mbhc->btn_press_compl);
+
+ /* Register event notifier */
+ mbhc->nblock.notifier_call = wcd_event_notify;
+ if (mbhc->mbhc_cb->register_notifier) {
+ ret = mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock,
+ true);
+ if (ret) {
+ pr_err("%s: Failed to register notifier %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ init_waitqueue_head(&mbhc->wait_btn_press);
+ mutex_init(&mbhc->codec_resource_lock);
+
+ ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->mbhc_sw_intr,
+ wcd_mbhc_mech_plug_detect_irq,
+ "mbhc sw intr", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
+ mbhc->intr_ids->mbhc_sw_intr, ret);
+ goto err_mbhc_sw_irq;
+ }
+
+ ret = mbhc->mbhc_cb->request_irq(codec,
+ mbhc->intr_ids->mbhc_btn_press_intr,
+ wcd_mbhc_btn_press_handler,
+ "Button Press detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ mbhc->intr_ids->mbhc_btn_press_intr);
+ goto err_btn_press_irq;
+ }
+
+ ret = mbhc->mbhc_cb->request_irq(codec,
+ mbhc->intr_ids->mbhc_btn_release_intr,
+ wcd_mbhc_release_handler,
+ "Button Release detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ mbhc->intr_ids->mbhc_btn_release_intr);
+ goto err_btn_release_irq;
+ }
+
+ ret = mbhc->mbhc_cb->request_irq(codec,
+ mbhc->intr_ids->mbhc_hs_ins_intr,
+ wcd_mbhc_hs_ins_irq,
+ "Elect Insert", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ mbhc->intr_ids->mbhc_hs_ins_intr);
+ goto err_mbhc_hs_ins_irq;
+ }
+ mbhc->mbhc_cb->irq_control(codec, mbhc->intr_ids->mbhc_hs_ins_intr,
+ false);
+ clear_bit(WCD_MBHC_ELEC_HS_INS, &mbhc->intr_status);
+
+ ret = mbhc->mbhc_cb->request_irq(codec,
+ mbhc->intr_ids->mbhc_hs_rem_intr,
+ wcd_mbhc_hs_rem_irq,
+ "Elect Remove", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ mbhc->intr_ids->mbhc_hs_rem_intr);
+ goto err_mbhc_hs_rem_irq;
+ }
+ mbhc->mbhc_cb->irq_control(codec, mbhc->intr_ids->mbhc_hs_rem_intr,
+ false);
+ clear_bit(WCD_MBHC_ELEC_HS_REM, &mbhc->intr_status);
+
+ ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->hph_left_ocp,
+ wcd_mbhc_hphl_ocp_irq, "HPH_L OCP detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ mbhc->intr_ids->hph_left_ocp);
+ goto err_hphl_ocp_irq;
+ }
+
+ ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->hph_right_ocp,
+ wcd_mbhc_hphr_ocp_irq, "HPH_R OCP detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ mbhc->intr_ids->hph_right_ocp);
+ goto err_hphr_ocp_irq;
+ }
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+
+err_hphr_ocp_irq:
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_left_ocp, mbhc);
+err_hphl_ocp_irq:
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
+err_mbhc_hs_rem_irq:
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
+err_mbhc_hs_ins_irq:
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_release_intr,
+ mbhc);
+err_btn_release_irq:
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_press_intr,
+ mbhc);
+err_btn_press_irq:
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_sw_intr, mbhc);
+err_mbhc_sw_irq:
+ if (mbhc->mbhc_cb->register_notifier)
+ mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false);
+ mutex_destroy(&mbhc->codec_resource_lock);
+err:
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL(wcd_mbhc_init);
+
+void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_sw_intr, mbhc);
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_press_intr,
+ mbhc);
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_release_intr,
+ mbhc);
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_left_ocp, mbhc);
+ mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_right_ocp, mbhc);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->register_notifier)
+ mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false);
+ wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+ mutex_destroy(&mbhc->codec_resource_lock);
+ mutex_destroy(&mbhc->hphl_pa_lock);
+ mutex_destroy(&mbhc->hphr_pa_lock);
+}
+EXPORT_SYMBOL(wcd_mbhc_deinit);
+
+MODULE_DESCRIPTION("wcd MBHC v2 module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h
new file mode 100644
index 000000000000..09b9307ad61d
--- /dev/null
+++ b/sound/soc/codecs/wcd-mbhc-v2.h
@@ -0,0 +1,571 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD_MBHC_V2_H__
+#define __WCD_MBHC_V2_H__
+
+#include <linux/wait.h>
+#include <linux/stringify.h>
+#include <linux/power_supply.h>
+#include "wcdcal-hwdep.h"
+
+#define TOMBAK_MBHC_NC 0
+#define TOMBAK_MBHC_NO 1
+#define WCD_MBHC_DEF_BUTTONS 8
+#define WCD_MBHC_KEYCODE_NUM 8
+#define WCD_MBHC_USLEEP_RANGE_MARGIN_US 100
+#define WCD_MBHC_THR_HS_MICB_MV 2700
+/* z value defined in Ohms */
+#define WCD_MONO_HS_MIN_THR 2
+#define WCD_MBHC_STRINGIFY(s) __stringify(s)
+
+enum {
+ WCD_MBHC_ELEC_HS_INS,
+ WCD_MBHC_ELEC_HS_REM,
+};
+
+struct wcd_mbhc;
+enum wcd_mbhc_register_function {
+ WCD_MBHC_L_DET_EN,
+ WCD_MBHC_GND_DET_EN,
+ WCD_MBHC_MECH_DETECTION_TYPE,
+ WCD_MBHC_MIC_CLAMP_CTL,
+ WCD_MBHC_ELECT_DETECTION_TYPE,
+ WCD_MBHC_HS_L_DET_PULL_UP_CTRL,
+ WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL,
+ WCD_MBHC_HPHL_PLUG_TYPE,
+ WCD_MBHC_GND_PLUG_TYPE,
+ WCD_MBHC_SW_HPH_LP_100K_TO_GND,
+ WCD_MBHC_ELECT_SCHMT_ISRC,
+ WCD_MBHC_FSM_EN,
+ WCD_MBHC_INSREM_DBNC,
+ WCD_MBHC_BTN_DBNC,
+ WCD_MBHC_HS_VREF,
+ WCD_MBHC_HS_COMP_RESULT,
+ WCD_MBHC_MIC_SCHMT_RESULT,
+ WCD_MBHC_HPHL_SCHMT_RESULT,
+ WCD_MBHC_HPHR_SCHMT_RESULT,
+ WCD_MBHC_OCP_FSM_EN,
+ WCD_MBHC_BTN_RESULT,
+ WCD_MBHC_BTN_ISRC_CTL,
+ WCD_MBHC_ELECT_RESULT,
+ WCD_MBHC_MICB_CTRL, /* Pull-up and micb control */
+ WCD_MBHC_HPH_CNP_WG_TIME,
+ WCD_MBHC_HPHR_PA_EN,
+ WCD_MBHC_HPHL_PA_EN,
+ WCD_MBHC_HPH_PA_EN,
+ WCD_MBHC_SWCH_LEVEL_REMOVE,
+ WCD_MBHC_PULLDOWN_CTRL,
+ WCD_MBHC_ANC_DET_EN,
+ WCD_MBHC_FSM_STATUS,
+ WCD_MBHC_MUX_CTL,
+ WCD_MBHC_HPHL_OCP_DET_EN,
+ WCD_MBHC_HPHR_OCP_DET_EN,
+ WCD_MBHC_HPHL_OCP_STATUS,
+ WCD_MBHC_HPHR_OCP_STATUS,
+ WCD_MBHC_REG_FUNC_MAX,
+};
+
+enum wcd_mbhc_plug_type {
+ MBHC_PLUG_TYPE_INVALID = -1,
+ MBHC_PLUG_TYPE_NONE,
+ MBHC_PLUG_TYPE_HEADSET,
+ MBHC_PLUG_TYPE_HEADPHONE,
+ MBHC_PLUG_TYPE_HIGH_HPH,
+ MBHC_PLUG_TYPE_GND_MIC_SWAP,
+ MBHC_PLUG_TYPE_ANC_HEADPHONE,
+};
+
+enum pa_dac_ack_flags {
+ WCD_MBHC_HPHL_PA_OFF_ACK = 0,
+ WCD_MBHC_HPHR_PA_OFF_ACK,
+};
+
+enum anc_ack_flags {
+ WCD_MBHC_ANC0_OFF_ACK = 0,
+ WCD_MBHC_ANC1_OFF_ACK,
+};
+
+enum wcd_mbhc_btn_det_mem {
+ WCD_MBHC_BTN_DET_V_BTN_LOW,
+ WCD_MBHC_BTN_DET_V_BTN_HIGH
+};
+
+enum {
+ MIC_BIAS_1 = 1,
+ MIC_BIAS_2,
+ MIC_BIAS_3,
+ MIC_BIAS_4
+};
+
+enum {
+ MICB_PULLUP_ENABLE,
+ MICB_PULLUP_DISABLE,
+ MICB_ENABLE,
+ MICB_DISABLE,
+};
+
+enum {
+ MBHC_COMMON_MICB_PRECHARGE,
+ MBHC_COMMON_MICB_SET_VAL,
+ MBHC_COMMON_MICB_TAIL_CURR,
+};
+
+enum wcd_notify_event {
+ WCD_EVENT_INVALID,
+ /* events for micbias ON and OFF */
+ WCD_EVENT_PRE_MICBIAS_2_OFF,
+ WCD_EVENT_POST_MICBIAS_2_OFF,
+ WCD_EVENT_PRE_MICBIAS_2_ON,
+ WCD_EVENT_POST_MICBIAS_2_ON,
+ WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_OFF,
+ WCD_EVENT_PRE_DAPM_MICBIAS_2_ON,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_ON,
+ /* events for PA ON and OFF */
+ WCD_EVENT_PRE_HPHL_PA_ON,
+ WCD_EVENT_POST_HPHL_PA_OFF,
+ WCD_EVENT_PRE_HPHR_PA_ON,
+ WCD_EVENT_POST_HPHR_PA_OFF,
+ WCD_EVENT_PRE_HPHL_PA_OFF,
+ WCD_EVENT_PRE_HPHR_PA_OFF,
+ WCD_EVENT_OCP_OFF,
+ WCD_EVENT_OCP_ON,
+ WCD_EVENT_LAST,
+};
+
+enum wcd_mbhc_event_state {
+ WCD_MBHC_EVENT_PA_HPHL,
+ WCD_MBHC_EVENT_PA_HPHR,
+};
+struct wcd_mbhc_general_cfg {
+ u8 t_ldoh;
+ u8 t_bg_fast_settle;
+ u8 t_shutdown_plug_rem;
+ u8 mbhc_nsa;
+ u8 mbhc_navg;
+ u8 v_micbias_l;
+ u8 v_micbias;
+ u8 mbhc_reserved;
+ u16 settle_wait;
+ u16 t_micbias_rampup;
+ u16 t_micbias_rampdown;
+ u16 t_supply_bringup;
+} __packed;
+
+struct wcd_mbhc_plug_detect_cfg {
+ u32 mic_current;
+ u32 hph_current;
+ u16 t_mic_pid;
+ u16 t_ins_complete;
+ u16 t_ins_retry;
+ u16 v_removal_delta;
+ u8 micbias_slow_ramp;
+ u8 reserved0;
+ u8 reserved1;
+ u8 reserved2;
+} __packed;
+
+struct wcd_mbhc_plug_type_cfg {
+ u8 av_detect;
+ u8 mono_detect;
+ u8 num_ins_tries;
+ u8 reserved0;
+ s16 v_no_mic;
+ s16 v_av_min;
+ s16 v_av_max;
+ s16 v_hs_min;
+ s16 v_hs_max;
+ u16 reserved1;
+} __packed;
+
+struct wcd_mbhc_btn_detect_cfg {
+ s8 c[8];
+ u8 nc;
+ u8 n_meas;
+ u8 mbhc_nsc;
+ u8 n_btn_meas;
+ u8 n_btn_con;
+ u8 num_btn;
+ u8 reserved0;
+ u8 reserved1;
+ u16 t_poll;
+ u16 t_bounce_wait;
+ u16 t_rel_timeout;
+ s16 v_btn_press_delta_sta;
+ s16 v_btn_press_delta_cic;
+ u16 t_btn0_timeout;
+ s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
+ s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
+ u8 _n_ready[2];
+ u8 _n_cic[2];
+ u8 _gain[2];
+} __packed;
+
+struct wcd_mbhc_imped_detect_cfg {
+ u8 _hs_imped_detect;
+ u8 _n_rload;
+ u8 _hph_keep_on;
+ u8 _repeat_rload_calc;
+ u16 _t_dac_ramp_time;
+ u16 _rhph_high;
+ u16 _rhph_low;
+ u16 _rload[0]; /* rload[n_rload] */
+ u16 _alpha[0]; /* alpha[n_rload] */
+ u16 _beta[3];
+} __packed;
+
+enum wcd_mbhc_hph_type {
+ WCD_MBHC_HPH_NONE = 0,
+ WCD_MBHC_HPH_MONO,
+ WCD_MBHC_HPH_STEREO,
+};
+
+/*
+ * These enum definitions are directly mapped to the register
+ * definitions
+ */
+enum mbhc_moisture_vref {
+ V_OFF,
+ V_45_MV,
+ V_100_MV,
+ V_225_MV,
+};
+
+enum mbhc_hs_pullup_iref {
+ I_DEFAULT = -1,
+ I_OFF = 0,
+ I_1P0_UA,
+ I_2P0_UA,
+ I_3P0_UA,
+};
+
+enum mbhc_moisture_rref {
+ R_OFF,
+ R_24_KOHM,
+ R_84_KOHM,
+ R_184_KOHM,
+};
+
+struct usbc_ana_audio_config {
+ int usbc_en1_gpio;
+ int usbc_en2n_gpio;
+ int usbc_force_gpio;
+ struct device_node *usbc_en1_gpio_p; /* used by pinctrl API */
+ struct device_node *usbc_en2n_gpio_p; /* used by pinctrl API */
+ struct device_node *usbc_force_gpio_p; /* used by pinctrl API */
+};
+
+struct wcd_mbhc_config {
+ bool read_fw_bin;
+ void *calibration;
+ bool detect_extn_cable;
+ bool mono_stero_detection;
+ bool (*swap_gnd_mic)(struct snd_soc_codec *codec);
+ bool hs_ext_micbias;
+ bool gnd_det_en;
+ int key_code[WCD_MBHC_KEYCODE_NUM];
+ uint32_t linein_th;
+ bool moisture_en;
+ int mbhc_micbias;
+ int anc_micbias;
+ bool enable_anc_mic_detect;
+ u32 enable_usbc_analog;
+ struct usbc_ana_audio_config usbc_analog_cfg;
+};
+
+struct wcd_mbhc_intr {
+ int mbhc_sw_intr;
+ int mbhc_btn_press_intr;
+ int mbhc_btn_release_intr;
+ int mbhc_hs_ins_intr;
+ int mbhc_hs_rem_intr;
+ int hph_left_ocp;
+ int hph_right_ocp;
+};
+
+struct wcd_mbhc_register {
+ const char *id;
+ u16 reg;
+ u8 mask;
+ u8 offset;
+ u8 invert;
+};
+
+#define WCD_MBHC_REGISTER(rid, rreg, rmask, rshift, rinvert) \
+{ .id = rid, .reg = rreg, .mask = rmask, .offset = rshift, .invert = rinvert }
+
+#define WCD_MBHC_RSC_LOCK(mbhc) \
+{ \
+ pr_debug("%s: Acquiring BCL\n", __func__); \
+ mutex_lock(&mbhc->codec_resource_lock); \
+ pr_debug("%s: Acquiring BCL done\n", __func__); \
+}
+
+#define WCD_MBHC_RSC_UNLOCK(mbhc) \
+{ \
+ pr_debug("%s: Release BCL\n", __func__); \
+ mutex_unlock(&mbhc->codec_resource_lock); \
+}
+
+#define WCD_MBHC_RSC_ASSERT_LOCKED(mbhc) \
+{ \
+ WARN_ONCE(!mutex_is_locked(&mbhc->codec_resource_lock), \
+ "%s: BCL should have acquired\n", __func__); \
+}
+
+/*
+ * Macros to update and read mbhc register bits. Check for
+ * "0" before updating or reading the register, because it
+ * is possible that one codec wants to write to that bit and
+ * other codec does not.
+ */
+#define WCD_MBHC_REG_UPDATE_BITS(function, val) \
+do { \
+ if (mbhc->wcd_mbhc_regs[function].reg) { \
+ snd_soc_update_bits(mbhc->codec, \
+ mbhc->wcd_mbhc_regs[function].reg, \
+ mbhc->wcd_mbhc_regs[function].mask, \
+ val << (mbhc->wcd_mbhc_regs[function].offset)); \
+ } \
+} while (0)
+
+#define WCD_MBHC_REG_READ(function, val) \
+do { \
+ if (mbhc->wcd_mbhc_regs[function].reg) { \
+ val = (((snd_soc_read(mbhc->codec, \
+ mbhc->wcd_mbhc_regs[function].reg)) & \
+ (mbhc->wcd_mbhc_regs[function].mask)) >> \
+ (mbhc->wcd_mbhc_regs[function].offset)); \
+ } else { \
+ val = -EINVAL; \
+ } \
+} while (0)
+
+struct wcd_mbhc_cb {
+ int (*enable_mb_source)(struct wcd_mbhc *, bool);
+ void (*trim_btn_reg)(struct snd_soc_codec *);
+ void (*compute_impedance)(struct wcd_mbhc *, uint32_t *, uint32_t *);
+ void (*set_micbias_value)(struct snd_soc_codec *);
+ void (*set_auto_zeroing)(struct snd_soc_codec *, bool);
+ struct firmware_cal * (*get_hwdep_fw_cal)(struct wcd_mbhc *,
+ enum wcd_cal_type);
+ void (*set_cap_mode)(struct snd_soc_codec *, bool, bool);
+ int (*register_notifier)(struct wcd_mbhc *,
+ struct notifier_block *nblock,
+ bool enable);
+ int (*request_irq)(struct snd_soc_codec *,
+ int, irq_handler_t, const char *, void *);
+ void (*irq_control)(struct snd_soc_codec *,
+ int irq, bool enable);
+ int (*free_irq)(struct snd_soc_codec *,
+ int irq, void *);
+ void (*clk_setup)(struct snd_soc_codec *, bool);
+ int (*map_btn_code_to_num)(struct snd_soc_codec *);
+ bool (*lock_sleep)(struct wcd_mbhc *, bool);
+ bool (*micbias_enable_status)(struct wcd_mbhc *, int);
+ void (*mbhc_bias)(struct snd_soc_codec *, bool);
+ void (*mbhc_common_micb_ctrl)(struct snd_soc_codec *,
+ int event, bool);
+ void (*micb_internal)(struct snd_soc_codec *,
+ int micb_num, bool);
+ bool (*hph_pa_on_status)(struct snd_soc_codec *);
+ void (*set_btn_thr)(struct snd_soc_codec *, s16 *, s16 *,
+ int num_btn, bool);
+ void (*hph_pull_up_control)(struct snd_soc_codec *,
+ enum mbhc_hs_pullup_iref);
+ int (*mbhc_micbias_control)(struct snd_soc_codec *, int, int req);
+ void (*mbhc_micb_ramp_control)(struct snd_soc_codec *, bool);
+ void (*skip_imped_detect)(struct snd_soc_codec *);
+ bool (*extn_use_mb)(struct snd_soc_codec *);
+ int (*mbhc_micb_ctrl_thr_mic)(struct snd_soc_codec *, int, bool);
+ void (*mbhc_gnd_det_ctrl)(struct snd_soc_codec *, bool);
+ void (*hph_pull_down_ctrl)(struct snd_soc_codec *, bool);
+ void (*mbhc_moisture_config)(struct wcd_mbhc *);
+ bool (*hph_register_recovery)(struct wcd_mbhc *);
+ void (*update_anc_state)(struct snd_soc_codec *codec,
+ bool enable, int anc_num);
+ bool (*is_anc_on)(struct wcd_mbhc *mbhc);
+};
+
+struct wcd_mbhc {
+ /* Delayed work to report long button press */
+ struct delayed_work mbhc_btn_dwork;
+ int buttons_pressed;
+ struct wcd_mbhc_config *mbhc_cfg;
+ const struct wcd_mbhc_cb *mbhc_cb;
+
+ u32 hph_status; /* track headhpone status */
+ u8 hphlocp_cnt; /* headphone left ocp retry */
+ u8 hphrocp_cnt; /* headphone right ocp retry */
+
+ wait_queue_head_t wait_btn_press;
+ bool is_btn_press;
+ u8 current_plug;
+ bool in_swch_irq_handler;
+ bool hphl_swh; /*track HPHL switch NC / NO */
+ bool gnd_swh; /*track GND switch NC / NO */
+ u32 moist_vref;
+ u32 moist_iref;
+ u32 moist_rref;
+ u8 micbias1_cap_mode; /* track ext cap setting */
+ u8 micbias2_cap_mode; /* track ext cap setting */
+ bool hs_detect_work_stop;
+ bool micbias_enable;
+ bool btn_press_intr;
+ bool is_hs_recording;
+ bool is_extn_cable;
+ bool skip_imped_detection;
+ bool is_btn_already_regd;
+
+ struct snd_soc_codec *codec;
+ /* Work to perform MBHC Firmware Read */
+ struct delayed_work mbhc_firmware_dwork;
+ const struct firmware *mbhc_fw;
+ struct firmware_cal *mbhc_cal;
+
+ /* track PA/DAC state to sync with userspace */
+ unsigned long hph_pa_dac_state;
+ unsigned long hph_anc_state;
+ unsigned long event_state;
+ unsigned long jiffies_atreport;
+
+ /* impedance of hphl and hphr */
+ uint32_t zl, zr;
+ bool impedance_detect;
+
+ /* Holds type of Headset - Mono/Stereo */
+ enum wcd_mbhc_hph_type hph_type;
+
+ struct snd_soc_jack headset_jack;
+ struct snd_soc_jack button_jack;
+ struct mutex codec_resource_lock;
+
+ /* Holds codec specific interrupt mapping */
+ const struct wcd_mbhc_intr *intr_ids;
+
+ /* Work to correct accessory type */
+ struct work_struct correct_plug_swch;
+ struct notifier_block nblock;
+
+ struct wcd_mbhc_register *wcd_mbhc_regs;
+
+ struct completion btn_press_compl;
+ struct mutex hphl_pa_lock;
+ struct mutex hphr_pa_lock;
+
+ unsigned long intr_status;
+ bool is_hph_ocp_pending;
+
+ bool usbc_force_pr_mode;
+ int usbc_mode;
+ struct notifier_block psy_nb;
+ struct power_supply *usb_psy;
+ struct work_struct usbc_analog_work;
+};
+#define WCD_MBHC_CAL_SIZE(buttons, rload) ( \
+ sizeof(struct wcd_mbhc_general_cfg) + \
+ sizeof(struct wcd_mbhc_plug_detect_cfg) + \
+ ((sizeof(s16) + sizeof(s16)) * buttons) + \
+ sizeof(struct wcd_mbhc_plug_type_cfg) + \
+ sizeof(struct wcd_mbhc_btn_detect_cfg) + \
+ sizeof(struct wcd_mbhc_imped_detect_cfg) + \
+ ((sizeof(u16) + sizeof(u16)) * rload) \
+ )
+
+
+#define WCD_MBHC_CAL_GENERAL_PTR(cali) ( \
+ (struct wcd_mbhc_general_cfg *) cali)
+#define WCD_MBHC_CAL_PLUG_DET_PTR(cali) ( \
+ (struct wcd_mbhc_plug_detect_cfg *) \
+ &(WCD_MBHC_CAL_GENERAL_PTR(cali)[1]))
+#define WCD_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
+ (struct wcd_mbhc_plug_type_cfg *) \
+ &(WCD_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
+#define WCD_MBHC_CAL_BTN_DET_PTR(cali) ( \
+ (struct wcd_mbhc_btn_detect_cfg *) \
+ &(WCD_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
+#define WCD_MBHC_CAL_IMPED_DET_PTR(cali) ( \
+ (struct wcd_mbhc_imped_detect_cfg *) \
+ (((void *)&WCD_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
+ (WCD_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
+ (sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
+ sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
+ )
+
+#define WCD_MBHC_CAL_MIN_SIZE ( \
+ sizeof(struct wcd_mbhc_general_cfg) + \
+ sizeof(struct wcd_mbhc_plug_detect_cfg) + \
+ sizeof(struct wcd_mbhc_plug_type_cfg) + \
+ sizeof(struct wcd_mbhc_btn_detect_cfg) + \
+ sizeof(struct wcd_mbhc_imped_detect_cfg) + \
+ (sizeof(u16)*2) \
+ )
+
+#define WCD_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
+ sizeof(struct wcd_mbhc_btn_detect_cfg) + \
+ (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
+ sizeof(cfg_ptr->_v_btn_high[0]))))
+
+#define WCD_MBHC_CAL_IMPED_MIN_SZ ( \
+ sizeof(struct wcd_mbhc_imped_detect_cfg) + sizeof(u16) * 2)
+
+#define WCD_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
+ sizeof(struct wcd_mbhc_imped_detect_cfg) + \
+ (cfg_ptr->_n_rload * \
+ (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0]))))
+
+#ifdef CONFIG_SND_SOC_WCD_MBHC
+int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc);
+int wcd_mbhc_start(struct wcd_mbhc *mbhc,
+ struct wcd_mbhc_config *mbhc_cfg);
+void wcd_mbhc_stop(struct wcd_mbhc *mbhc);
+int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec,
+ const struct wcd_mbhc_cb *mbhc_cb,
+ const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
+ struct wcd_mbhc_register *mbhc_reg,
+ bool impedance_det_en);
+int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr);
+void wcd_mbhc_deinit(struct wcd_mbhc *mbhc);
+#else
+static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
+{
+ return;
+}
+static inline int wcd_mbhc_init(struct wcd_mbhc *mbhc,
+ struct snd_soc_codec *codec,
+ const struct wcd_mbhc_cb *mbhc_cb,
+ const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
+ struct wcd_mbhc_register *mbhc_reg,
+ bool impedance_det_en)
+{
+ return 0;
+}
+static inline int wcd_mbhc_start(struct wcd_mbhc *mbhc,
+ struct wcd_mbhc_config *mbhc_cfg)
+{
+ return 0;
+}
+static inline int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc,
+ uint32_t *zl,
+ uint32_t *zr)
+{
+ *zl = 0;
+ *zr = 0;
+ return -EINVAL;
+}
+static inline void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
+{
+}
+#endif
+
+#endif /* __WCD_MBHC_V2_H__ */
diff --git a/sound/soc/codecs/wcd-spi-registers.h b/sound/soc/codecs/wcd-spi-registers.h
new file mode 100644
index 000000000000..4e579696cc49
--- /dev/null
+++ b/sound/soc/codecs/wcd-spi-registers.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD_SPI_REGISTERS_H__
+#define __WCD_SPI_REGISTERS_H__
+
+#include <linux/regmap.h>
+
+#define WCD_SPI_SLAVE_SANITY (0x00)
+#define WCD_SPI_SLAVE_DEVICE_ID (0x04)
+#define WCD_SPI_SLAVE_STATUS (0x08)
+#define WCD_SPI_SLAVE_CONFIG (0x0c)
+#define WCD_SPI_SLAVE_SW_RESET (0x10)
+#define WCD_SPI_SLAVE_IRQ_STATUS (0x14)
+#define WCD_SPI_SLAVE_IRQ_EN (0x18)
+#define WCD_SPI_SLAVE_IRQ_CLR (0x1c)
+#define WCD_SPI_SLAVE_IRQ_FORCE (0x20)
+#define WCD_SPI_SLAVE_TX (0x24)
+#define WCD_SPI_SLAVE_TEST_BUS_DATA (0x2c)
+#define WCD_SPI_SLAVE_TEST_BUS_CTRL (0x30)
+#define WCD_SPI_SLAVE_SW_RST_IRQ (0x34)
+#define WCD_SPI_SLAVE_CHAR_CFG (0x38)
+#define WCD_SPI_SLAVE_CHAR_DATA_MOSI (0x3c)
+#define WCD_SPI_SLAVE_CHAR_DATA_CS_N (0x40)
+#define WCD_SPI_SLAVE_CHAR_DATA_MISO (0x44)
+#define WCD_SPI_SLAVE_TRNS_BYTE_CNT (0x4c)
+#define WCD_SPI_SLAVE_TRNS_LEN (0x50)
+#define WCD_SPI_SLAVE_FIFO_LEVEL (0x54)
+#define WCD_SPI_SLAVE_GENERICS (0x58)
+#define WCD_SPI_SLAVE_EXT_BASE_ADDR (0x5c)
+#define WCD_SPI_MAX_REGISTER (0x5F)
+
+#endif /* End __WCD_SPI_REGISTERS_H__ */
diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c
new file mode 100644
index 000000000000..fde1d3e815d3
--- /dev/null
+++ b/sound/soc/codecs/wcd-spi.c
@@ -0,0 +1,1521 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/component.h>
+#include <linux/ratelimit.h>
+#include <sound/wcd-dsp-mgr.h>
+#include <sound/wcd-spi.h>
+#include "wcd-spi-registers.h"
+
+/* Byte manipulations */
+#define SHIFT_1_BYTES (8)
+#define SHIFT_2_BYTES (16)
+#define SHIFT_3_BYTES (24)
+
+/* Command opcodes */
+#define WCD_SPI_CMD_NOP (0x00)
+#define WCD_SPI_CMD_WREN (0x06)
+#define WCD_SPI_CMD_CLKREQ (0xDA)
+#define WCD_SPI_CMD_RDSR (0x05)
+#define WCD_SPI_CMD_IRR (0x81)
+#define WCD_SPI_CMD_IRW (0x82)
+#define WCD_SPI_CMD_MIOR (0x83)
+#define WCD_SPI_CMD_FREAD (0x0B)
+#define WCD_SPI_CMD_MIOW (0x02)
+#define WCD_SPI_WRITE_FRAME_OPCODE \
+ (WCD_SPI_CMD_MIOW << SHIFT_3_BYTES)
+#define WCD_SPI_READ_FRAME_OPCODE \
+ (WCD_SPI_CMD_MIOR << SHIFT_3_BYTES)
+#define WCD_SPI_FREAD_FRAME_OPCODE \
+ (WCD_SPI_CMD_FREAD << SHIFT_3_BYTES)
+
+/* Command lengths */
+#define WCD_SPI_OPCODE_LEN (0x01)
+#define WCD_SPI_CMD_NOP_LEN (0x01)
+#define WCD_SPI_CMD_WREN_LEN (0x01)
+#define WCD_SPI_CMD_CLKREQ_LEN (0x04)
+#define WCD_SPI_CMD_IRR_LEN (0x04)
+#define WCD_SPI_CMD_IRW_LEN (0x06)
+#define WCD_SPI_WRITE_SINGLE_LEN (0x08)
+#define WCD_SPI_READ_SINGLE_LEN (0x13)
+#define WCD_SPI_CMD_FREAD_LEN (0x13)
+
+/* Command delays */
+#define WCD_SPI_CLKREQ_DELAY_USECS (500)
+#define WCD_SPI_CLK_OFF_TIMER_MS (500)
+#define WCD_SPI_RESUME_TIMEOUT_MS 100
+
+/* Command masks */
+#define WCD_CMD_ADDR_MASK \
+ (0xFF | \
+ (0xFF << SHIFT_1_BYTES) | \
+ (0xFF << SHIFT_2_BYTES))
+
+/* Clock ctrl request related */
+#define WCD_SPI_CLK_ENABLE true
+#define WCD_SPI_CLK_DISABLE false
+#define WCD_SPI_CLK_FLAG_DELAYED (1 << 0)
+#define WCD_SPI_CLK_FLAG_IMMEDIATE (1 << 1)
+
+/* Internal addresses */
+#define WCD_SPI_ADDR_IPC_CTL_HOST (0x012014)
+
+/* Word sizes and min/max lengths */
+#define WCD_SPI_WORD_BYTE_CNT (4)
+#define WCD_SPI_RW_MULTI_MIN_LEN (16)
+
+/* Max size is 32 bytes less than 64Kbytes */
+#define WCD_SPI_RW_MULTI_MAX_LEN ((64 * 1024) - 32)
+
+/*
+ * Max size for the pre-allocated buffers is the max
+ * possible read/write length + 32 bytes for the SPI
+ * read/write command header itself.
+ */
+#define WCD_SPI_RW_MAX_BUF_SIZE (WCD_SPI_RW_MULTI_MAX_LEN + 32)
+
+/* Alignment requirements */
+#define WCD_SPI_RW_MIN_ALIGN WCD_SPI_WORD_BYTE_CNT
+#define WCD_SPI_RW_MULTI_ALIGN (16)
+
+/* Status mask bits */
+#define WCD_SPI_CLK_STATE_ENABLED BIT(0)
+#define WCD_SPI_IS_SUSPENDED BIT(1)
+
+/* Locking related */
+#define WCD_SPI_MUTEX_LOCK(spi, lock) \
+{ \
+ dev_vdbg(&spi->dev, "%s: mutex_lock(%s)\n", \
+ __func__, __stringify_1(lock)); \
+ mutex_lock(&lock); \
+}
+
+#define WCD_SPI_MUTEX_UNLOCK(spi, lock) \
+{ \
+ dev_vdbg(&spi->dev, "%s: mutex_unlock(%s)\n", \
+ __func__, __stringify_1(lock)); \
+ mutex_unlock(&lock); \
+}
+
+struct wcd_spi_debug_data {
+ struct dentry *dir;
+ u32 addr;
+ u32 size;
+};
+
+struct wcd_spi_priv {
+ struct spi_device *spi;
+ u32 mem_base_addr;
+
+ struct regmap *regmap;
+
+ /* Message for single transfer */
+ struct spi_message msg1;
+ struct spi_transfer xfer1;
+
+ /* Message for two transfers */
+ struct spi_message msg2;
+ struct spi_transfer xfer2[2];
+
+ /* Register access related */
+ u32 reg_bytes;
+ u32 val_bytes;
+
+ /* Clock requests related */
+ struct mutex clk_mutex;
+ int clk_users;
+ unsigned long status_mask;
+ struct delayed_work clk_dwork;
+
+ /* Transaction related */
+ struct mutex xfer_mutex;
+
+ struct device *m_dev;
+ struct wdsp_mgr_ops *m_ops;
+
+ /* Debugfs related information */
+ struct wcd_spi_debug_data debug_data;
+
+ /* Completion object to indicate system resume completion */
+ struct completion resume_comp;
+
+ /* Buffers to hold memory used for transfers */
+ void *tx_buf;
+ void *rx_buf;
+};
+
+enum xfer_request {
+ WCD_SPI_XFER_WRITE,
+ WCD_SPI_XFER_READ,
+};
+
+
+static char *wcd_spi_xfer_req_str(enum xfer_request req)
+{
+ if (req == WCD_SPI_XFER_WRITE)
+ return "xfer_write";
+ else if (req == WCD_SPI_XFER_READ)
+ return "xfer_read";
+ else
+ return "xfer_invalid";
+}
+
+static void wcd_spi_reinit_xfer(struct spi_transfer *xfer)
+{
+ xfer->tx_buf = NULL;
+ xfer->rx_buf = NULL;
+ xfer->delay_usecs = 0;
+ xfer->len = 0;
+}
+
+static bool wcd_spi_is_suspended(struct wcd_spi_priv *wcd_spi)
+{
+ return test_bit(WCD_SPI_IS_SUSPENDED, &wcd_spi->status_mask);
+}
+
+static bool wcd_spi_can_suspend(struct wcd_spi_priv *wcd_spi)
+{
+ struct spi_device *spi = wcd_spi->spi;
+
+ if (wcd_spi->clk_users > 0 ||
+ test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) {
+ dev_err(&spi->dev, "%s: cannot suspend, clk_users = %d\n",
+ __func__, wcd_spi->clk_users);
+ return false;
+ }
+
+ return true;
+}
+
+static int wcd_spi_wait_for_resume(struct wcd_spi_priv *wcd_spi)
+{
+ struct spi_device *spi = wcd_spi->spi;
+ int rc = 0;
+
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+ /* If the system is already in resumed state, return right away */
+ if (!wcd_spi_is_suspended(wcd_spi))
+ goto done;
+
+ /* If suspended then wait for resume to happen */
+ reinit_completion(&wcd_spi->resume_comp);
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+ rc = wait_for_completion_timeout(&wcd_spi->resume_comp,
+ msecs_to_jiffies(WCD_SPI_RESUME_TIMEOUT_MS));
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+ if (rc == 0) {
+ dev_err(&spi->dev, "%s: failed to resume in %u msec\n",
+ __func__, WCD_SPI_RESUME_TIMEOUT_MS);
+ rc = -EIO;
+ goto done;
+ }
+
+ dev_dbg(&spi->dev, "%s: resume successful\n", __func__);
+ rc = 0;
+done:
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+ return rc;
+}
+
+static int wcd_spi_read_single(struct spi_device *spi,
+ u32 remote_addr, u32 *val)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0];
+ struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1];
+ u8 *tx_buf = wcd_spi->tx_buf;
+ u32 frame = 0;
+ int ret;
+
+ dev_dbg(&spi->dev, "%s: remote_addr = 0x%x\n",
+ __func__, remote_addr);
+
+ if (!tx_buf) {
+ dev_err(&spi->dev, "%s: tx_buf not allocated\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ frame |= WCD_SPI_READ_FRAME_OPCODE;
+ frame |= remote_addr & WCD_CMD_ADDR_MASK;
+
+ wcd_spi_reinit_xfer(tx_xfer);
+ frame = cpu_to_be32(frame);
+ memcpy(tx_buf, &frame, sizeof(frame));
+ tx_xfer->tx_buf = tx_buf;
+ tx_xfer->len = WCD_SPI_READ_SINGLE_LEN;
+
+ wcd_spi_reinit_xfer(rx_xfer);
+ rx_xfer->rx_buf = val;
+ rx_xfer->len = sizeof(*val);
+
+ ret = spi_sync(spi, &wcd_spi->msg2);
+
+ return ret;
+}
+
+static int wcd_spi_read_multi(struct spi_device *spi,
+ u32 remote_addr, u8 *data,
+ size_t len)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *xfer = &wcd_spi->xfer1;
+ u8 *tx_buf = wcd_spi->tx_buf;
+ u8 *rx_buf = wcd_spi->rx_buf;
+ u32 frame = 0;
+ int ret;
+
+ dev_dbg(&spi->dev, "%s: addr 0x%x, len = %zd\n",
+ __func__, remote_addr, len);
+
+ frame |= WCD_SPI_FREAD_FRAME_OPCODE;
+ frame |= remote_addr & WCD_CMD_ADDR_MASK;
+
+ if (!tx_buf || !rx_buf) {
+ dev_err(&spi->dev, "%s: %s not allocated\n", __func__,
+ (!tx_buf) ? "tx_buf" : "rx_buf");
+ return -ENOMEM;
+ }
+
+ wcd_spi_reinit_xfer(xfer);
+ frame = cpu_to_be32(frame);
+ memcpy(tx_buf, &frame, sizeof(frame));
+ xfer->tx_buf = tx_buf;
+ xfer->rx_buf = rx_buf;
+ xfer->len = WCD_SPI_CMD_FREAD_LEN + len;
+
+ ret = spi_sync(spi, &wcd_spi->msg1);
+ if (ret) {
+ dev_err(&spi->dev, "%s: failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ memcpy(data, rx_buf + WCD_SPI_CMD_FREAD_LEN, len);
+done:
+ return ret;
+}
+
+static int wcd_spi_write_single(struct spi_device *spi,
+ u32 remote_addr, u32 val)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *xfer = &wcd_spi->xfer1;
+ u8 buf[WCD_SPI_WRITE_SINGLE_LEN];
+ u32 frame = 0;
+
+ dev_dbg(&spi->dev, "%s: remote_addr = 0x%x, val = 0x%x\n",
+ __func__, remote_addr, val);
+
+ memset(buf, 0, WCD_SPI_WRITE_SINGLE_LEN);
+ frame |= WCD_SPI_WRITE_FRAME_OPCODE;
+ frame |= (remote_addr & WCD_CMD_ADDR_MASK);
+
+ frame = cpu_to_be32(frame);
+ memcpy(buf, &frame, sizeof(frame));
+ memcpy(buf + sizeof(frame), &val, sizeof(val));
+
+ wcd_spi_reinit_xfer(xfer);
+ xfer->tx_buf = buf;
+ xfer->len = WCD_SPI_WRITE_SINGLE_LEN;
+
+ return spi_sync(spi, &wcd_spi->msg1);
+}
+
+static int wcd_spi_write_multi(struct spi_device *spi,
+ u32 remote_addr, u8 *data,
+ size_t len)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *xfer = &wcd_spi->xfer1;
+ u32 frame = 0;
+ u8 *tx_buf = wcd_spi->tx_buf;
+ int xfer_len, ret;
+
+ dev_dbg(&spi->dev, "%s: addr = 0x%x len = %zd\n",
+ __func__, remote_addr, len);
+
+ frame |= WCD_SPI_WRITE_FRAME_OPCODE;
+ frame |= (remote_addr & WCD_CMD_ADDR_MASK);
+
+ frame = cpu_to_be32(frame);
+ xfer_len = len + sizeof(frame);
+
+ if (!tx_buf) {
+ dev_err(&spi->dev, "%s: tx_buf not allocated\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(tx_buf, &frame, sizeof(frame));
+ memcpy(tx_buf + sizeof(frame), data, len);
+
+ wcd_spi_reinit_xfer(xfer);
+ xfer->tx_buf = tx_buf;
+ xfer->len = xfer_len;
+
+ ret = spi_sync(spi, &wcd_spi->msg1);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev,
+ "%s: Failed, addr = 0x%x, len = %zd\n",
+ __func__, remote_addr, len);
+ return ret;
+}
+
+static int wcd_spi_transfer_split(struct spi_device *spi,
+ struct wcd_spi_msg *data_msg,
+ enum xfer_request xfer_req)
+{
+ u32 addr = data_msg->remote_addr;
+ u8 *data = data_msg->data;
+ int remain_size = data_msg->len;
+ int to_xfer, loop_cnt, ret = 0;
+
+ /* Perform single writes until multi word alignment is met */
+ loop_cnt = 1;
+ while (remain_size &&
+ !IS_ALIGNED(addr, WCD_SPI_RW_MULTI_ALIGN)) {
+ if (xfer_req == WCD_SPI_XFER_WRITE)
+ ret = wcd_spi_write_single(spi, addr,
+ (*(u32 *)data));
+ else
+ ret = wcd_spi_read_single(spi, addr,
+ (u32 *)data);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev,
+ "%s: %s fail iter(%d) start-word addr (0x%x)\n",
+ __func__, wcd_spi_xfer_req_str(xfer_req),
+ loop_cnt, addr);
+ goto done;
+ }
+
+ addr += WCD_SPI_WORD_BYTE_CNT;
+ data += WCD_SPI_WORD_BYTE_CNT;
+ remain_size -= WCD_SPI_WORD_BYTE_CNT;
+ loop_cnt++;
+ }
+
+ /* Perform multi writes for max allowed multi writes */
+ loop_cnt = 1;
+ while (remain_size >= WCD_SPI_RW_MULTI_MAX_LEN) {
+ if (xfer_req == WCD_SPI_XFER_WRITE)
+ ret = wcd_spi_write_multi(spi, addr, data,
+ WCD_SPI_RW_MULTI_MAX_LEN);
+ else
+ ret = wcd_spi_read_multi(spi, addr, data,
+ WCD_SPI_RW_MULTI_MAX_LEN);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev,
+ "%s: %s fail iter(%d) max-write addr (0x%x)\n",
+ __func__, wcd_spi_xfer_req_str(xfer_req),
+ loop_cnt, addr);
+ goto done;
+ }
+
+ addr += WCD_SPI_RW_MULTI_MAX_LEN;
+ data += WCD_SPI_RW_MULTI_MAX_LEN;
+ remain_size -= WCD_SPI_RW_MULTI_MAX_LEN;
+ loop_cnt++;
+ }
+
+ /*
+ * Perform write for max possible data that is multiple
+ * of the minimum size for multi-write commands.
+ */
+ to_xfer = remain_size - (remain_size % WCD_SPI_RW_MULTI_MIN_LEN);
+ if (remain_size >= WCD_SPI_RW_MULTI_MIN_LEN &&
+ to_xfer > 0) {
+ if (xfer_req == WCD_SPI_XFER_WRITE)
+ ret = wcd_spi_write_multi(spi, addr, data, to_xfer);
+ else
+ ret = wcd_spi_read_multi(spi, addr, data, to_xfer);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev,
+ "%s: %s fail write addr (0x%x), size (0x%x)\n",
+ __func__, wcd_spi_xfer_req_str(xfer_req),
+ addr, to_xfer);
+ goto done;
+ }
+
+ addr += to_xfer;
+ data += to_xfer;
+ remain_size -= to_xfer;
+ }
+
+ /* Perform single writes for the last remaining data */
+ loop_cnt = 1;
+ while (remain_size > 0) {
+ if (xfer_req == WCD_SPI_XFER_WRITE)
+ ret = wcd_spi_write_single(spi, addr, (*((u32 *)data)));
+ else
+ ret = wcd_spi_read_single(spi, addr, (u32 *) data);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev,
+ "%s: %s fail iter(%d) end-write addr (0x%x)\n",
+ __func__, wcd_spi_xfer_req_str(xfer_req),
+ loop_cnt, addr);
+ goto done;
+ }
+
+ addr += WCD_SPI_WORD_BYTE_CNT;
+ data += WCD_SPI_WORD_BYTE_CNT;
+ remain_size -= WCD_SPI_WORD_BYTE_CNT;
+ loop_cnt++;
+ }
+
+done:
+ return ret;
+}
+
+static int wcd_spi_cmd_nop(struct spi_device *spi)
+{
+ u8 nop = WCD_SPI_CMD_NOP;
+
+ return spi_write(spi, &nop, WCD_SPI_CMD_NOP_LEN);
+}
+
+static int wcd_spi_cmd_clkreq(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *xfer = &wcd_spi->xfer1;
+ u8 cmd[WCD_SPI_CMD_CLKREQ_LEN] = {
+ WCD_SPI_CMD_CLKREQ,
+ 0xBA, 0x80, 0x00};
+
+ wcd_spi_reinit_xfer(xfer);
+ xfer->tx_buf = cmd;
+ xfer->len = WCD_SPI_CMD_CLKREQ_LEN;
+ xfer->delay_usecs = WCD_SPI_CLKREQ_DELAY_USECS;
+
+ return spi_sync(spi, &wcd_spi->msg1);
+}
+
+static int wcd_spi_cmd_wr_en(struct spi_device *spi)
+{
+ u8 wr_en = WCD_SPI_CMD_WREN;
+
+ return spi_write(spi, &wr_en, WCD_SPI_CMD_WREN_LEN);
+}
+
+static int wcd_spi_cmd_rdsr(struct spi_device *spi,
+ u32 *rdsr_status)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0];
+ struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1];
+ u8 rdsr_cmd;
+ u32 status;
+ int ret;
+
+ rdsr_cmd = WCD_SPI_CMD_RDSR;
+ wcd_spi_reinit_xfer(tx_xfer);
+ tx_xfer->tx_buf = &rdsr_cmd;
+ tx_xfer->len = sizeof(rdsr_cmd);
+
+
+ wcd_spi_reinit_xfer(rx_xfer);
+ rx_xfer->rx_buf = &status;
+ rx_xfer->len = sizeof(status);
+
+ ret = spi_sync(spi, &wcd_spi->msg2);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: RDSR failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ *rdsr_status = be32_to_cpu(status);
+
+ dev_dbg(&spi->dev, "%s: RDSR success, value = 0x%x\n",
+ __func__, *rdsr_status);
+done:
+ return ret;
+}
+
+static int wcd_spi_clk_enable(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret;
+ u32 rd_status = 0;
+
+ ret = wcd_spi_cmd_nop(spi);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: NOP1 failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = wcd_spi_cmd_clkreq(spi);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: CLK_REQ failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = wcd_spi_cmd_nop(spi);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: NOP2 failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+ wcd_spi_cmd_rdsr(spi, &rd_status);
+ /*
+ * Read status zero means reads are not
+ * happenning on the bus, possibly because
+ * clock request failed.
+ */
+ if (rd_status) {
+ set_bit(WCD_SPI_CLK_STATE_ENABLED,
+ &wcd_spi->status_mask);
+ } else {
+ dev_err(&spi->dev, "%s: RDSR status is zero\n",
+ __func__);
+ ret = -EIO;
+ }
+done:
+ return ret;
+}
+
+static int wcd_spi_clk_disable(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret;
+
+ ret = wcd_spi_write_single(spi, WCD_SPI_ADDR_IPC_CTL_HOST, 0x01);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev, "%s: Failed, err = %d\n",
+ __func__, ret);
+ else
+ clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask);
+
+ return ret;
+}
+
+static int wcd_spi_clk_ctrl(struct spi_device *spi,
+ bool request, u32 flags)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret = 0;
+ const char *delay_str;
+
+ delay_str = (flags == WCD_SPI_CLK_FLAG_DELAYED) ?
+ "delayed" : "immediate";
+
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+
+ /* Reject any unbalanced disable request */
+ if (wcd_spi->clk_users < 0 ||
+ (!request && wcd_spi->clk_users == 0)) {
+ dev_err(&spi->dev, "%s: Unbalanced clk_users %d for %s\n",
+ __func__, wcd_spi->clk_users,
+ request ? "enable" : "disable");
+ ret = -EINVAL;
+
+ /* Reset the clk_users to 0 */
+ wcd_spi->clk_users = 0;
+
+ goto done;
+ }
+
+ if (request == WCD_SPI_CLK_ENABLE) {
+ /*
+ * If the SPI bus is suspended, then return error
+ * as the transaction cannot be completed.
+ */
+ if (wcd_spi_is_suspended(wcd_spi)) {
+ dev_err(&spi->dev,
+ "%s: SPI suspended, cannot enable clk\n",
+ __func__);
+ ret = -EIO;
+ goto done;
+ }
+
+ /* Cancel the disable clk work */
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+ cancel_delayed_work_sync(&wcd_spi->clk_dwork);
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+
+ wcd_spi->clk_users++;
+
+ /*
+ * If clk state is already set,
+ * then clk wasnt really disabled
+ */
+ if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask))
+ goto done;
+ else if (wcd_spi->clk_users == 1)
+ ret = wcd_spi_clk_enable(spi);
+
+ } else {
+ wcd_spi->clk_users--;
+
+ /* Clock is still voted for */
+ if (wcd_spi->clk_users > 0)
+ goto done;
+
+ /*
+ * If we are here, clk_users must be 0 and needs
+ * to be disabled. Call the disable based on the
+ * flags.
+ */
+ if (flags == WCD_SPI_CLK_FLAG_DELAYED) {
+ schedule_delayed_work(&wcd_spi->clk_dwork,
+ msecs_to_jiffies(WCD_SPI_CLK_OFF_TIMER_MS));
+ } else {
+ ret = wcd_spi_clk_disable(spi);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev,
+ "%s: Failed to disable clk err = %d\n",
+ __func__, ret);
+ }
+ }
+
+done:
+ dev_dbg(&spi->dev, "%s: updated clk_users = %d, request_%s %s\n",
+ __func__, wcd_spi->clk_users, request ? "enable" : "disable",
+ request ? "" : delay_str);
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+
+ return ret;
+}
+
+static int wcd_spi_init(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret;
+
+ ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
+ WCD_SPI_CLK_FLAG_IMMEDIATE);
+ if (IS_ERR_VALUE(ret))
+ goto done;
+
+ ret = wcd_spi_cmd_wr_en(spi);
+ if (IS_ERR_VALUE(ret))
+ goto err_wr_en;
+
+ /*
+ * In case spi_init is called after component deinit,
+ * it is possible hardware register state is also reset.
+ * Sync the regcache here so hardware state is updated
+ * to reflect the cache.
+ */
+ regcache_sync(wcd_spi->regmap);
+
+ regmap_write(wcd_spi->regmap, WCD_SPI_SLAVE_CONFIG,
+ 0x0F3D0800);
+
+ /* Write the MTU to max allowed size */
+ regmap_update_bits(wcd_spi->regmap,
+ WCD_SPI_SLAVE_TRNS_LEN,
+ 0xFFFF0000, 0xFFFF0000);
+err_wr_en:
+ wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
+ WCD_SPI_CLK_FLAG_IMMEDIATE);
+done:
+ return ret;
+}
+
+static void wcd_spi_clk_work(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd_spi_priv *wcd_spi;
+ struct spi_device *spi;
+ int ret;
+
+ dwork = to_delayed_work(work);
+ wcd_spi = container_of(dwork, struct wcd_spi_priv, clk_dwork);
+ spi = wcd_spi->spi;
+
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+ ret = wcd_spi_clk_disable(spi);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev,
+ "%s: Failed to disable clk, err = %d\n",
+ __func__, ret);
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+}
+
+static int __wcd_spi_data_xfer(struct spi_device *spi,
+ struct wcd_spi_msg *msg,
+ enum xfer_request xfer_req)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret;
+
+ /* Check for minimum alignment requirements */
+ if (!IS_ALIGNED(msg->remote_addr, WCD_SPI_RW_MIN_ALIGN)) {
+ dev_err(&spi->dev,
+ "%s addr 0x%x is not aligned to 0x%x\n",
+ __func__, msg->remote_addr, WCD_SPI_RW_MIN_ALIGN);
+ return -EINVAL;
+ } else if (msg->len % WCD_SPI_WORD_BYTE_CNT) {
+ dev_err(&spi->dev,
+ "%s len 0x%zx is not multiple of %d\n",
+ __func__, msg->len, WCD_SPI_WORD_BYTE_CNT);
+ return -EINVAL;
+ }
+
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->xfer_mutex);
+ if (msg->len == WCD_SPI_WORD_BYTE_CNT) {
+ if (xfer_req == WCD_SPI_XFER_WRITE)
+ ret = wcd_spi_write_single(spi, msg->remote_addr,
+ (*((u32 *)msg->data)));
+ else
+ ret = wcd_spi_read_single(spi, msg->remote_addr,
+ (u32 *) msg->data);
+ } else {
+ ret = wcd_spi_transfer_split(spi, msg, xfer_req);
+ }
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->xfer_mutex);
+
+ return ret;
+}
+
+static int wcd_spi_data_xfer(struct spi_device *spi,
+ struct wcd_spi_msg *msg,
+ enum xfer_request req)
+{
+ int ret, ret1;
+
+ if (msg->len <= 0) {
+ dev_err(&spi->dev, "%s: Invalid size %zd\n",
+ __func__, msg->len);
+ return -EINVAL;
+ }
+
+ /* Request for clock */
+ ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
+ WCD_SPI_CLK_FLAG_IMMEDIATE);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: clk enable failed %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ /* Perform the transaction */
+ ret = __wcd_spi_data_xfer(spi, msg, req);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev,
+ "%s: Failed %s, addr = 0x%x, size = 0x%zx, err = %d\n",
+ __func__, wcd_spi_xfer_req_str(req),
+ msg->remote_addr, msg->len, ret);
+
+ /* Release the clock even if xfer failed */
+ ret1 = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
+ WCD_SPI_CLK_FLAG_DELAYED);
+ if (IS_ERR_VALUE(ret1))
+ dev_err(&spi->dev, "%s: clk disable failed %d\n",
+ __func__, ret1);
+done:
+ return ret;
+}
+
+/*
+ * wcd_spi_data_write: Write data to WCD SPI
+ * @spi: spi_device struct
+ * @msg: msg that needs to be written to WCD
+ *
+ * This API writes length of data to address specified. These details
+ * about the write are encapsulated in @msg. Write size should be multiple
+ * of 4 bytes and write address should be 4-byte aligned.
+ */
+int wcd_spi_data_write(struct spi_device *spi,
+ struct wcd_spi_msg *msg)
+{
+ if (!spi || !msg) {
+ pr_err("%s: Invalid %s\n", __func__,
+ (!spi) ? "spi device" : "msg");
+ return -EINVAL;
+ }
+
+ dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x, len = %zu\n",
+ __func__, msg->remote_addr, msg->len);
+ return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_WRITE);
+}
+EXPORT_SYMBOL(wcd_spi_data_write);
+
+/*
+ * wcd_spi_data_read: Read data from WCD SPI
+ * @spi: spi_device struct
+ * @msg: msg that needs to be read from WCD
+ *
+ * This API reads length of data from address specified. These details
+ * about the read are encapsulated in @msg. Read size should be multiple
+ * of 4 bytes and read address should be 4-byte aligned.
+ */
+int wcd_spi_data_read(struct spi_device *spi,
+ struct wcd_spi_msg *msg)
+{
+ if (!spi || !msg) {
+ pr_err("%s: Invalid %s\n", __func__,
+ (!spi) ? "spi device" : "msg");
+ return -EINVAL;
+ }
+
+ dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x,len = %zu\n",
+ __func__, msg->remote_addr, msg->len);
+ return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_READ);
+}
+EXPORT_SYMBOL(wcd_spi_data_read);
+
+static int wdsp_spi_dload_section(struct spi_device *spi,
+ void *data)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct wdsp_img_section *sec = data;
+ struct wcd_spi_msg msg;
+ int ret;
+
+ dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n",
+ __func__, sec->addr, sec->size);
+
+ msg.remote_addr = sec->addr + wcd_spi->mem_base_addr;
+ msg.data = sec->data;
+ msg.len = sec->size;
+
+ ret = __wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_WRITE);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n",
+ __func__, msg.remote_addr, msg.len);
+ return ret;
+}
+
+static int wdsp_spi_read_section(struct spi_device *spi, void *data)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct wdsp_img_section *sec = data;
+ struct wcd_spi_msg msg;
+ int ret;
+
+ msg.remote_addr = sec->addr + wcd_spi->mem_base_addr;
+ msg.data = sec->data;
+ msg.len = sec->size;
+
+ dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n",
+ __func__, msg.remote_addr, msg.len);
+
+ ret = wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_READ);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n",
+ __func__, msg.remote_addr, msg.len);
+ return ret;
+}
+
+static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
+ enum wdsp_event_type event,
+ void *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret = 0;
+
+ dev_dbg(&spi->dev, "%s: event type %d\n",
+ __func__, event);
+
+ switch (event) {
+ case WDSP_EVENT_POST_SHUTDOWN:
+ cancel_delayed_work_sync(&wcd_spi->clk_dwork);
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+ if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask))
+ wcd_spi_clk_disable(spi);
+ wcd_spi->clk_users = 0;
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+ break;
+
+ case WDSP_EVENT_PRE_DLOAD_CODE:
+ case WDSP_EVENT_PRE_DLOAD_DATA:
+ ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
+ WCD_SPI_CLK_FLAG_IMMEDIATE);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev, "%s: clk_req failed %d\n",
+ __func__, ret);
+ break;
+
+ case WDSP_EVENT_POST_DLOAD_CODE:
+ case WDSP_EVENT_POST_DLOAD_DATA:
+ case WDSP_EVENT_DLOAD_FAILED:
+
+ ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
+ WCD_SPI_CLK_FLAG_IMMEDIATE);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev, "%s: clk unvote failed %d\n",
+ __func__, ret);
+ break;
+
+ case WDSP_EVENT_DLOAD_SECTION:
+ ret = wdsp_spi_dload_section(spi, data);
+ break;
+
+ case WDSP_EVENT_READ_SECTION:
+ ret = wdsp_spi_read_section(spi, data);
+ break;
+
+ case WDSP_EVENT_SUSPEND:
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+ if (!wcd_spi_can_suspend(wcd_spi))
+ ret = -EBUSY;
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+ break;
+
+ case WDSP_EVENT_RESUME:
+ ret = wcd_spi_wait_for_resume(wcd_spi);
+ break;
+
+ default:
+ dev_dbg(&spi->dev, "%s: Unhandled event %d\n",
+ __func__, event);
+ break;
+ }
+
+ return ret;
+}
+
+static int wcd_spi_bus_gwrite(void *context, const void *reg,
+ size_t reg_len, const void *val,
+ size_t val_len)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ u8 tx_buf[WCD_SPI_CMD_IRW_LEN];
+
+ if (!reg || !val || reg_len != wcd_spi->reg_bytes ||
+ val_len != wcd_spi->val_bytes) {
+ dev_err(&spi->dev,
+ "%s: Invalid input, reg_len = %zd, val_len = %zd",
+ __func__, reg_len, val_len);
+ return -EINVAL;
+ }
+
+ tx_buf[0] = WCD_SPI_CMD_IRW;
+ tx_buf[1] = *((u8 *)reg);
+ memcpy(&tx_buf[WCD_SPI_OPCODE_LEN + reg_len],
+ val, val_len);
+
+ return spi_write(spi, tx_buf, WCD_SPI_CMD_IRW_LEN);
+}
+
+static int wcd_spi_bus_write(void *context, const void *data,
+ size_t count)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+ if (count < (wcd_spi->reg_bytes + wcd_spi->val_bytes)) {
+ dev_err(&spi->dev, "%s: Invalid size %zd\n",
+ __func__, count);
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ return wcd_spi_bus_gwrite(context, data, wcd_spi->reg_bytes,
+ data + wcd_spi->reg_bytes,
+ count - wcd_spi->reg_bytes);
+}
+
+static int wcd_spi_bus_read(void *context, const void *reg,
+ size_t reg_len, void *val,
+ size_t val_len)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0];
+ struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1];
+ u8 tx_buf[WCD_SPI_CMD_IRR_LEN];
+
+ if (!reg || !val || reg_len != wcd_spi->reg_bytes ||
+ val_len != wcd_spi->val_bytes) {
+ dev_err(&spi->dev,
+ "%s: Invalid input, reg_len = %zd, val_len = %zd",
+ __func__, reg_len, val_len);
+ return -EINVAL;
+ }
+
+ memset(tx_buf, 0, WCD_SPI_OPCODE_LEN);
+ tx_buf[0] = WCD_SPI_CMD_IRR;
+ tx_buf[1] = *((u8 *)reg);
+
+ wcd_spi_reinit_xfer(tx_xfer);
+ tx_xfer->tx_buf = tx_buf;
+ tx_xfer->rx_buf = NULL;
+ tx_xfer->len = WCD_SPI_CMD_IRR_LEN;
+
+ wcd_spi_reinit_xfer(rx_xfer);
+ rx_xfer->tx_buf = NULL;
+ rx_xfer->rx_buf = val;
+ rx_xfer->len = val_len;
+
+ return spi_sync(spi, &wcd_spi->msg2);
+}
+
+static struct regmap_bus wcd_spi_regmap_bus = {
+ .write = wcd_spi_bus_write,
+ .gather_write = wcd_spi_bus_gwrite,
+ .read = wcd_spi_bus_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static int wcd_spi_state_show(struct seq_file *f, void *ptr)
+{
+ struct spi_device *spi = f->private;
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ const char *clk_state, *clk_mutex, *xfer_mutex;
+
+ if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask))
+ clk_state = "enabled";
+ else
+ clk_state = "disabled";
+
+ clk_mutex = mutex_is_locked(&wcd_spi->clk_mutex) ?
+ "locked" : "unlocked";
+
+ xfer_mutex = mutex_is_locked(&wcd_spi->xfer_mutex) ?
+ "locked" : "unlocked";
+
+ seq_printf(f, "clk_state = %s\nclk_users = %d\n"
+ "clk_mutex = %s\nxfer_mutex = %s\n",
+ clk_state, wcd_spi->clk_users, clk_mutex,
+ xfer_mutex);
+ return 0;
+}
+
+static int wcd_spi_state_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wcd_spi_state_show, inode->i_private);
+}
+
+static const struct file_operations state_fops = {
+ .open = wcd_spi_state_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static ssize_t wcd_spi_debugfs_mem_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct spi_device *spi = file->private_data;
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data;
+ struct wcd_spi_msg msg;
+ ssize_t buf_size, read_count = 0;
+ char *buf;
+ int ret;
+
+ if (*ppos < 0 || !count)
+ return -EINVAL;
+
+ if (dbg_data->size == 0 || dbg_data->addr == 0) {
+ dev_err(&spi->dev,
+ "%s: Invalid request, size = %u, addr = 0x%x\n",
+ __func__, dbg_data->size, dbg_data->addr);
+ return 0;
+ }
+
+ buf_size = count < dbg_data->size ? count : dbg_data->size;
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ msg.data = buf;
+ msg.remote_addr = dbg_data->addr;
+ msg.len = buf_size;
+ msg.flags = 0;
+
+ ret = wcd_spi_data_read(spi, &msg);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev,
+ "%s: Failed to read %zu bytes from addr 0x%x\n",
+ __func__, buf_size, msg.remote_addr);
+ goto done;
+ }
+
+ read_count = simple_read_from_buffer(ubuf, count, ppos, buf, buf_size);
+
+done:
+ kfree(buf);
+ if (ret < 0)
+ return ret;
+ else
+ return read_count;
+}
+
+static const struct file_operations mem_read_fops = {
+ .open = simple_open,
+ .read = wcd_spi_debugfs_mem_read,
+};
+
+static int wcd_spi_debugfs_init(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data;
+ int rc = 0;
+
+ dbg_data->dir = debugfs_create_dir("wcd_spi", NULL);
+ if (IS_ERR_OR_NULL(dbg_data->dir)) {
+ dbg_data->dir = NULL;
+ rc = -ENODEV;
+ goto done;
+ }
+
+ debugfs_create_file("state", 0444, dbg_data->dir, spi, &state_fops);
+ debugfs_create_u32("addr", S_IRUGO | S_IWUSR, dbg_data->dir,
+ &dbg_data->addr);
+ debugfs_create_u32("size", S_IRUGO | S_IWUSR, dbg_data->dir,
+ &dbg_data->size);
+
+ debugfs_create_file("mem_read", S_IRUGO, dbg_data->dir,
+ spi, &mem_read_fops);
+done:
+ return rc;
+}
+
+
+static const struct reg_default wcd_spi_defaults[] = {
+ {WCD_SPI_SLAVE_SANITY, 0xDEADBEEF},
+ {WCD_SPI_SLAVE_DEVICE_ID, 0x00500000},
+ {WCD_SPI_SLAVE_STATUS, 0x80100000},
+ {WCD_SPI_SLAVE_CONFIG, 0x0F200808},
+ {WCD_SPI_SLAVE_SW_RESET, 0x00000000},
+ {WCD_SPI_SLAVE_IRQ_STATUS, 0x00000000},
+ {WCD_SPI_SLAVE_IRQ_EN, 0x00000000},
+ {WCD_SPI_SLAVE_IRQ_CLR, 0x00000000},
+ {WCD_SPI_SLAVE_IRQ_FORCE, 0x00000000},
+ {WCD_SPI_SLAVE_TX, 0x00000000},
+ {WCD_SPI_SLAVE_TEST_BUS_DATA, 0x00000000},
+ {WCD_SPI_SLAVE_TEST_BUS_CTRL, 0x00000000},
+ {WCD_SPI_SLAVE_SW_RST_IRQ, 0x00000000},
+ {WCD_SPI_SLAVE_CHAR_CFG, 0x00000000},
+ {WCD_SPI_SLAVE_CHAR_DATA_MOSI, 0x00000000},
+ {WCD_SPI_SLAVE_CHAR_DATA_CS_N, 0x00000000},
+ {WCD_SPI_SLAVE_CHAR_DATA_MISO, 0x00000000},
+ {WCD_SPI_SLAVE_TRNS_BYTE_CNT, 0x00000000},
+ {WCD_SPI_SLAVE_TRNS_LEN, 0x00000000},
+ {WCD_SPI_SLAVE_FIFO_LEVEL, 0x00000000},
+ {WCD_SPI_SLAVE_GENERICS, 0x80000000},
+ {WCD_SPI_SLAVE_EXT_BASE_ADDR, 0x00000000},
+};
+
+static bool wcd_spi_is_volatile_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case WCD_SPI_SLAVE_SANITY:
+ case WCD_SPI_SLAVE_STATUS:
+ case WCD_SPI_SLAVE_IRQ_STATUS:
+ case WCD_SPI_SLAVE_TX:
+ case WCD_SPI_SLAVE_SW_RST_IRQ:
+ case WCD_SPI_SLAVE_TRNS_BYTE_CNT:
+ case WCD_SPI_SLAVE_FIFO_LEVEL:
+ case WCD_SPI_SLAVE_GENERICS:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wcd_spi_is_readable_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case WCD_SPI_SLAVE_SW_RESET:
+ case WCD_SPI_SLAVE_IRQ_CLR:
+ case WCD_SPI_SLAVE_IRQ_FORCE:
+ return false;
+ }
+
+ return true;
+}
+
+static struct regmap_config wcd_spi_regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wcd_spi_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wcd_spi_defaults),
+ .max_register = WCD_SPI_MAX_REGISTER,
+ .volatile_reg = wcd_spi_is_volatile_reg,
+ .readable_reg = wcd_spi_is_readable_reg,
+};
+
+static int wdsp_spi_init(struct device *dev, void *priv_data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int ret;
+
+ ret = wcd_spi_init(spi);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev, "%s: Init failed, err = %d\n",
+ __func__, ret);
+ return ret;
+}
+
+static int wdsp_spi_deinit(struct device *dev, void *priv_data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+ /*
+ * Deinit means the hardware is reset. Mark the cache
+ * as dirty here, so init will sync the cache
+ */
+ regcache_mark_dirty(wcd_spi->regmap);
+
+ return 0;
+}
+
+static struct wdsp_cmpnt_ops wdsp_spi_ops = {
+ .init = wdsp_spi_init,
+ .deinit = wdsp_spi_deinit,
+ .event_handler = wdsp_spi_event_handler,
+};
+
+static int wcd_spi_component_bind(struct device *dev,
+ struct device *master,
+ void *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret = 0;
+
+ wcd_spi->m_dev = master;
+ wcd_spi->m_ops = data;
+
+ if (wcd_spi->m_ops &&
+ wcd_spi->m_ops->register_cmpnt_ops)
+ ret = wcd_spi->m_ops->register_cmpnt_ops(master, dev,
+ wcd_spi,
+ &wdsp_spi_ops);
+ if (ret) {
+ dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ wcd_spi->reg_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.reg_bits, 8);
+ wcd_spi->val_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.val_bits, 8);
+
+ wcd_spi->regmap = devm_regmap_init(&spi->dev, &wcd_spi_regmap_bus,
+ &spi->dev, &wcd_spi_regmap_cfg);
+ if (IS_ERR(wcd_spi->regmap)) {
+ ret = PTR_ERR(wcd_spi->regmap);
+ dev_err(&spi->dev, "%s: Failed to allocate regmap, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ if (wcd_spi_debugfs_init(spi))
+ dev_err(&spi->dev, "%s: Failed debugfs init\n", __func__);
+
+ spi_message_init(&wcd_spi->msg1);
+ spi_message_add_tail(&wcd_spi->xfer1, &wcd_spi->msg1);
+
+ spi_message_init(&wcd_spi->msg2);
+ spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2);
+ spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2);
+
+ /* Pre-allocate the buffers */
+ wcd_spi->tx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE,
+ GFP_KERNEL | GFP_DMA);
+ if (!wcd_spi->tx_buf) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ wcd_spi->rx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE,
+ GFP_KERNEL | GFP_DMA);
+ if (!wcd_spi->rx_buf) {
+ kfree(wcd_spi->tx_buf);
+ wcd_spi->tx_buf = NULL;
+ ret = -ENOMEM;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static void wcd_spi_component_unbind(struct device *dev,
+ struct device *master,
+ void *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+ wcd_spi->m_dev = NULL;
+ wcd_spi->m_ops = NULL;
+
+ spi_transfer_del(&wcd_spi->xfer1);
+ spi_transfer_del(&wcd_spi->xfer2[0]);
+ spi_transfer_del(&wcd_spi->xfer2[1]);
+
+ kfree(wcd_spi->tx_buf);
+ kfree(wcd_spi->rx_buf);
+ wcd_spi->tx_buf = NULL;
+ wcd_spi->rx_buf = NULL;
+}
+
+static const struct component_ops wcd_spi_component_ops = {
+ .bind = wcd_spi_component_bind,
+ .unbind = wcd_spi_component_unbind,
+};
+
+static int wcd_spi_probe(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi;
+ int ret = 0;
+
+ wcd_spi = devm_kzalloc(&spi->dev, sizeof(*wcd_spi),
+ GFP_KERNEL);
+ if (!wcd_spi)
+ return -ENOMEM;
+
+ ret = of_property_read_u32(spi->dev.of_node,
+ "qcom,mem-base-addr",
+ &wcd_spi->mem_base_addr);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: Missing %s DT entry",
+ __func__, "qcom,mem-base-addr");
+ goto err_ret;
+ }
+
+ dev_dbg(&spi->dev,
+ "%s: mem_base_addr 0x%x\n", __func__, wcd_spi->mem_base_addr);
+
+ mutex_init(&wcd_spi->clk_mutex);
+ mutex_init(&wcd_spi->xfer_mutex);
+ INIT_DELAYED_WORK(&wcd_spi->clk_dwork, wcd_spi_clk_work);
+ init_completion(&wcd_spi->resume_comp);
+
+ wcd_spi->spi = spi;
+ spi_set_drvdata(spi, wcd_spi);
+
+ ret = component_add(&spi->dev, &wcd_spi_component_ops);
+ if (ret) {
+ dev_err(&spi->dev, "%s: component_add failed err = %d\n",
+ __func__, ret);
+ goto err_component_add;
+ }
+
+ return ret;
+
+err_component_add:
+ mutex_destroy(&wcd_spi->clk_mutex);
+ mutex_destroy(&wcd_spi->xfer_mutex);
+err_ret:
+ devm_kfree(&spi->dev, wcd_spi);
+ spi_set_drvdata(spi, NULL);
+ return ret;
+}
+
+static int wcd_spi_remove(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+ component_del(&spi->dev, &wcd_spi_component_ops);
+
+ mutex_destroy(&wcd_spi->clk_mutex);
+ mutex_destroy(&wcd_spi->xfer_mutex);
+
+ devm_kfree(&spi->dev, wcd_spi);
+ spi_set_drvdata(spi, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wcd_spi_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int rc = 0;
+
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+ if (!wcd_spi_can_suspend(wcd_spi)) {
+ rc = -EBUSY;
+ goto done;
+ }
+
+ /*
+ * If we are here, it is okay to let the suspend go
+ * through for this driver. But, still need to notify
+ * the master to make sure all other components can suspend
+ * as well.
+ */
+ if (wcd_spi->m_dev && wcd_spi->m_ops &&
+ wcd_spi->m_ops->suspend) {
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+ rc = wcd_spi->m_ops->suspend(wcd_spi->m_dev);
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+ }
+
+ if (rc == 0)
+ set_bit(WCD_SPI_IS_SUSPENDED, &wcd_spi->status_mask);
+ else
+ dev_dbg(&spi->dev, "%s: cannot suspend, err = %d\n",
+ __func__, rc);
+done:
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+ return rc;
+}
+
+static int wcd_spi_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+ clear_bit(WCD_SPI_IS_SUSPENDED, &wcd_spi->status_mask);
+ complete(&wcd_spi->resume_comp);
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops wcd_spi_pm_ops = {
+ .suspend = wcd_spi_suspend,
+ .resume = wcd_spi_resume,
+};
+#endif
+
+static const struct of_device_id wcd_spi_of_match[] = {
+ { .compatible = "qcom,wcd-spi-v2", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wcd_spi_of_match);
+
+static struct spi_driver wcd_spi_driver = {
+ .driver = {
+ .name = "wcd-spi-v2",
+ .of_match_table = wcd_spi_of_match,
+#ifdef CONFIG_PM
+ .pm = &wcd_spi_pm_ops,
+#endif
+ },
+ .probe = wcd_spi_probe,
+ .remove = wcd_spi_remove,
+};
+
+module_spi_driver(wcd_spi_driver);
+
+MODULE_DESCRIPTION("WCD SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9330-tables.c b/sound/soc/codecs/wcd9330-tables.c
new file mode 100644
index 000000000000..1e0821d99bc3
--- /dev/null
+++ b/sound/soc/codecs/wcd9330-tables.c
@@ -0,0 +1,1675 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/mfd/wcd9xxx/wcd9330_registers.h>
+#include "wcd9330.h"
+
+const u8 tomtom_reg_readable[WCD9330_MAX_REGISTER + 1] = {
+ [TOMTOM_A_CHIP_CTL] = 1,
+ [TOMTOM_A_CHIP_STATUS] = 1,
+ [TOMTOM_A_CHIP_ID_BYTE_0] = 1,
+ [TOMTOM_A_CHIP_ID_BYTE_1] = 1,
+ [TOMTOM_A_CHIP_ID_BYTE_2] = 1,
+ [TOMTOM_A_CHIP_ID_BYTE_3] = 1,
+ [TOMTOM_A_CHIP_I2C_SLAVE_ID] = 1,
+ [TOMTOM_A_SLAVE_ID_1] = 1,
+ [TOMTOM_A_SLAVE_ID_2] = 1,
+ [TOMTOM_A_SLAVE_ID_3] = 1,
+ [TOMTOM_A_PIN_CTL_OE0] = 1,
+ [TOMTOM_A_PIN_CTL_OE1] = 1,
+ [TOMTOM_A_PIN_CTL_OE2] = 1,
+ [TOMTOM_A_PIN_CTL_DATA0] = 1,
+ [TOMTOM_A_PIN_CTL_DATA1] = 1,
+ [TOMTOM_A_PIN_CTL_DATA2] = 1,
+ [TOMTOM_A_HDRIVE_GENERIC] = 1,
+ [TOMTOM_A_HDRIVE_OVERRIDE] = 1,
+ [TOMTOM_A_ANA_CSR_WAIT_STATE] = 1,
+ [TOMTOM_A_PROCESS_MONITOR_CTL0] = 1,
+ [TOMTOM_A_PROCESS_MONITOR_CTL1] = 1,
+ [TOMTOM_A_PROCESS_MONITOR_CTL2] = 1,
+ [TOMTOM_A_PROCESS_MONITOR_CTL3] = 1,
+ [TOMTOM_A_QFUSE_CTL] = 1,
+ [TOMTOM_A_QFUSE_STATUS] = 1,
+ [TOMTOM_A_QFUSE_DATA_OUT0] = 1,
+ [TOMTOM_A_QFUSE_DATA_OUT1] = 1,
+ [TOMTOM_A_QFUSE_DATA_OUT2] = 1,
+ [TOMTOM_A_QFUSE_DATA_OUT3] = 1,
+ [TOMTOM_A_QFUSE_DATA_OUT4] = 1,
+ [TOMTOM_A_QFUSE_DATA_OUT5] = 1,
+ [TOMTOM_A_QFUSE_DATA_OUT6] = 1,
+ [TOMTOM_A_QFUSE_DATA_OUT7] = 1,
+ [TOMTOM_A_CDC_CTL] = 1,
+ [TOMTOM_A_LEAKAGE_CTL] = 1,
+ [TOMTOM_A_SVASS_MEM_PTR0] = 1,
+ [TOMTOM_A_SVASS_MEM_PTR1] = 1,
+ [TOMTOM_A_SVASS_MEM_PTR2] = 1,
+ [TOMTOM_A_SVASS_MEM_CTL] = 1,
+ [TOMTOM_A_SVASS_MEM_BANK] = 1,
+ [TOMTOM_A_DMIC_B1_CTL] = 1,
+ [TOMTOM_A_DMIC_B2_CTL] = 1,
+ [TOMTOM_A_SVASS_CLKRST_CTL] = 1,
+ [TOMTOM_A_SVASS_CPAR_CFG] = 1,
+ [TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD] = 1,
+ [TOMTOM_A_SVASS_CPAR_WDOG_CFG] = 1,
+ [TOMTOM_A_SVASS_CFG] = 1,
+ [TOMTOM_A_SVASS_SPE_CFG] = 1,
+ [TOMTOM_A_SVASS_STATUS] = 1,
+ [TOMTOM_A_SVASS_INT_MASK] = 1,
+ [TOMTOM_A_SVASS_INT_STATUS] = 1,
+ [TOMTOM_A_SVASS_INT_CLR] = 0,
+ [TOMTOM_A_SVASS_DEBUG] = 1,
+ [TOMTOM_A_SVASS_SPE_BKUP_INT] = 0,
+ [TOMTOM_A_SVASS_MEM_ACC] = 1,
+ [TOMTOM_A_MEM_LEAKAGE_CTL] = 1,
+ [TOMTOM_A_SVASS_SPE_INBOX_TRG] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_0] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_1] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_2] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_3] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_4] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_5] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_6] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_7] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_8] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_9] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_10] = 0,
+ [TOMTOM_A_SVASS_SPE_INBOX_11] = 0,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_0] = 1,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_1] = 1,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_2] = 1,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_3] = 1,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_4] = 1,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_5] = 1,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_6] = 1,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_7] = 1,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_8] = 1,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_9] = 1,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_10] = 1,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_11] = 1,
+ [TOMTOM_A_INTR_MODE] = 1,
+ [TOMTOM_A_INTR1_MASK0] = 1,
+ [TOMTOM_A_INTR1_MASK1] = 1,
+ [TOMTOM_A_INTR1_MASK2] = 1,
+ [TOMTOM_A_INTR1_MASK3] = 1,
+ [TOMTOM_A_INTR1_STATUS0] = 1,
+ [TOMTOM_A_INTR1_STATUS1] = 1,
+ [TOMTOM_A_INTR1_STATUS2] = 1,
+ [TOMTOM_A_INTR1_STATUS3] = 1,
+ [TOMTOM_A_INTR1_CLEAR0] = 0,
+ [TOMTOM_A_INTR1_CLEAR1] = 0,
+ [TOMTOM_A_INTR1_CLEAR2] = 0,
+ [TOMTOM_A_INTR1_CLEAR3] = 0,
+ [TOMTOM_A_INTR1_LEVEL0] = 1,
+ [TOMTOM_A_INTR1_LEVEL1] = 1,
+ [TOMTOM_A_INTR1_LEVEL2] = 1,
+ [TOMTOM_A_INTR1_LEVEL3] = 1,
+ [TOMTOM_A_INTR1_TEST0] = 1,
+ [TOMTOM_A_INTR1_TEST1] = 1,
+ [TOMTOM_A_INTR1_TEST2] = 1,
+ [TOMTOM_A_INTR1_TEST3] = 1,
+ [TOMTOM_A_INTR1_SET0] = 1,
+ [TOMTOM_A_INTR1_SET1] = 1,
+ [TOMTOM_A_INTR1_SET2] = 1,
+ [TOMTOM_A_INTR1_SET3] = 1,
+ [TOMTOM_A_INTR2_MASK0] = 1,
+ [TOMTOM_A_INTR2_STATUS0] = 1,
+ [TOMTOM_A_INTR2_CLEAR0] = 0,
+ [TOMTOM_A_INTR2_LEVEL0] = 1,
+ [TOMTOM_A_INTR2_TEST0] = 1,
+ [TOMTOM_A_INTR2_SET0] = 1,
+ [TOMTOM_A_CDC_TX_I2S_SCK_MODE] = 1,
+ [TOMTOM_A_CDC_TX_I2S_WS_MODE] = 1,
+ [TOMTOM_A_CDC_DMIC_DATA0_MODE] = 1,
+ [TOMTOM_A_CDC_DMIC_CLK0_MODE] = 1,
+ [TOMTOM_A_CDC_DMIC_DATA1_MODE] = 1,
+ [TOMTOM_A_CDC_DMIC_CLK1_MODE] = 1,
+ [TOMTOM_A_CDC_RX_I2S_SCK_MODE] = 1,
+ [TOMTOM_A_CDC_RX_I2S_WS_MODE] = 1,
+ [TOMTOM_A_CDC_DMIC_DATA2_MODE] = 1,
+ [TOMTOM_A_CDC_DMIC_CLK2_MODE] = 1,
+ [TOMTOM_A_CDC_INTR1_MODE] = 1,
+ [TOMTOM_A_CDC_SB_NRZ_SEL_MODE] = 1,
+ [TOMTOM_A_CDC_INTR2_MODE] = 1,
+ [TOMTOM_A_CDC_RF_PA_ON_MODE] = 1,
+ [TOMTOM_A_CDC_BOOST_MODE] = 1,
+ [TOMTOM_A_CDC_JTCK_MODE] = 1,
+ [TOMTOM_A_CDC_JTDI_MODE] = 1,
+ [TOMTOM_A_CDC_JTMS_MODE] = 1,
+ [TOMTOM_A_CDC_JTDO_MODE] = 1,
+ [TOMTOM_A_CDC_JTRST_MODE] = 1,
+ [TOMTOM_A_CDC_BIST_MODE_MODE] = 1,
+ [TOMTOM_A_CDC_MAD_MAIN_CTL_1] = 1,
+ [TOMTOM_A_CDC_MAD_MAIN_CTL_2] = 1,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_1] = 1,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_2] = 1,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_3] = 1,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_4] = 1,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_5] = 1,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_6] = 1,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_7] = 1,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_8] = 1,
+ [TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR] = 1,
+ [TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL] = 1,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_1] = 1,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_2] = 1,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_3] = 1,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_4] = 1,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_5] = 1,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_6] = 1,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_7] = 1,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_1] = 1,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_2] = 1,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_3] = 1,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_4] = 1,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_5] = 1,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_6] = 1,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_7] = 1,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_8] = 1,
+ [TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR] = 1,
+ [TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL] = 1,
+ [TOMTOM_A_CDC_MAD_INP_SEL] = 1,
+ [TOMTOM_A_BIAS_REF_CTL] = 1,
+ [TOMTOM_A_BIAS_CENTRAL_BG_CTL] = 1,
+ [TOMTOM_A_BIAS_PRECHRG_CTL] = 1,
+ [TOMTOM_A_BIAS_CURR_CTL_1] = 1,
+ [TOMTOM_A_BIAS_CURR_CTL_2] = 1,
+ [TOMTOM_A_BIAS_OSC_BG_CTL] = 1,
+ [TOMTOM_A_CLK_BUFF_EN1] = 1,
+ [TOMTOM_A_CLK_BUFF_EN2] = 1,
+ [TOMTOM_A_LDO_L_MODE_1] = 1,
+ [TOMTOM_A_LDO_L_MODE_2] = 1,
+ [TOMTOM_A_LDO_L_CTRL_1] = 1,
+ [TOMTOM_A_LDO_L_CTRL_2] = 1,
+ [TOMTOM_A_LDO_L_CTRL_3] = 1,
+ [TOMTOM_A_LDO_L_CTRL_4] = 1,
+ [TOMTOM_A_LDO_H_MODE_1] = 1,
+ [TOMTOM_A_LDO_H_MODE_2] = 1,
+ [TOMTOM_A_LDO_H_LOOP_CTL] = 1,
+ [TOMTOM_A_LDO_H_COMP_1] = 1,
+ [TOMTOM_A_LDO_H_COMP_2] = 1,
+ [TOMTOM_A_LDO_H_BIAS_1] = 1,
+ [TOMTOM_A_LDO_H_BIAS_2] = 1,
+ [TOMTOM_A_LDO_H_BIAS_3] = 1,
+ [TOMTOM_A_VBAT_CLK] = 1,
+ [TOMTOM_A_VBAT_LOOP] = 1,
+ [TOMTOM_A_VBAT_REF] = 1,
+ [TOMTOM_A_VBAT_ADC_TEST] = 1,
+ [TOMTOM_A_VBAT_FE] = 1,
+ [TOMTOM_A_VBAT_BIAS_1] = 1,
+ [TOMTOM_A_VBAT_BIAS_2] = 1,
+ [TOMTOM_A_VBAT_ADC_DATA_MSB] = 1,
+ [TOMTOM_A_VBAT_ADC_DATA_LSB] = 1,
+ [TOMTOM_A_FLL_NREF] = 1,
+ [TOMTOM_A_FLL_KDCO_TUNE] = 1,
+ [TOMTOM_A_FLL_LOCK_THRESH] = 1,
+ [TOMTOM_A_FLL_LOCK_DET_COUNT] = 1,
+ [TOMTOM_A_FLL_DAC_THRESHOLD] = 1,
+ [TOMTOM_A_FLL_TEST_DCO_FREERUN] = 1,
+ [TOMTOM_A_FLL_TEST_ENABLE] = 1,
+ [TOMTOM_A_MICB_CFILT_1_CTL] = 1,
+ [TOMTOM_A_MICB_CFILT_1_VAL] = 1,
+ [TOMTOM_A_MICB_CFILT_1_PRECHRG] = 1,
+ [TOMTOM_A_MICB_1_CTL] = 1,
+ [TOMTOM_A_MICB_1_INT_RBIAS] = 1,
+ [TOMTOM_A_MICB_1_MBHC] = 1,
+ [TOMTOM_A_MICB_CFILT_2_CTL] = 1,
+ [TOMTOM_A_MICB_CFILT_2_VAL] = 1,
+ [TOMTOM_A_MICB_CFILT_2_PRECHRG] = 1,
+ [TOMTOM_A_MICB_2_CTL] = 1,
+ [TOMTOM_A_MICB_2_INT_RBIAS] = 1,
+ [TOMTOM_A_MICB_2_MBHC] = 1,
+ [TOMTOM_A_MICB_CFILT_3_CTL] = 1,
+ [TOMTOM_A_MICB_CFILT_3_VAL] = 1,
+ [TOMTOM_A_MICB_CFILT_3_PRECHRG] = 1,
+ [TOMTOM_A_MICB_3_CTL] = 1,
+ [TOMTOM_A_MICB_3_INT_RBIAS] = 1,
+ [TOMTOM_A_MICB_3_MBHC] = 1,
+ [TOMTOM_A_MICB_4_CTL] = 1,
+ [TOMTOM_A_MICB_4_INT_RBIAS] = 1,
+ [TOMTOM_A_MICB_4_MBHC] = 1,
+ [TOMTOM_A_SPKR_DRV2_EN] = 1,
+ [TOMTOM_A_SPKR_DRV2_GAIN] = 1,
+ [TOMTOM_A_SPKR_DRV2_DAC_CTL] = 1,
+ [TOMTOM_A_SPKR_DRV2_OCP_CTL] = 1,
+ [TOMTOM_A_SPKR_DRV2_CLIP_DET] = 1,
+ [TOMTOM_A_SPKR_DRV2_DBG_DAC] = 1,
+ [TOMTOM_A_SPKR_DRV2_DBG_PA] = 1,
+ [TOMTOM_A_SPKR_DRV2_DBG_PWRSTG] = 1,
+ [TOMTOM_A_SPKR_DRV2_BIAS_LDO] = 1,
+ [TOMTOM_A_SPKR_DRV2_BIAS_INT] = 1,
+ [TOMTOM_A_SPKR_DRV2_BIAS_PA] = 1,
+ [TOMTOM_A_SPKR_DRV2_STATUS_OCP] = 1,
+ [TOMTOM_A_SPKR_DRV2_STATUS_PA] = 1,
+ [TOMTOM_A_MBHC_INSERT_DETECT] = 1,
+ [TOMTOM_A_MBHC_INSERT_DET_STATUS] = 1,
+ [TOMTOM_A_TX_COM_BIAS] = 1,
+ [TOMTOM_A_MBHC_INSERT_DETECT2] = 1,
+ [TOMTOM_A_MBHC_SCALING_MUX_1] = 1,
+ [TOMTOM_A_MBHC_SCALING_MUX_2] = 1,
+ [TOMTOM_A_MAD_ANA_CTRL] = 1,
+ [TOMTOM_A_TX_SUP_SWITCH_CTRL_1] = 1,
+ [TOMTOM_A_TX_SUP_SWITCH_CTRL_2] = 1,
+ [TOMTOM_A_TX_1_GAIN] = 1,
+ [TOMTOM_A_TX_1_2_TEST_EN] = 1,
+ [TOMTOM_A_TX_2_GAIN] = 1,
+ [TOMTOM_A_TX_1_2_ADC_IB] = 1,
+ [TOMTOM_A_TX_1_2_ATEST_REFCTRL] = 1,
+ [TOMTOM_A_TX_1_2_TEST_CTL] = 1,
+ [TOMTOM_A_TX_1_2_TEST_BLOCK_EN] = 1,
+ [TOMTOM_A_TX_1_2_TXFE_CLKDIV] = 1,
+ [TOMTOM_A_TX_1_2_SAR_ERR_CH1] = 1,
+ [TOMTOM_A_TX_1_2_SAR_ERR_CH2] = 1,
+ [TOMTOM_A_TX_3_GAIN] = 1,
+ [TOMTOM_A_TX_3_4_TEST_EN] = 1,
+ [TOMTOM_A_TX_4_GAIN] = 1,
+ [TOMTOM_A_TX_3_4_ADC_IB] = 1,
+ [TOMTOM_A_TX_3_4_ATEST_REFCTRL] = 1,
+ [TOMTOM_A_TX_3_4_TEST_CTL] = 1,
+ [TOMTOM_A_TX_3_4_TEST_BLOCK_EN] = 1,
+ [TOMTOM_A_TX_3_4_TXFE_CKDIV] = 1,
+ [TOMTOM_A_TX_3_4_SAR_ERR_CH3] = 1,
+ [TOMTOM_A_TX_3_4_SAR_ERR_CH4] = 1,
+ [TOMTOM_A_TX_5_GAIN] = 1,
+ [TOMTOM_A_TX_5_6_TEST_EN] = 1,
+ [TOMTOM_A_TX_6_GAIN] = 1,
+ [TOMTOM_A_TX_5_6_ADC_IB] = 1,
+ [TOMTOM_A_TX_5_6_ATEST_REFCTRL] = 1,
+ [TOMTOM_A_TX_5_6_TEST_CTL] = 1,
+ [TOMTOM_A_TX_5_6_TEST_BLOCK_EN] = 1,
+ [TOMTOM_A_TX_5_6_TXFE_CKDIV] = 1,
+ [TOMTOM_A_TX_5_6_SAR_ERR_CH5] = 1,
+ [TOMTOM_A_TX_5_6_SAR_ERR_CH6] = 1,
+ [TOMTOM_A_TX_7_MBHC_EN] = 1,
+ [TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL] = 1,
+ [TOMTOM_A_TX_7_MBHC_ADC] = 1,
+ [TOMTOM_A_TX_7_MBHC_TEST_CTL] = 1,
+ [TOMTOM_A_TX_7_MBHC_SAR_ERR] = 1,
+ [TOMTOM_A_TX_7_TXFE_CLKDIV] = 1,
+ [TOMTOM_A_RCO_CTRL] = 1,
+ [TOMTOM_A_RCO_CALIBRATION_CTRL1] = 1,
+ [TOMTOM_A_RCO_CALIBRATION_CTRL2] = 1,
+ [TOMTOM_A_RCO_CALIBRATION_CTRL3] = 1,
+ [TOMTOM_A_RCO_TEST_CTRL] = 1,
+ [TOMTOM_A_RCO_CALIBRATION_RESULT1] = 1,
+ [TOMTOM_A_RCO_CALIBRATION_RESULT2] = 1,
+ [TOMTOM_A_BUCK_MODE_1] = 1,
+ [TOMTOM_A_BUCK_MODE_2] = 1,
+ [TOMTOM_A_BUCK_MODE_3] = 1,
+ [TOMTOM_A_BUCK_MODE_4] = 1,
+ [TOMTOM_A_BUCK_MODE_5] = 1,
+ [TOMTOM_A_BUCK_CTRL_VCL_1] = 1,
+ [TOMTOM_A_BUCK_CTRL_VCL_2] = 1,
+ [TOMTOM_A_BUCK_CTRL_VCL_3] = 1,
+ [TOMTOM_A_BUCK_CTRL_CCL_1] = 1,
+ [TOMTOM_A_BUCK_CTRL_CCL_2] = 1,
+ [TOMTOM_A_BUCK_CTRL_CCL_3] = 1,
+ [TOMTOM_A_BUCK_CTRL_CCL_4] = 1,
+ [TOMTOM_A_BUCK_CTRL_PWM_DRVR_1] = 1,
+ [TOMTOM_A_BUCK_CTRL_PWM_DRVR_2] = 1,
+ [TOMTOM_A_BUCK_CTRL_PWM_DRVR_3] = 1,
+ [TOMTOM_A_BUCK_TMUX_A_D] = 1,
+ [TOMTOM_A_NCP_BUCKREF] = 1,
+ [TOMTOM_A_NCP_EN] = 1,
+ [TOMTOM_A_NCP_CLK] = 1,
+ [TOMTOM_A_NCP_STATIC] = 1,
+ [TOMTOM_A_NCP_VTH_LOW] = 1,
+ [TOMTOM_A_NCP_VTH_HIGH] = 1,
+ [TOMTOM_A_NCP_ATEST] = 1,
+ [TOMTOM_A_NCP_DTEST] = 1,
+ [TOMTOM_A_NCP_DLY1] = 1,
+ [TOMTOM_A_NCP_DLY2] = 1,
+ [TOMTOM_A_RX_AUX_SW_CTL] = 1,
+ [TOMTOM_A_RX_PA_AUX_IN_CONN] = 1,
+ [TOMTOM_A_RX_COM_TIMER_DIV] = 1,
+ [TOMTOM_A_RX_COM_OCP_CTL] = 1,
+ [TOMTOM_A_RX_COM_OCP_COUNT] = 1,
+ [TOMTOM_A_RX_COM_DAC_CTL] = 1,
+ [TOMTOM_A_RX_COM_BIAS] = 1,
+ [TOMTOM_A_RX_HPH_AUTO_CHOP] = 1,
+ [TOMTOM_A_RX_HPH_CHOP_CTL] = 1,
+ [TOMTOM_A_RX_HPH_BIAS_PA] = 1,
+ [TOMTOM_A_RX_HPH_BIAS_LDO] = 1,
+ [TOMTOM_A_RX_HPH_BIAS_CNP] = 1,
+ [TOMTOM_A_RX_HPH_BIAS_WG_OCP] = 1,
+ [TOMTOM_A_RX_HPH_OCP_CTL] = 1,
+ [TOMTOM_A_RX_HPH_CNP_EN] = 1,
+ [TOMTOM_A_RX_HPH_CNP_WG_CTL] = 1,
+ [TOMTOM_A_RX_HPH_CNP_WG_TIME] = 1,
+ [TOMTOM_A_RX_HPH_L_GAIN] = 1,
+ [TOMTOM_A_RX_HPH_L_TEST] = 1,
+ [TOMTOM_A_RX_HPH_L_PA_CTL] = 1,
+ [TOMTOM_A_RX_HPH_L_DAC_CTL] = 1,
+ [TOMTOM_A_RX_HPH_L_ATEST] = 1,
+ [TOMTOM_A_RX_HPH_L_STATUS] = 1,
+ [TOMTOM_A_RX_HPH_R_GAIN] = 1,
+ [TOMTOM_A_RX_HPH_R_TEST] = 1,
+ [TOMTOM_A_RX_HPH_R_PA_CTL] = 1,
+ [TOMTOM_A_RX_HPH_R_DAC_CTL] = 1,
+ [TOMTOM_A_RX_HPH_R_ATEST] = 1,
+ [TOMTOM_A_RX_HPH_R_STATUS] = 1,
+ [TOMTOM_A_RX_EAR_BIAS_PA] = 1,
+ [TOMTOM_A_RX_EAR_BIAS_CMBUFF] = 1,
+ [TOMTOM_A_RX_EAR_EN] = 1,
+ [TOMTOM_A_RX_EAR_GAIN] = 1,
+ [TOMTOM_A_RX_EAR_CMBUFF] = 1,
+ [TOMTOM_A_RX_EAR_ICTL] = 1,
+ [TOMTOM_A_RX_EAR_CCOMP] = 1,
+ [TOMTOM_A_RX_EAR_VCM] = 1,
+ [TOMTOM_A_RX_EAR_CNP] = 1,
+ [TOMTOM_A_RX_EAR_DAC_CTL_ATEST] = 1,
+ [TOMTOM_A_RX_EAR_STATUS] = 1,
+ [TOMTOM_A_RX_LINE_BIAS_PA] = 1,
+ [TOMTOM_A_RX_BUCK_BIAS1] = 1,
+ [TOMTOM_A_RX_BUCK_BIAS2] = 1,
+ [TOMTOM_A_RX_LINE_COM] = 1,
+ [TOMTOM_A_RX_LINE_CNP_EN] = 1,
+ [TOMTOM_A_RX_LINE_CNP_WG_CTL] = 1,
+ [TOMTOM_A_RX_LINE_CNP_WG_TIME] = 1,
+ [TOMTOM_A_RX_LINE_1_GAIN] = 1,
+ [TOMTOM_A_RX_LINE_1_TEST] = 1,
+ [TOMTOM_A_RX_LINE_1_DAC_CTL] = 1,
+ [TOMTOM_A_RX_LINE_1_STATUS] = 1,
+ [TOMTOM_A_RX_LINE_2_GAIN] = 1,
+ [TOMTOM_A_RX_LINE_2_TEST] = 1,
+ [TOMTOM_A_RX_LINE_2_DAC_CTL] = 1,
+ [TOMTOM_A_RX_LINE_2_STATUS] = 1,
+ [TOMTOM_A_RX_LINE_3_GAIN] = 1,
+ [TOMTOM_A_RX_LINE_3_TEST] = 1,
+ [TOMTOM_A_RX_LINE_3_DAC_CTL] = 1,
+ [TOMTOM_A_RX_LINE_3_STATUS] = 1,
+ [TOMTOM_A_RX_LINE_4_GAIN] = 1,
+ [TOMTOM_A_RX_LINE_4_TEST] = 1,
+ [TOMTOM_A_RX_LINE_4_DAC_CTL] = 1,
+ [TOMTOM_A_RX_LINE_4_STATUS] = 1,
+ [TOMTOM_A_RX_LINE_CNP_DBG] = 1,
+ [TOMTOM_A_SPKR_DRV1_EN] = 1,
+ [TOMTOM_A_SPKR_DRV1_GAIN] = 1,
+ [TOMTOM_A_SPKR_DRV1_DAC_CTL] = 1,
+ [TOMTOM_A_SPKR_DRV1_OCP_CTL] = 1,
+ [TOMTOM_A_SPKR_DRV1_CLIP_DET] = 1,
+ [TOMTOM_A_SPKR_DRV1_IEC] = 1,
+ [TOMTOM_A_SPKR_DRV1_DBG_DAC] = 1,
+ [TOMTOM_A_SPKR_DRV1_DBG_PA] = 1,
+ [TOMTOM_A_SPKR_DRV1_DBG_PWRSTG] = 1,
+ [TOMTOM_A_SPKR_DRV1_BIAS_LDO] = 1,
+ [TOMTOM_A_SPKR_DRV1_BIAS_INT] = 1,
+ [TOMTOM_A_SPKR_DRV1_BIAS_PA] = 1,
+ [TOMTOM_A_SPKR_DRV1_STATUS_OCP] = 1,
+ [TOMTOM_A_SPKR_DRV1_STATUS_PA] = 1,
+ [TOMTOM_A_SPKR1_PROT_EN] = 1,
+ [TOMTOM_A_SPKR1_PROT_ADC_TEST_EN] = 1,
+ [TOMTOM_A_SPKR1_PROT_ATEST] = 1,
+ [TOMTOM_A_SPKR1_PROT_LDO_CTRL] = 1,
+ [TOMTOM_A_SPKR1_PROT_ISENSE_CTRL] = 1,
+ [TOMTOM_A_SPKR1_PROT_VSENSE_CTRL] = 1,
+ [TOMTOM_A_SPKR2_PROT_EN] = 1,
+ [TOMTOM_A_SPKR2_PROT_ADC_TEST_EN] = 1,
+ [TOMTOM_A_SPKR2_PROT_ATEST] = 1,
+ [TOMTOM_A_SPKR2_PROT_LDO_CTRL] = 1,
+ [TOMTOM_A_SPKR2_PROT_ISENSE_CTRL] = 1,
+ [TOMTOM_A_SPKR2_PROT_VSENSE_CTRL] = 1,
+ [TOMTOM_A_MBHC_HPH] = 1,
+ [TOMTOM_A_CDC_ANC1_B1_CTL] = 1,
+ [TOMTOM_A_CDC_ANC2_B1_CTL] = 1,
+ [TOMTOM_A_CDC_ANC1_SHIFT] = 1,
+ [TOMTOM_A_CDC_ANC2_SHIFT] = 1,
+ [TOMTOM_A_CDC_ANC1_IIR_B1_CTL] = 1,
+ [TOMTOM_A_CDC_ANC2_IIR_B1_CTL] = 1,
+ [TOMTOM_A_CDC_ANC1_IIR_B2_CTL] = 1,
+ [TOMTOM_A_CDC_ANC2_IIR_B2_CTL] = 1,
+ [TOMTOM_A_CDC_ANC1_IIR_B3_CTL] = 1,
+ [TOMTOM_A_CDC_ANC2_IIR_B3_CTL] = 1,
+ [TOMTOM_A_CDC_ANC1_LPF_B1_CTL] = 1,
+ [TOMTOM_A_CDC_ANC2_LPF_B1_CTL] = 1,
+ [TOMTOM_A_CDC_ANC1_LPF_B2_CTL] = 1,
+ [TOMTOM_A_CDC_ANC2_LPF_B2_CTL] = 1,
+ [TOMTOM_A_CDC_ANC1_SPARE] = 1,
+ [TOMTOM_A_CDC_ANC2_SPARE] = 1,
+ [TOMTOM_A_CDC_ANC1_SMLPF_CTL] = 1,
+ [TOMTOM_A_CDC_ANC2_SMLPF_CTL] = 1,
+ [TOMTOM_A_CDC_ANC1_DCFLT_CTL] = 1,
+ [TOMTOM_A_CDC_ANC2_DCFLT_CTL] = 1,
+ [TOMTOM_A_CDC_ANC1_GAIN_CTL] = 1,
+ [TOMTOM_A_CDC_ANC2_GAIN_CTL] = 1,
+ [TOMTOM_A_CDC_ANC1_B2_CTL] = 1,
+ [TOMTOM_A_CDC_ANC2_B2_CTL] = 1,
+ [TOMTOM_A_CDC_TX1_VOL_CTL_TIMER] = 1,
+ [TOMTOM_A_CDC_TX2_VOL_CTL_TIMER] = 1,
+ [TOMTOM_A_CDC_TX3_VOL_CTL_TIMER] = 1,
+ [TOMTOM_A_CDC_TX4_VOL_CTL_TIMER] = 1,
+ [TOMTOM_A_CDC_TX5_VOL_CTL_TIMER] = 1,
+ [TOMTOM_A_CDC_TX6_VOL_CTL_TIMER] = 1,
+ [TOMTOM_A_CDC_TX7_VOL_CTL_TIMER] = 1,
+ [TOMTOM_A_CDC_TX8_VOL_CTL_TIMER] = 1,
+ [TOMTOM_A_CDC_TX9_VOL_CTL_TIMER] = 1,
+ [TOMTOM_A_CDC_TX10_VOL_CTL_TIMER] = 1,
+ [TOMTOM_A_CDC_TX1_VOL_CTL_GAIN] = 1,
+ [TOMTOM_A_CDC_TX2_VOL_CTL_GAIN] = 1,
+ [TOMTOM_A_CDC_TX3_VOL_CTL_GAIN] = 1,
+ [TOMTOM_A_CDC_TX4_VOL_CTL_GAIN] = 1,
+ [TOMTOM_A_CDC_TX5_VOL_CTL_GAIN] = 1,
+ [TOMTOM_A_CDC_TX6_VOL_CTL_GAIN] = 1,
+ [TOMTOM_A_CDC_TX7_VOL_CTL_GAIN] = 1,
+ [TOMTOM_A_CDC_TX8_VOL_CTL_GAIN] = 1,
+ [TOMTOM_A_CDC_TX9_VOL_CTL_GAIN] = 1,
+ [TOMTOM_A_CDC_TX10_VOL_CTL_GAIN] = 1,
+ [TOMTOM_A_CDC_TX1_VOL_CTL_CFG] = 1,
+ [TOMTOM_A_CDC_TX2_VOL_CTL_CFG] = 1,
+ [TOMTOM_A_CDC_TX3_VOL_CTL_CFG] = 1,
+ [TOMTOM_A_CDC_TX4_VOL_CTL_CFG] = 1,
+ [TOMTOM_A_CDC_TX5_VOL_CTL_CFG] = 1,
+ [TOMTOM_A_CDC_TX6_VOL_CTL_CFG] = 1,
+ [TOMTOM_A_CDC_TX7_VOL_CTL_CFG] = 1,
+ [TOMTOM_A_CDC_TX8_VOL_CTL_CFG] = 1,
+ [TOMTOM_A_CDC_TX9_VOL_CTL_CFG] = 1,
+ [TOMTOM_A_CDC_TX10_VOL_CTL_CFG] = 1,
+ [TOMTOM_A_CDC_TX1_MUX_CTL] = 1,
+ [TOMTOM_A_CDC_TX2_MUX_CTL] = 1,
+ [TOMTOM_A_CDC_TX3_MUX_CTL] = 1,
+ [TOMTOM_A_CDC_TX4_MUX_CTL] = 1,
+ [TOMTOM_A_CDC_TX5_MUX_CTL] = 1,
+ [TOMTOM_A_CDC_TX6_MUX_CTL] = 1,
+ [TOMTOM_A_CDC_TX7_MUX_CTL] = 1,
+ [TOMTOM_A_CDC_TX8_MUX_CTL] = 1,
+ [TOMTOM_A_CDC_TX9_MUX_CTL] = 1,
+ [TOMTOM_A_CDC_TX10_MUX_CTL] = 1,
+ [TOMTOM_A_CDC_TX1_CLK_FS_CTL] = 1,
+ [TOMTOM_A_CDC_TX2_CLK_FS_CTL] = 1,
+ [TOMTOM_A_CDC_TX3_CLK_FS_CTL] = 1,
+ [TOMTOM_A_CDC_TX4_CLK_FS_CTL] = 1,
+ [TOMTOM_A_CDC_TX5_CLK_FS_CTL] = 1,
+ [TOMTOM_A_CDC_TX6_CLK_FS_CTL] = 1,
+ [TOMTOM_A_CDC_TX7_CLK_FS_CTL] = 1,
+ [TOMTOM_A_CDC_TX8_CLK_FS_CTL] = 1,
+ [TOMTOM_A_CDC_TX9_CLK_FS_CTL] = 1,
+ [TOMTOM_A_CDC_TX10_CLK_FS_CTL] = 1,
+ [TOMTOM_A_CDC_TX1_DMIC_CTL] = 1,
+ [TOMTOM_A_CDC_TX2_DMIC_CTL] = 1,
+ [TOMTOM_A_CDC_TX3_DMIC_CTL] = 1,
+ [TOMTOM_A_CDC_TX4_DMIC_CTL] = 1,
+ [TOMTOM_A_CDC_TX5_DMIC_CTL] = 1,
+ [TOMTOM_A_CDC_TX6_DMIC_CTL] = 1,
+ [TOMTOM_A_CDC_TX7_DMIC_CTL] = 1,
+ [TOMTOM_A_CDC_TX8_DMIC_CTL] = 1,
+ [TOMTOM_A_CDC_TX9_DMIC_CTL] = 1,
+ [TOMTOM_A_CDC_TX10_DMIC_CTL] = 1,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL0] = 1,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL1] = 1,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL2] = 1,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL3] = 1,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL4] = 1,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL5] = 1,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL6] = 1,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL7] = 1,
+ [TOMTOM_A_CDC_DEBUG_B1_CTL] = 1,
+ [TOMTOM_A_CDC_DEBUG_B2_CTL] = 1,
+ [TOMTOM_A_CDC_DEBUG_B3_CTL] = 1,
+ [TOMTOM_A_CDC_DEBUG_B4_CTL] = 1,
+ [TOMTOM_A_CDC_DEBUG_B5_CTL] = 1,
+ [TOMTOM_A_CDC_DEBUG_B6_CTL] = 1,
+ [TOMTOM_A_CDC_DEBUG_B7_CTL] = 1,
+ [TOMTOM_A_CDC_SRC1_PDA_CFG] = 1,
+ [TOMTOM_A_CDC_SRC2_PDA_CFG] = 1,
+ [TOMTOM_A_CDC_SRC1_FS_CTL] = 1,
+ [TOMTOM_A_CDC_SRC2_FS_CTL] = 1,
+ [TOMTOM_A_CDC_RX1_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX2_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX3_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX4_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX5_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX6_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX7_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX1_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX2_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX3_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX4_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX5_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX6_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX7_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX1_B3_CTL] = 1,
+ [TOMTOM_A_CDC_RX2_B3_CTL] = 1,
+ [TOMTOM_A_CDC_RX3_B3_CTL] = 1,
+ [TOMTOM_A_CDC_RX4_B3_CTL] = 1,
+ [TOMTOM_A_CDC_RX5_B3_CTL] = 1,
+ [TOMTOM_A_CDC_RX6_B3_CTL] = 1,
+ [TOMTOM_A_CDC_RX7_B3_CTL] = 1,
+ [TOMTOM_A_CDC_RX1_B4_CTL] = 1,
+ [TOMTOM_A_CDC_RX2_B4_CTL] = 1,
+ [TOMTOM_A_CDC_RX3_B4_CTL] = 1,
+ [TOMTOM_A_CDC_RX4_B4_CTL] = 1,
+ [TOMTOM_A_CDC_RX5_B4_CTL] = 1,
+ [TOMTOM_A_CDC_RX6_B4_CTL] = 1,
+ [TOMTOM_A_CDC_RX7_B4_CTL] = 1,
+ [TOMTOM_A_CDC_RX1_B5_CTL] = 1,
+ [TOMTOM_A_CDC_RX2_B5_CTL] = 1,
+ [TOMTOM_A_CDC_RX3_B5_CTL] = 1,
+ [TOMTOM_A_CDC_RX4_B5_CTL] = 1,
+ [TOMTOM_A_CDC_RX5_B5_CTL] = 1,
+ [TOMTOM_A_CDC_RX6_B5_CTL] = 1,
+ [TOMTOM_A_CDC_RX7_B5_CTL] = 1,
+ [TOMTOM_A_CDC_RX1_B6_CTL] = 1,
+ [TOMTOM_A_CDC_RX2_B6_CTL] = 1,
+ [TOMTOM_A_CDC_RX3_B6_CTL] = 1,
+ [TOMTOM_A_CDC_RX4_B6_CTL] = 1,
+ [TOMTOM_A_CDC_RX5_B6_CTL] = 1,
+ [TOMTOM_A_CDC_RX6_B6_CTL] = 1,
+ [TOMTOM_A_CDC_RX7_B6_CTL] = 1,
+ [TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL] = 1,
+ [TOMTOM_A_CDC_VBAT_CFG] = 1,
+ [TOMTOM_A_CDC_VBAT_ADC_CAL1] = 1,
+ [TOMTOM_A_CDC_VBAT_ADC_CAL2] = 1,
+ [TOMTOM_A_CDC_VBAT_ADC_CAL3] = 1,
+ [TOMTOM_A_CDC_VBAT_PK_EST1] = 1,
+ [TOMTOM_A_CDC_VBAT_PK_EST2] = 1,
+ [TOMTOM_A_CDC_VBAT_PK_EST3] = 1,
+ [TOMTOM_A_CDC_VBAT_RF_PROC1] = 1,
+ [TOMTOM_A_CDC_VBAT_RF_PROC2] = 1,
+ [TOMTOM_A_CDC_VBAT_TAC1] = 1,
+ [TOMTOM_A_CDC_VBAT_TAC2] = 1,
+ [TOMTOM_A_CDC_VBAT_TAC3] = 1,
+ [TOMTOM_A_CDC_VBAT_TAC4] = 1,
+ [TOMTOM_A_CDC_VBAT_GAIN_UPD1] = 1,
+ [TOMTOM_A_CDC_VBAT_GAIN_UPD2] = 1,
+ [TOMTOM_A_CDC_VBAT_GAIN_UPD3] = 1,
+ [TOMTOM_A_CDC_VBAT_GAIN_UPD4] = 1,
+ [TOMTOM_A_CDC_VBAT_DEBUG1] = 1,
+ [TOMTOM_A_CDC_VBAT_GAIN_UPD_MON] = 0,
+ [TOMTOM_A_CDC_VBAT_GAIN_MON_VAL] = 1,
+ [TOMTOM_A_CDC_CLK_ANC_RESET_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_RX_RESET_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_RX_I2S_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_TX_I2S_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_OTHR_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_RX_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_RX_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_MCLK_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_PDM_CTL] = 1,
+ [TOMTOM_A_CDC_CLK_SD_CTL] = 1,
+ [TOMTOM_A_CDC_CLSH_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CLSH_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CLSH_B3_CTL] = 1,
+ [TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS] = 1,
+ [TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD] = 1,
+ [TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD] = 1,
+ [TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD] = 1,
+ [TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD] = 1,
+ [TOMTOM_A_CDC_CLSH_K_ADDR] = 1,
+ [TOMTOM_A_CDC_CLSH_K_DATA] = 1,
+ [TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L] = 1,
+ [TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U] = 1,
+ [TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L] = 1,
+ [TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U] = 1,
+ [TOMTOM_A_CDC_CLSH_V_PA_HD_EAR] = 1,
+ [TOMTOM_A_CDC_CLSH_V_PA_HD_HPH] = 1,
+ [TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR] = 1,
+ [TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH] = 1,
+ [TOMTOM_A_CDC_IIR1_GAIN_B1_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_GAIN_B1_CTL] = 1,
+ [TOMTOM_A_CDC_IIR1_GAIN_B2_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_GAIN_B2_CTL] = 1,
+ [TOMTOM_A_CDC_IIR1_GAIN_B3_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_GAIN_B3_CTL] = 1,
+ [TOMTOM_A_CDC_IIR1_GAIN_B4_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_GAIN_B4_CTL] = 1,
+ [TOMTOM_A_CDC_IIR1_GAIN_B5_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_GAIN_B5_CTL] = 1,
+ [TOMTOM_A_CDC_IIR1_GAIN_B6_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_GAIN_B6_CTL] = 1,
+ [TOMTOM_A_CDC_IIR1_GAIN_B7_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_GAIN_B7_CTL] = 1,
+ [TOMTOM_A_CDC_IIR1_GAIN_B8_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_GAIN_B8_CTL] = 1,
+ [TOMTOM_A_CDC_IIR1_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_CTL] = 1,
+ [TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL] = 1,
+ [TOMTOM_A_CDC_IIR1_COEF_B1_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_COEF_B1_CTL] = 1,
+ [TOMTOM_A_CDC_IIR1_COEF_B2_CTL] = 1,
+ [TOMTOM_A_CDC_IIR2_COEF_B2_CTL] = 1,
+ [TOMTOM_A_CDC_TOP_GAIN_UPDATE] = 1,
+ [TOMTOM_A_CDC_PA_RAMP_B1_CTL] = 1,
+ [TOMTOM_A_CDC_PA_RAMP_B2_CTL] = 1,
+ [TOMTOM_A_CDC_PA_RAMP_B3_CTL] = 1,
+ [TOMTOM_A_CDC_PA_RAMP_B4_CTL] = 1,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL] = 1,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL] = 1,
+ [TOMTOM_A_CDC_COMP0_B1_CTL] = 1,
+ [TOMTOM_A_CDC_COMP1_B1_CTL] = 1,
+ [TOMTOM_A_CDC_COMP2_B1_CTL] = 1,
+ [TOMTOM_A_CDC_COMP0_B2_CTL] = 1,
+ [TOMTOM_A_CDC_COMP1_B2_CTL] = 1,
+ [TOMTOM_A_CDC_COMP2_B2_CTL] = 1,
+ [TOMTOM_A_CDC_COMP0_B3_CTL] = 1,
+ [TOMTOM_A_CDC_COMP1_B3_CTL] = 1,
+ [TOMTOM_A_CDC_COMP2_B3_CTL] = 1,
+ [TOMTOM_A_CDC_COMP0_B4_CTL] = 1,
+ [TOMTOM_A_CDC_COMP1_B4_CTL] = 1,
+ [TOMTOM_A_CDC_COMP2_B4_CTL] = 1,
+ [TOMTOM_A_CDC_COMP0_B5_CTL] = 1,
+ [TOMTOM_A_CDC_COMP1_B5_CTL] = 1,
+ [TOMTOM_A_CDC_COMP2_B5_CTL] = 1,
+ [TOMTOM_A_CDC_COMP0_B6_CTL] = 1,
+ [TOMTOM_A_CDC_COMP1_B6_CTL] = 1,
+ [TOMTOM_A_CDC_COMP2_B6_CTL] = 1,
+ [TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS] = 1,
+ [TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS] = 1,
+ [TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS] = 1,
+ [TOMTOM_A_CDC_COMP0_FS_CFG] = 1,
+ [TOMTOM_A_CDC_COMP1_FS_CFG] = 1,
+ [TOMTOM_A_CDC_COMP2_FS_CFG] = 1,
+ [TOMTOM_A_CDC_CONN_RX1_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX1_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX1_B3_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX2_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX2_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX2_B3_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX3_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX3_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX4_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX4_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX5_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX5_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX6_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX6_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX7_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX7_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX7_B3_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_ANC_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_ANC_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_B3_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_B4_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_EQ1_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_EQ1_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_EQ1_B3_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_EQ1_B4_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_EQ2_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_EQ2_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_EQ2_B3_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_EQ2_B4_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_SRC1_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_SRC1_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_SRC2_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_SRC2_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_SB_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_SB_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_SB_B3_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_SB_B4_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_SB_B5_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_SB_B6_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_SB_B7_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_SB_B8_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_SB_B9_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_SB_B10_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_TX_SB_B11_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX_SB_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_RX_SB_B2_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_CLSH_CTL] = 1,
+ [TOMTOM_A_CDC_CONN_MISC] = 1,
+ [TOMTOM_A_CDC_CONN_RX8_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK] = 1,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING] = 1,
+ [TOMTOM_A_CDC_MBHC_EN_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_FIR_B1_CFG] = 1,
+ [TOMTOM_A_CDC_MBHC_FIR_B2_CFG] = 1,
+ [TOMTOM_A_CDC_MBHC_TIMER_B1_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_TIMER_B2_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_TIMER_B3_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_TIMER_B4_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_TIMER_B5_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_TIMER_B6_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_B1_STATUS] = 1,
+ [TOMTOM_A_CDC_MBHC_B2_STATUS] = 1,
+ [TOMTOM_A_CDC_MBHC_B3_STATUS] = 1,
+ [TOMTOM_A_CDC_MBHC_B4_STATUS] = 1,
+ [TOMTOM_A_CDC_MBHC_B5_STATUS] = 1,
+ [TOMTOM_A_CDC_MBHC_B1_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_B2_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B1_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B2_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B3_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B4_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B5_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B6_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B7_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B8_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B9_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B10_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B11_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_VOLT_B12_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_CLK_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_INT_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_DEBUG_CTL] = 1,
+ [TOMTOM_A_CDC_MBHC_SPARE] = 1,
+ [TOMTOM_A_CDC_RX8_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX8_B2_CTL] = 1,
+ [TOMTOM_A_CDC_RX8_B3_CTL] = 1,
+ [TOMTOM_A_CDC_RX8_B4_CTL] = 1,
+ [TOMTOM_A_CDC_RX8_B5_CTL] = 1,
+ [TOMTOM_A_CDC_RX8_B6_CTL] = 1,
+ [TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL] = 1,
+ [TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL] = 1,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0] = 1,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1] = 1,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2] = 1,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3] = 1,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4] = 1,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5] = 1,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6] = 1,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7] = 1,
+ [TOMTOM_A_CDC_BOOST_MODE_CTL] = 1,
+ [TOMTOM_A_CDC_BOOST_THRESHOLD] = 1,
+ [TOMTOM_A_CDC_BOOST_TAP_SEL] = 1,
+ [TOMTOM_A_CDC_BOOST_HOLD_TIME] = 1,
+ [TOMTOM_A_CDC_BOOST_TRGR_EN] = 1,
+};
+
+const u8 tomtom_reset_reg_defaults[TOMTOM_CACHE_SIZE] = {
+ [TOMTOM_A_CHIP_CTL] = TOMTOM_A_CHIP_CTL__POR,
+ [TOMTOM_A_CHIP_STATUS] = TOMTOM_A_CHIP_STATUS__POR,
+ [TOMTOM_A_CHIP_ID_BYTE_0] = TOMTOM_A_CHIP_ID_BYTE_0__POR,
+ [TOMTOM_A_CHIP_ID_BYTE_1] = TOMTOM_A_CHIP_ID_BYTE_1__POR,
+ [TOMTOM_A_CHIP_ID_BYTE_2] = TOMTOM_A_CHIP_ID_BYTE_2__POR,
+ [TOMTOM_A_CHIP_ID_BYTE_3] = TOMTOM_A_CHIP_ID_BYTE_3__POR,
+ [TOMTOM_A_CHIP_I2C_SLAVE_ID] = TOMTOM_A_CHIP_I2C_SLAVE_ID__POR,
+ [TOMTOM_A_SLAVE_ID_1] = TOMTOM_A_SLAVE_ID_1__POR,
+ [TOMTOM_A_SLAVE_ID_2] = TOMTOM_A_SLAVE_ID_2__POR,
+ [TOMTOM_A_SLAVE_ID_3] = TOMTOM_A_SLAVE_ID_3__POR,
+ [TOMTOM_A_PIN_CTL_OE0] = TOMTOM_A_PIN_CTL_OE0__POR,
+ [TOMTOM_A_PIN_CTL_OE1] = TOMTOM_A_PIN_CTL_OE1__POR,
+ [TOMTOM_A_PIN_CTL_OE2] = TOMTOM_A_PIN_CTL_OE2__POR,
+ [TOMTOM_A_PIN_CTL_DATA0] = TOMTOM_A_PIN_CTL_DATA0__POR,
+ [TOMTOM_A_PIN_CTL_DATA1] = TOMTOM_A_PIN_CTL_DATA1__POR,
+ [TOMTOM_A_PIN_CTL_DATA2] = TOMTOM_A_PIN_CTL_DATA2__POR,
+ [TOMTOM_A_HDRIVE_GENERIC] = TOMTOM_A_HDRIVE_GENERIC__POR,
+ [TOMTOM_A_HDRIVE_OVERRIDE] = TOMTOM_A_HDRIVE_OVERRIDE__POR,
+ [TOMTOM_A_ANA_CSR_WAIT_STATE] = TOMTOM_A_ANA_CSR_WAIT_STATE__POR,
+ [TOMTOM_A_PROCESS_MONITOR_CTL0] = TOMTOM_A_PROCESS_MONITOR_CTL0__POR,
+ [TOMTOM_A_PROCESS_MONITOR_CTL1] = TOMTOM_A_PROCESS_MONITOR_CTL1__POR,
+ [TOMTOM_A_PROCESS_MONITOR_CTL2] = TOMTOM_A_PROCESS_MONITOR_CTL2__POR,
+ [TOMTOM_A_PROCESS_MONITOR_CTL3] = TOMTOM_A_PROCESS_MONITOR_CTL3__POR,
+ [TOMTOM_A_QFUSE_CTL] = TOMTOM_A_QFUSE_CTL__POR,
+ [TOMTOM_A_QFUSE_STATUS] = TOMTOM_A_QFUSE_STATUS__POR,
+ [TOMTOM_A_QFUSE_DATA_OUT0] = TOMTOM_A_QFUSE_DATA_OUT0__POR,
+ [TOMTOM_A_QFUSE_DATA_OUT1] = TOMTOM_A_QFUSE_DATA_OUT1__POR,
+ [TOMTOM_A_QFUSE_DATA_OUT2] = TOMTOM_A_QFUSE_DATA_OUT2__POR,
+ [TOMTOM_A_QFUSE_DATA_OUT3] = TOMTOM_A_QFUSE_DATA_OUT3__POR,
+ [TOMTOM_A_QFUSE_DATA_OUT4] = TOMTOM_A_QFUSE_DATA_OUT4__POR,
+ [TOMTOM_A_QFUSE_DATA_OUT5] = TOMTOM_A_QFUSE_DATA_OUT5__POR,
+ [TOMTOM_A_QFUSE_DATA_OUT6] = TOMTOM_A_QFUSE_DATA_OUT6__POR,
+ [TOMTOM_A_QFUSE_DATA_OUT7] = TOMTOM_A_QFUSE_DATA_OUT7__POR,
+ [TOMTOM_A_CDC_CTL] = TOMTOM_A_CDC_CTL__POR,
+ [TOMTOM_A_LEAKAGE_CTL] = TOMTOM_A_LEAKAGE_CTL__POR,
+ [TOMTOM_A_SVASS_MEM_PTR0] = TOMTOM_A_SVASS_MEM_PTR0__POR,
+ [TOMTOM_A_SVASS_MEM_PTR1] = TOMTOM_A_SVASS_MEM_PTR1__POR,
+ [TOMTOM_A_SVASS_MEM_PTR2] = TOMTOM_A_SVASS_MEM_PTR2__POR,
+ [TOMTOM_A_SVASS_MEM_CTL] = TOMTOM_A_SVASS_MEM_CTL__POR,
+ [TOMTOM_A_SVASS_MEM_BANK] = TOMTOM_A_SVASS_MEM_BANK__POR,
+ [TOMTOM_A_DMIC_B1_CTL] = TOMTOM_A_DMIC_B1_CTL__POR,
+ [TOMTOM_A_DMIC_B2_CTL] = TOMTOM_A_DMIC_B2_CTL__POR,
+ [TOMTOM_A_SVASS_CLKRST_CTL] = TOMTOM_A_SVASS_CLKRST_CTL__POR,
+ [TOMTOM_A_SVASS_CPAR_CFG] = TOMTOM_A_SVASS_CPAR_CFG__POR,
+ [TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD] =
+ TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD__POR,
+ [TOMTOM_A_SVASS_CPAR_WDOG_CFG] = TOMTOM_A_SVASS_CPAR_WDOG_CFG__POR,
+ [TOMTOM_A_SVASS_CFG] = TOMTOM_A_SVASS_CFG__POR,
+ [TOMTOM_A_SVASS_SPE_CFG] = TOMTOM_A_SVASS_SPE_CFG__POR,
+ [TOMTOM_A_SVASS_STATUS] = TOMTOM_A_SVASS_STATUS__POR,
+ [TOMTOM_A_SVASS_INT_MASK] = TOMTOM_A_SVASS_INT_MASK__POR,
+ [TOMTOM_A_SVASS_INT_STATUS] = TOMTOM_A_SVASS_INT_STATUS__POR,
+ [TOMTOM_A_SVASS_INT_CLR] = TOMTOM_A_SVASS_INT_CLR__POR,
+ [TOMTOM_A_SVASS_DEBUG] = TOMTOM_A_SVASS_DEBUG__POR,
+ [TOMTOM_A_SVASS_SPE_BKUP_INT] = TOMTOM_A_SVASS_SPE_BKUP_INT__POR,
+ [TOMTOM_A_SVASS_MEM_ACC] = TOMTOM_A_SVASS_MEM_ACC__POR,
+ [TOMTOM_A_MEM_LEAKAGE_CTL] = TOMTOM_A_MEM_LEAKAGE_CTL__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_TRG] = TOMTOM_A_SVASS_SPE_INBOX_TRG__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_0] = TOMTOM_A_SVASS_SPE_INBOX_0__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_1] = TOMTOM_A_SVASS_SPE_INBOX_1__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_2] = TOMTOM_A_SVASS_SPE_INBOX_2__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_3] = TOMTOM_A_SVASS_SPE_INBOX_3__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_4] = TOMTOM_A_SVASS_SPE_INBOX_4__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_5] = TOMTOM_A_SVASS_SPE_INBOX_5__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_6] = TOMTOM_A_SVASS_SPE_INBOX_6__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_7] = TOMTOM_A_SVASS_SPE_INBOX_7__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_8] = TOMTOM_A_SVASS_SPE_INBOX_8__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_9] = TOMTOM_A_SVASS_SPE_INBOX_9__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_10] = TOMTOM_A_SVASS_SPE_INBOX_10__POR,
+ [TOMTOM_A_SVASS_SPE_INBOX_11] = TOMTOM_A_SVASS_SPE_INBOX_11__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_0] = TOMTOM_A_SVASS_SPE_OUTBOX_0__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_1] = TOMTOM_A_SVASS_SPE_OUTBOX_1__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_2] = TOMTOM_A_SVASS_SPE_OUTBOX_2__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_3] = TOMTOM_A_SVASS_SPE_OUTBOX_3__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_4] = TOMTOM_A_SVASS_SPE_OUTBOX_4__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_5] = TOMTOM_A_SVASS_SPE_OUTBOX_5__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_6] = TOMTOM_A_SVASS_SPE_OUTBOX_6__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_7] = TOMTOM_A_SVASS_SPE_OUTBOX_7__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_8] = TOMTOM_A_SVASS_SPE_OUTBOX_8__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_9] = TOMTOM_A_SVASS_SPE_OUTBOX_9__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_10] = TOMTOM_A_SVASS_SPE_OUTBOX_10__POR,
+ [TOMTOM_A_SVASS_SPE_OUTBOX_11] = TOMTOM_A_SVASS_SPE_OUTBOX_11__POR,
+ [TOMTOM_A_INTR_MODE] = TOMTOM_A_INTR_MODE__POR,
+ [TOMTOM_A_INTR1_MASK0] = TOMTOM_A_INTR1_MASK0__POR,
+ [TOMTOM_A_INTR1_MASK1] = TOMTOM_A_INTR1_MASK1__POR,
+ [TOMTOM_A_INTR1_MASK2] = TOMTOM_A_INTR1_MASK2__POR,
+ [TOMTOM_A_INTR1_MASK3] = TOMTOM_A_INTR1_MASK3__POR,
+ [TOMTOM_A_INTR1_STATUS0] = TOMTOM_A_INTR1_STATUS0__POR,
+ [TOMTOM_A_INTR1_STATUS1] = TOMTOM_A_INTR1_STATUS1__POR,
+ [TOMTOM_A_INTR1_STATUS2] = TOMTOM_A_INTR1_STATUS2__POR,
+ [TOMTOM_A_INTR1_STATUS3] = TOMTOM_A_INTR1_STATUS3__POR,
+ [TOMTOM_A_INTR1_CLEAR0] = TOMTOM_A_INTR1_CLEAR0__POR,
+ [TOMTOM_A_INTR1_CLEAR1] = TOMTOM_A_INTR1_CLEAR1__POR,
+ [TOMTOM_A_INTR1_CLEAR2] = TOMTOM_A_INTR1_CLEAR2__POR,
+ [TOMTOM_A_INTR1_CLEAR3] = TOMTOM_A_INTR1_CLEAR3__POR,
+ [TOMTOM_A_INTR1_LEVEL0] = TOMTOM_A_INTR1_LEVEL0__POR,
+ [TOMTOM_A_INTR1_LEVEL1] = TOMTOM_A_INTR1_LEVEL1__POR,
+ [TOMTOM_A_INTR1_LEVEL2] = TOMTOM_A_INTR1_LEVEL2__POR,
+ [TOMTOM_A_INTR1_LEVEL3] = TOMTOM_A_INTR1_LEVEL3__POR,
+ [TOMTOM_A_INTR1_TEST0] = TOMTOM_A_INTR1_TEST0__POR,
+ [TOMTOM_A_INTR1_TEST1] = TOMTOM_A_INTR1_TEST1__POR,
+ [TOMTOM_A_INTR1_TEST2] = TOMTOM_A_INTR1_TEST2__POR,
+ [TOMTOM_A_INTR1_TEST3] = TOMTOM_A_INTR1_TEST3__POR,
+ [TOMTOM_A_INTR1_SET0] = TOMTOM_A_INTR1_SET0__POR,
+ [TOMTOM_A_INTR1_SET1] = TOMTOM_A_INTR1_SET1__POR,
+ [TOMTOM_A_INTR1_SET2] = TOMTOM_A_INTR1_SET2__POR,
+ [TOMTOM_A_INTR1_SET3] = TOMTOM_A_INTR1_SET3__POR,
+ [TOMTOM_A_INTR2_MASK0] = TOMTOM_A_INTR2_MASK0__POR,
+ [TOMTOM_A_INTR2_STATUS0] = TOMTOM_A_INTR2_STATUS0__POR,
+ [TOMTOM_A_INTR2_CLEAR0] = TOMTOM_A_INTR2_CLEAR0__POR,
+ [TOMTOM_A_INTR2_LEVEL0] = TOMTOM_A_INTR2_LEVEL0__POR,
+ [TOMTOM_A_INTR2_TEST0] = TOMTOM_A_INTR2_TEST0__POR,
+ [TOMTOM_A_INTR2_SET0] = TOMTOM_A_INTR2_SET0__POR,
+ [TOMTOM_A_CDC_TX_I2S_SCK_MODE] = TOMTOM_A_CDC_TX_I2S_SCK_MODE__POR,
+ [TOMTOM_A_CDC_TX_I2S_WS_MODE] = TOMTOM_A_CDC_TX_I2S_WS_MODE__POR,
+ [TOMTOM_A_CDC_DMIC_DATA0_MODE] = TOMTOM_A_CDC_DMIC_DATA0_MODE__POR,
+ [TOMTOM_A_CDC_DMIC_CLK0_MODE] = TOMTOM_A_CDC_DMIC_CLK0_MODE__POR,
+ [TOMTOM_A_CDC_DMIC_DATA1_MODE] = TOMTOM_A_CDC_DMIC_DATA1_MODE__POR,
+ [TOMTOM_A_CDC_DMIC_CLK1_MODE] = TOMTOM_A_CDC_DMIC_CLK1_MODE__POR,
+ [TOMTOM_A_CDC_RX_I2S_SCK_MODE] = TOMTOM_A_CDC_RX_I2S_SCK_MODE__POR,
+ [TOMTOM_A_CDC_RX_I2S_WS_MODE] = TOMTOM_A_CDC_RX_I2S_WS_MODE__POR,
+ [TOMTOM_A_CDC_DMIC_DATA2_MODE] = TOMTOM_A_CDC_DMIC_DATA2_MODE__POR,
+ [TOMTOM_A_CDC_DMIC_CLK2_MODE] = TOMTOM_A_CDC_DMIC_CLK2_MODE__POR,
+ [TOMTOM_A_CDC_INTR1_MODE] = TOMTOM_A_CDC_INTR1_MODE__POR,
+ [TOMTOM_A_CDC_SB_NRZ_SEL_MODE] = TOMTOM_A_CDC_SB_NRZ_SEL_MODE__POR,
+ [TOMTOM_A_CDC_INTR2_MODE] = TOMTOM_A_CDC_INTR2_MODE__POR,
+ [TOMTOM_A_CDC_RF_PA_ON_MODE] = TOMTOM_A_CDC_RF_PA_ON_MODE__POR,
+ [TOMTOM_A_CDC_BOOST_MODE] = TOMTOM_A_CDC_BOOST_MODE__POR,
+ [TOMTOM_A_CDC_JTCK_MODE] = TOMTOM_A_CDC_JTCK_MODE__POR,
+ [TOMTOM_A_CDC_JTDI_MODE] = TOMTOM_A_CDC_JTDI_MODE__POR,
+ [TOMTOM_A_CDC_JTMS_MODE] = TOMTOM_A_CDC_JTMS_MODE__POR,
+ [TOMTOM_A_CDC_JTDO_MODE] = TOMTOM_A_CDC_JTDO_MODE__POR,
+ [TOMTOM_A_CDC_JTRST_MODE] = TOMTOM_A_CDC_JTRST_MODE__POR,
+ [TOMTOM_A_CDC_BIST_MODE_MODE] = TOMTOM_A_CDC_BIST_MODE_MODE__POR,
+ [TOMTOM_A_CDC_MAD_MAIN_CTL_1] = TOMTOM_A_CDC_MAD_MAIN_CTL_1__POR,
+ [TOMTOM_A_CDC_MAD_MAIN_CTL_2] = TOMTOM_A_CDC_MAD_MAIN_CTL_2__POR,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_1] = TOMTOM_A_CDC_MAD_AUDIO_CTL_1__POR,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_2] = TOMTOM_A_CDC_MAD_AUDIO_CTL_2__POR,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_3] = TOMTOM_A_CDC_MAD_AUDIO_CTL_3__POR,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_4] = TOMTOM_A_CDC_MAD_AUDIO_CTL_4__POR,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_5] = TOMTOM_A_CDC_MAD_AUDIO_CTL_5__POR,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_6] = TOMTOM_A_CDC_MAD_AUDIO_CTL_6__POR,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_7] = TOMTOM_A_CDC_MAD_AUDIO_CTL_7__POR,
+ [TOMTOM_A_CDC_MAD_AUDIO_CTL_8] = TOMTOM_A_CDC_MAD_AUDIO_CTL_8__POR,
+ [TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR] =
+ TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR__POR,
+ [TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL] =
+ TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL__POR,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_1] = TOMTOM_A_CDC_MAD_ULTR_CTL_1__POR,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_2] = TOMTOM_A_CDC_MAD_ULTR_CTL_2__POR,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_3] = TOMTOM_A_CDC_MAD_ULTR_CTL_3__POR,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_4] = TOMTOM_A_CDC_MAD_ULTR_CTL_4__POR,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_5] = TOMTOM_A_CDC_MAD_ULTR_CTL_5__POR,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_6] = TOMTOM_A_CDC_MAD_ULTR_CTL_6__POR,
+ [TOMTOM_A_CDC_MAD_ULTR_CTL_7] = TOMTOM_A_CDC_MAD_ULTR_CTL_7__POR,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_1] = TOMTOM_A_CDC_MAD_BEACON_CTL_1__POR,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_2] = TOMTOM_A_CDC_MAD_BEACON_CTL_2__POR,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_3] = TOMTOM_A_CDC_MAD_BEACON_CTL_3__POR,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_4] = TOMTOM_A_CDC_MAD_BEACON_CTL_4__POR,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_5] = TOMTOM_A_CDC_MAD_BEACON_CTL_5__POR,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_6] = TOMTOM_A_CDC_MAD_BEACON_CTL_6__POR,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_7] = TOMTOM_A_CDC_MAD_BEACON_CTL_7__POR,
+ [TOMTOM_A_CDC_MAD_BEACON_CTL_8] = TOMTOM_A_CDC_MAD_BEACON_CTL_8__POR,
+ [TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR] =
+ TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR__POR,
+ [TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL] =
+ TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL__POR,
+ [TOMTOM_A_CDC_MAD_INP_SEL] = TOMTOM_A_CDC_MAD_INP_SEL__POR,
+ [TOMTOM_A_BIAS_REF_CTL] = TOMTOM_A_BIAS_REF_CTL__POR,
+ [TOMTOM_A_BIAS_CENTRAL_BG_CTL] = TOMTOM_A_BIAS_CENTRAL_BG_CTL__POR,
+ [TOMTOM_A_BIAS_PRECHRG_CTL] = TOMTOM_A_BIAS_PRECHRG_CTL__POR,
+ [TOMTOM_A_BIAS_CURR_CTL_1] = TOMTOM_A_BIAS_CURR_CTL_1__POR,
+ [TOMTOM_A_BIAS_CURR_CTL_2] = TOMTOM_A_BIAS_CURR_CTL_2__POR,
+ [TOMTOM_A_BIAS_OSC_BG_CTL] = TOMTOM_A_BIAS_OSC_BG_CTL__POR,
+ [TOMTOM_A_CLK_BUFF_EN1] = TOMTOM_A_CLK_BUFF_EN1__POR,
+ [TOMTOM_A_CLK_BUFF_EN2] = TOMTOM_A_CLK_BUFF_EN2__POR,
+ [TOMTOM_A_LDO_L_MODE_1] = TOMTOM_A_LDO_L_MODE_1__POR,
+ [TOMTOM_A_LDO_L_MODE_2] = TOMTOM_A_LDO_L_MODE_2__POR,
+ [TOMTOM_A_LDO_L_CTRL_1] = TOMTOM_A_LDO_L_CTRL_1__POR,
+ [TOMTOM_A_LDO_L_CTRL_2] = TOMTOM_A_LDO_L_CTRL_2__POR,
+ [TOMTOM_A_LDO_L_CTRL_3] = TOMTOM_A_LDO_L_CTRL_3__POR,
+ [TOMTOM_A_LDO_L_CTRL_4] = TOMTOM_A_LDO_L_CTRL_4__POR,
+ [TOMTOM_A_LDO_H_MODE_1] = TOMTOM_A_LDO_H_MODE_1__POR,
+ [TOMTOM_A_LDO_H_MODE_2] = TOMTOM_A_LDO_H_MODE_2__POR,
+ [TOMTOM_A_LDO_H_LOOP_CTL] = TOMTOM_A_LDO_H_LOOP_CTL__POR,
+ [TOMTOM_A_LDO_H_COMP_1] = TOMTOM_A_LDO_H_COMP_1__POR,
+ [TOMTOM_A_LDO_H_COMP_2] = TOMTOM_A_LDO_H_COMP_2__POR,
+ [TOMTOM_A_LDO_H_BIAS_1] = TOMTOM_A_LDO_H_BIAS_1__POR,
+ [TOMTOM_A_LDO_H_BIAS_2] = TOMTOM_A_LDO_H_BIAS_2__POR,
+ [TOMTOM_A_LDO_H_BIAS_3] = TOMTOM_A_LDO_H_BIAS_3__POR,
+ [TOMTOM_A_VBAT_CLK] = TOMTOM_A_VBAT_CLK__POR,
+ [TOMTOM_A_VBAT_LOOP] = TOMTOM_A_VBAT_LOOP__POR,
+ [TOMTOM_A_VBAT_REF] = TOMTOM_A_VBAT_REF__POR,
+ [TOMTOM_A_VBAT_ADC_TEST] = TOMTOM_A_VBAT_ADC_TEST__POR,
+ [TOMTOM_A_VBAT_FE] = TOMTOM_A_VBAT_FE__POR,
+ [TOMTOM_A_VBAT_BIAS_1] = TOMTOM_A_VBAT_BIAS_1__POR,
+ [TOMTOM_A_VBAT_BIAS_2] = TOMTOM_A_VBAT_BIAS_2__POR,
+ [TOMTOM_A_VBAT_ADC_DATA_MSB] = TOMTOM_A_VBAT_ADC_DATA_MSB__POR,
+ [TOMTOM_A_VBAT_ADC_DATA_LSB] = TOMTOM_A_VBAT_ADC_DATA_LSB__POR,
+ [TOMTOM_A_FLL_NREF] = TOMTOM_A_FLL_NREF__POR,
+ [TOMTOM_A_FLL_KDCO_TUNE] = TOMTOM_A_FLL_KDCO_TUNE__POR,
+ [TOMTOM_A_FLL_LOCK_THRESH] = TOMTOM_A_FLL_LOCK_THRESH__POR,
+ [TOMTOM_A_FLL_LOCK_DET_COUNT] = TOMTOM_A_FLL_LOCK_DET_COUNT__POR,
+ [TOMTOM_A_FLL_DAC_THRESHOLD] = TOMTOM_A_FLL_DAC_THRESHOLD__POR,
+ [TOMTOM_A_FLL_TEST_DCO_FREERUN] = TOMTOM_A_FLL_TEST_DCO_FREERUN__POR,
+ [TOMTOM_A_FLL_TEST_ENABLE] = TOMTOM_A_FLL_TEST_ENABLE__POR,
+ [TOMTOM_A_MICB_CFILT_1_CTL] = TOMTOM_A_MICB_CFILT_1_CTL__POR,
+ [TOMTOM_A_MICB_CFILT_1_VAL] = TOMTOM_A_MICB_CFILT_1_VAL__POR,
+ [TOMTOM_A_MICB_CFILT_1_PRECHRG] = TOMTOM_A_MICB_CFILT_1_PRECHRG__POR,
+ [TOMTOM_A_MICB_1_CTL] = TOMTOM_A_MICB_1_CTL__POR,
+ [TOMTOM_A_MICB_1_INT_RBIAS] = TOMTOM_A_MICB_1_INT_RBIAS__POR,
+ [TOMTOM_A_MICB_1_MBHC] = TOMTOM_A_MICB_1_MBHC__POR,
+ [TOMTOM_A_MICB_CFILT_2_CTL] = TOMTOM_A_MICB_CFILT_2_CTL__POR,
+ [TOMTOM_A_MICB_CFILT_2_VAL] = TOMTOM_A_MICB_CFILT_2_VAL__POR,
+ [TOMTOM_A_MICB_CFILT_2_PRECHRG] = TOMTOM_A_MICB_CFILT_2_PRECHRG__POR,
+ [TOMTOM_A_MICB_2_CTL] = TOMTOM_A_MICB_2_CTL__POR,
+ [TOMTOM_A_MICB_2_INT_RBIAS] = TOMTOM_A_MICB_2_INT_RBIAS__POR,
+ [TOMTOM_A_MICB_2_MBHC] = TOMTOM_A_MICB_2_MBHC__POR,
+ [TOMTOM_A_MICB_CFILT_3_CTL] = TOMTOM_A_MICB_CFILT_3_CTL__POR,
+ [TOMTOM_A_MICB_CFILT_3_VAL] = TOMTOM_A_MICB_CFILT_3_VAL__POR,
+ [TOMTOM_A_MICB_CFILT_3_PRECHRG] = TOMTOM_A_MICB_CFILT_3_PRECHRG__POR,
+ [TOMTOM_A_MICB_3_CTL] = TOMTOM_A_MICB_3_CTL__POR,
+ [TOMTOM_A_MICB_3_INT_RBIAS] = TOMTOM_A_MICB_3_INT_RBIAS__POR,
+ [TOMTOM_A_MICB_3_MBHC] = TOMTOM_A_MICB_3_MBHC__POR,
+ [TOMTOM_A_MICB_4_CTL] = TOMTOM_A_MICB_4_CTL__POR,
+ [TOMTOM_A_MICB_4_INT_RBIAS] = TOMTOM_A_MICB_4_INT_RBIAS__POR,
+ [TOMTOM_A_MICB_4_MBHC] = TOMTOM_A_MICB_4_MBHC__POR,
+ [TOMTOM_A_SPKR_DRV2_EN] = TOMTOM_A_SPKR_DRV2_EN__POR,
+ [TOMTOM_A_SPKR_DRV2_GAIN] = TOMTOM_A_SPKR_DRV2_GAIN__POR,
+ [TOMTOM_A_SPKR_DRV2_DAC_CTL] = TOMTOM_A_SPKR_DRV2_DAC_CTL__POR,
+ [TOMTOM_A_SPKR_DRV2_OCP_CTL] = TOMTOM_A_SPKR_DRV2_OCP_CTL__POR,
+ [TOMTOM_A_SPKR_DRV2_CLIP_DET] = TOMTOM_A_SPKR_DRV2_CLIP_DET__POR,
+ [TOMTOM_A_SPKR_DRV2_DBG_DAC] = TOMTOM_A_SPKR_DRV2_DBG_DAC__POR,
+ [TOMTOM_A_SPKR_DRV2_DBG_PA] = TOMTOM_A_SPKR_DRV2_DBG_PA__POR,
+ [TOMTOM_A_SPKR_DRV2_DBG_PWRSTG] = TOMTOM_A_SPKR_DRV2_DBG_PWRSTG__POR,
+ [TOMTOM_A_SPKR_DRV2_BIAS_LDO] = TOMTOM_A_SPKR_DRV2_BIAS_LDO__POR,
+ [TOMTOM_A_SPKR_DRV2_BIAS_INT] = TOMTOM_A_SPKR_DRV2_BIAS_INT__POR,
+ [TOMTOM_A_SPKR_DRV2_BIAS_PA] = TOMTOM_A_SPKR_DRV2_BIAS_PA__POR,
+ [TOMTOM_A_SPKR_DRV2_STATUS_OCP] = TOMTOM_A_SPKR_DRV2_STATUS_OCP__POR,
+ [TOMTOM_A_SPKR_DRV2_STATUS_PA] = TOMTOM_A_SPKR_DRV2_STATUS_PA__POR,
+ [TOMTOM_A_MBHC_INSERT_DETECT] = TOMTOM_A_MBHC_INSERT_DETECT__POR,
+ [TOMTOM_A_MBHC_INSERT_DET_STATUS] =
+ TOMTOM_A_MBHC_INSERT_DET_STATUS__POR,
+ [TOMTOM_A_TX_COM_BIAS] = TOMTOM_A_TX_COM_BIAS__POR,
+ [TOMTOM_A_MBHC_INSERT_DETECT2] = TOMTOM_A_MBHC_INSERT_DETECT2__POR,
+ [TOMTOM_A_MBHC_SCALING_MUX_1] = TOMTOM_A_MBHC_SCALING_MUX_1__POR,
+ [TOMTOM_A_MBHC_SCALING_MUX_2] = TOMTOM_A_MBHC_SCALING_MUX_2__POR,
+ [TOMTOM_A_MAD_ANA_CTRL] = TOMTOM_A_MAD_ANA_CTRL__POR,
+ [TOMTOM_A_TX_SUP_SWITCH_CTRL_1] = TOMTOM_A_TX_SUP_SWITCH_CTRL_1__POR,
+ [TOMTOM_A_TX_SUP_SWITCH_CTRL_2] = TOMTOM_A_TX_SUP_SWITCH_CTRL_2__POR,
+ [TOMTOM_A_TX_1_GAIN] = TOMTOM_A_TX_1_GAIN__POR,
+ [TOMTOM_A_TX_1_2_TEST_EN] = TOMTOM_A_TX_1_2_TEST_EN__POR,
+ [TOMTOM_A_TX_2_GAIN] = TOMTOM_A_TX_2_GAIN__POR,
+ [TOMTOM_A_TX_1_2_ADC_IB] = TOMTOM_A_TX_1_2_ADC_IB__POR,
+ [TOMTOM_A_TX_1_2_ATEST_REFCTRL] = TOMTOM_A_TX_1_2_ATEST_REFCTRL__POR,
+ [TOMTOM_A_TX_1_2_TEST_CTL] = TOMTOM_A_TX_1_2_TEST_CTL__POR,
+ [TOMTOM_A_TX_1_2_TEST_BLOCK_EN] = TOMTOM_A_TX_1_2_TEST_BLOCK_EN__POR,
+ [TOMTOM_A_TX_1_2_TXFE_CLKDIV] = TOMTOM_A_TX_1_2_TXFE_CLKDIV__POR,
+ [TOMTOM_A_TX_1_2_SAR_ERR_CH1] = TOMTOM_A_TX_1_2_SAR_ERR_CH1__POR,
+ [TOMTOM_A_TX_1_2_SAR_ERR_CH2] = TOMTOM_A_TX_1_2_SAR_ERR_CH2__POR,
+ [TOMTOM_A_TX_3_GAIN] = TOMTOM_A_TX_3_GAIN__POR,
+ [TOMTOM_A_TX_3_4_TEST_EN] = TOMTOM_A_TX_3_4_TEST_EN__POR,
+ [TOMTOM_A_TX_4_GAIN] = TOMTOM_A_TX_4_GAIN__POR,
+ [TOMTOM_A_TX_3_4_ADC_IB] = TOMTOM_A_TX_3_4_ADC_IB__POR,
+ [TOMTOM_A_TX_3_4_ATEST_REFCTRL] = TOMTOM_A_TX_3_4_ATEST_REFCTRL__POR,
+ [TOMTOM_A_TX_3_4_TEST_CTL] = TOMTOM_A_TX_3_4_TEST_CTL__POR,
+ [TOMTOM_A_TX_3_4_TEST_BLOCK_EN] = TOMTOM_A_TX_3_4_TEST_BLOCK_EN__POR,
+ [TOMTOM_A_TX_3_4_TXFE_CKDIV] = TOMTOM_A_TX_3_4_TXFE_CKDIV__POR,
+ [TOMTOM_A_TX_3_4_SAR_ERR_CH3] = TOMTOM_A_TX_3_4_SAR_ERR_CH3__POR,
+ [TOMTOM_A_TX_3_4_SAR_ERR_CH4] = TOMTOM_A_TX_3_4_SAR_ERR_CH4__POR,
+ [TOMTOM_A_TX_5_GAIN] = TOMTOM_A_TX_5_GAIN__POR,
+ [TOMTOM_A_TX_5_6_TEST_EN] = TOMTOM_A_TX_5_6_TEST_EN__POR,
+ [TOMTOM_A_TX_6_GAIN] = TOMTOM_A_TX_6_GAIN__POR,
+ [TOMTOM_A_TX_5_6_ADC_IB] = TOMTOM_A_TX_5_6_ADC_IB__POR,
+ [TOMTOM_A_TX_5_6_ATEST_REFCTRL] = TOMTOM_A_TX_5_6_ATEST_REFCTRL__POR,
+ [TOMTOM_A_TX_5_6_TEST_CTL] = TOMTOM_A_TX_5_6_TEST_CTL__POR,
+ [TOMTOM_A_TX_5_6_TEST_BLOCK_EN] = TOMTOM_A_TX_5_6_TEST_BLOCK_EN__POR,
+ [TOMTOM_A_TX_5_6_TXFE_CKDIV] = TOMTOM_A_TX_5_6_TXFE_CKDIV__POR,
+ [TOMTOM_A_TX_5_6_SAR_ERR_CH5] = TOMTOM_A_TX_5_6_SAR_ERR_CH5__POR,
+ [TOMTOM_A_TX_5_6_SAR_ERR_CH6] = TOMTOM_A_TX_5_6_SAR_ERR_CH6__POR,
+ [TOMTOM_A_TX_7_MBHC_EN] = TOMTOM_A_TX_7_MBHC_EN__POR,
+ [TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL] =
+ TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL__POR,
+ [TOMTOM_A_TX_7_MBHC_ADC] = TOMTOM_A_TX_7_MBHC_ADC__POR,
+ [TOMTOM_A_TX_7_MBHC_TEST_CTL] = TOMTOM_A_TX_7_MBHC_TEST_CTL__POR,
+ [TOMTOM_A_TX_7_MBHC_SAR_ERR] = TOMTOM_A_TX_7_MBHC_SAR_ERR__POR,
+ [TOMTOM_A_TX_7_TXFE_CLKDIV] = TOMTOM_A_TX_7_TXFE_CLKDIV__POR,
+ [TOMTOM_A_RCO_CTRL] = TOMTOM_A_RCO_CTRL__POR,
+ [TOMTOM_A_RCO_CALIBRATION_CTRL1] = TOMTOM_A_RCO_CALIBRATION_CTRL1__POR,
+ [TOMTOM_A_RCO_CALIBRATION_CTRL2] = TOMTOM_A_RCO_CALIBRATION_CTRL2__POR,
+ [TOMTOM_A_RCO_CALIBRATION_CTRL3] = TOMTOM_A_RCO_CALIBRATION_CTRL3__POR,
+ [TOMTOM_A_RCO_TEST_CTRL] = TOMTOM_A_RCO_TEST_CTRL__POR,
+ [TOMTOM_A_RCO_CALIBRATION_RESULT1] =
+ TOMTOM_A_RCO_CALIBRATION_RESULT1__POR,
+ [TOMTOM_A_RCO_CALIBRATION_RESULT2] =
+ TOMTOM_A_RCO_CALIBRATION_RESULT2__POR,
+ [TOMTOM_A_BUCK_MODE_1] = TOMTOM_A_BUCK_MODE_1__POR,
+ [TOMTOM_A_BUCK_MODE_2] = TOMTOM_A_BUCK_MODE_2__POR,
+ [TOMTOM_A_BUCK_MODE_3] = TOMTOM_A_BUCK_MODE_3__POR,
+ [TOMTOM_A_BUCK_MODE_4] = TOMTOM_A_BUCK_MODE_4__POR,
+ [TOMTOM_A_BUCK_MODE_5] = TOMTOM_A_BUCK_MODE_5__POR,
+ [TOMTOM_A_BUCK_CTRL_VCL_1] = TOMTOM_A_BUCK_CTRL_VCL_1__POR,
+ [TOMTOM_A_BUCK_CTRL_VCL_2] = TOMTOM_A_BUCK_CTRL_VCL_2__POR,
+ [TOMTOM_A_BUCK_CTRL_VCL_3] = TOMTOM_A_BUCK_CTRL_VCL_3__POR,
+ [TOMTOM_A_BUCK_CTRL_CCL_1] = TOMTOM_A_BUCK_CTRL_CCL_1__POR,
+ [TOMTOM_A_BUCK_CTRL_CCL_2] = TOMTOM_A_BUCK_CTRL_CCL_2__POR,
+ [TOMTOM_A_BUCK_CTRL_CCL_3] = TOMTOM_A_BUCK_CTRL_CCL_3__POR,
+ [TOMTOM_A_BUCK_CTRL_CCL_4] = TOMTOM_A_BUCK_CTRL_CCL_4__POR,
+ [TOMTOM_A_BUCK_CTRL_PWM_DRVR_1] = TOMTOM_A_BUCK_CTRL_PWM_DRVR_1__POR,
+ [TOMTOM_A_BUCK_CTRL_PWM_DRVR_2] = TOMTOM_A_BUCK_CTRL_PWM_DRVR_2__POR,
+ [TOMTOM_A_BUCK_CTRL_PWM_DRVR_3] = TOMTOM_A_BUCK_CTRL_PWM_DRVR_3__POR,
+ [TOMTOM_A_BUCK_TMUX_A_D] = TOMTOM_A_BUCK_TMUX_A_D__POR,
+ [TOMTOM_A_NCP_BUCKREF] = TOMTOM_A_NCP_BUCKREF__POR,
+ [TOMTOM_A_NCP_EN] = TOMTOM_A_NCP_EN__POR,
+ [TOMTOM_A_NCP_CLK] = TOMTOM_A_NCP_CLK__POR,
+ [TOMTOM_A_NCP_STATIC] = TOMTOM_A_NCP_STATIC__POR,
+ [TOMTOM_A_NCP_VTH_LOW] = TOMTOM_A_NCP_VTH_LOW__POR,
+ [TOMTOM_A_NCP_VTH_HIGH] = TOMTOM_A_NCP_VTH_HIGH__POR,
+ [TOMTOM_A_NCP_ATEST] = TOMTOM_A_NCP_ATEST__POR,
+ [TOMTOM_A_NCP_DTEST] = TOMTOM_A_NCP_DTEST__POR,
+ [TOMTOM_A_NCP_DLY1] = TOMTOM_A_NCP_DLY1__POR,
+ [TOMTOM_A_NCP_DLY2] = TOMTOM_A_NCP_DLY2__POR,
+ [TOMTOM_A_RX_AUX_SW_CTL] = TOMTOM_A_RX_AUX_SW_CTL__POR,
+ [TOMTOM_A_RX_PA_AUX_IN_CONN] = TOMTOM_A_RX_PA_AUX_IN_CONN__POR,
+ [TOMTOM_A_RX_COM_TIMER_DIV] = TOMTOM_A_RX_COM_TIMER_DIV__POR,
+ [TOMTOM_A_RX_COM_OCP_CTL] = TOMTOM_A_RX_COM_OCP_CTL__POR,
+ [TOMTOM_A_RX_COM_OCP_COUNT] = TOMTOM_A_RX_COM_OCP_COUNT__POR,
+ [TOMTOM_A_RX_COM_DAC_CTL] = TOMTOM_A_RX_COM_DAC_CTL__POR,
+ [TOMTOM_A_RX_COM_BIAS] = TOMTOM_A_RX_COM_BIAS__POR,
+ [TOMTOM_A_RX_HPH_AUTO_CHOP] = TOMTOM_A_RX_HPH_AUTO_CHOP__POR,
+ [TOMTOM_A_RX_HPH_CHOP_CTL] = TOMTOM_A_RX_HPH_CHOP_CTL__POR,
+ [TOMTOM_A_RX_HPH_BIAS_PA] = TOMTOM_A_RX_HPH_BIAS_PA__POR,
+ [TOMTOM_A_RX_HPH_BIAS_LDO] = TOMTOM_A_RX_HPH_BIAS_LDO__POR,
+ [TOMTOM_A_RX_HPH_BIAS_CNP] = TOMTOM_A_RX_HPH_BIAS_CNP__POR,
+ [TOMTOM_A_RX_HPH_BIAS_WG_OCP] = TOMTOM_A_RX_HPH_BIAS_WG_OCP__POR,
+ [TOMTOM_A_RX_HPH_OCP_CTL] = TOMTOM_A_RX_HPH_OCP_CTL__POR,
+ [TOMTOM_A_RX_HPH_CNP_EN] = TOMTOM_A_RX_HPH_CNP_EN__POR,
+ [TOMTOM_A_RX_HPH_CNP_WG_CTL] = TOMTOM_A_RX_HPH_CNP_WG_CTL__POR,
+ [TOMTOM_A_RX_HPH_CNP_WG_TIME] = TOMTOM_A_RX_HPH_CNP_WG_TIME__POR,
+ [TOMTOM_A_RX_HPH_L_GAIN] = TOMTOM_A_RX_HPH_L_GAIN__POR,
+ [TOMTOM_A_RX_HPH_L_TEST] = TOMTOM_A_RX_HPH_L_TEST__POR,
+ [TOMTOM_A_RX_HPH_L_PA_CTL] = TOMTOM_A_RX_HPH_L_PA_CTL__POR,
+ [TOMTOM_A_RX_HPH_L_DAC_CTL] = TOMTOM_A_RX_HPH_L_DAC_CTL__POR,
+ [TOMTOM_A_RX_HPH_L_ATEST] = TOMTOM_A_RX_HPH_L_ATEST__POR,
+ [TOMTOM_A_RX_HPH_L_STATUS] = TOMTOM_A_RX_HPH_L_STATUS__POR,
+ [TOMTOM_A_RX_HPH_R_GAIN] = TOMTOM_A_RX_HPH_R_GAIN__POR,
+ [TOMTOM_A_RX_HPH_R_TEST] = TOMTOM_A_RX_HPH_R_TEST__POR,
+ [TOMTOM_A_RX_HPH_R_PA_CTL] = TOMTOM_A_RX_HPH_R_PA_CTL__POR,
+ [TOMTOM_A_RX_HPH_R_DAC_CTL] = TOMTOM_A_RX_HPH_R_DAC_CTL__POR,
+ [TOMTOM_A_RX_HPH_R_ATEST] = TOMTOM_A_RX_HPH_R_ATEST__POR,
+ [TOMTOM_A_RX_HPH_R_STATUS] = TOMTOM_A_RX_HPH_R_STATUS__POR,
+ [TOMTOM_A_RX_EAR_BIAS_PA] = TOMTOM_A_RX_EAR_BIAS_PA__POR,
+ [TOMTOM_A_RX_EAR_BIAS_CMBUFF] = TOMTOM_A_RX_EAR_BIAS_CMBUFF__POR,
+ [TOMTOM_A_RX_EAR_EN] = TOMTOM_A_RX_EAR_EN__POR,
+ [TOMTOM_A_RX_EAR_GAIN] = TOMTOM_A_RX_EAR_GAIN__POR,
+ [TOMTOM_A_RX_EAR_CMBUFF] = TOMTOM_A_RX_EAR_CMBUFF__POR,
+ [TOMTOM_A_RX_EAR_ICTL] = TOMTOM_A_RX_EAR_ICTL__POR,
+ [TOMTOM_A_RX_EAR_CCOMP] = TOMTOM_A_RX_EAR_CCOMP__POR,
+ [TOMTOM_A_RX_EAR_VCM] = TOMTOM_A_RX_EAR_VCM__POR,
+ [TOMTOM_A_RX_EAR_CNP] = TOMTOM_A_RX_EAR_CNP__POR,
+ [TOMTOM_A_RX_EAR_DAC_CTL_ATEST] = TOMTOM_A_RX_EAR_DAC_CTL_ATEST__POR,
+ [TOMTOM_A_RX_EAR_STATUS] = TOMTOM_A_RX_EAR_STATUS__POR,
+ [TOMTOM_A_RX_LINE_BIAS_PA] = TOMTOM_A_RX_LINE_BIAS_PA__POR,
+ [TOMTOM_A_RX_BUCK_BIAS1] = TOMTOM_A_RX_BUCK_BIAS1__POR,
+ [TOMTOM_A_RX_BUCK_BIAS2] = TOMTOM_A_RX_BUCK_BIAS2__POR,
+ [TOMTOM_A_RX_LINE_COM] = TOMTOM_A_RX_LINE_COM__POR,
+ [TOMTOM_A_RX_LINE_CNP_EN] = TOMTOM_A_RX_LINE_CNP_EN__POR,
+ [TOMTOM_A_RX_LINE_CNP_WG_CTL] = TOMTOM_A_RX_LINE_CNP_WG_CTL__POR,
+ [TOMTOM_A_RX_LINE_CNP_WG_TIME] = TOMTOM_A_RX_LINE_CNP_WG_TIME__POR,
+ [TOMTOM_A_RX_LINE_1_GAIN] = TOMTOM_A_RX_LINE_1_GAIN__POR,
+ [TOMTOM_A_RX_LINE_1_TEST] = TOMTOM_A_RX_LINE_1_TEST__POR,
+ [TOMTOM_A_RX_LINE_1_DAC_CTL] = TOMTOM_A_RX_LINE_1_DAC_CTL__POR,
+ [TOMTOM_A_RX_LINE_1_STATUS] = TOMTOM_A_RX_LINE_1_STATUS__POR,
+ [TOMTOM_A_RX_LINE_2_GAIN] = TOMTOM_A_RX_LINE_2_GAIN__POR,
+ [TOMTOM_A_RX_LINE_2_TEST] = TOMTOM_A_RX_LINE_2_TEST__POR,
+ [TOMTOM_A_RX_LINE_2_DAC_CTL] = TOMTOM_A_RX_LINE_2_DAC_CTL__POR,
+ [TOMTOM_A_RX_LINE_2_STATUS] = TOMTOM_A_RX_LINE_2_STATUS__POR,
+ [TOMTOM_A_RX_LINE_3_GAIN] = TOMTOM_A_RX_LINE_3_GAIN__POR,
+ [TOMTOM_A_RX_LINE_3_TEST] = TOMTOM_A_RX_LINE_3_TEST__POR,
+ [TOMTOM_A_RX_LINE_3_DAC_CTL] = TOMTOM_A_RX_LINE_3_DAC_CTL__POR,
+ [TOMTOM_A_RX_LINE_3_STATUS] = TOMTOM_A_RX_LINE_3_STATUS__POR,
+ [TOMTOM_A_RX_LINE_4_GAIN] = TOMTOM_A_RX_LINE_4_GAIN__POR,
+ [TOMTOM_A_RX_LINE_4_TEST] = TOMTOM_A_RX_LINE_4_TEST__POR,
+ [TOMTOM_A_RX_LINE_4_DAC_CTL] = TOMTOM_A_RX_LINE_4_DAC_CTL__POR,
+ [TOMTOM_A_RX_LINE_4_STATUS] = TOMTOM_A_RX_LINE_4_STATUS__POR,
+ [TOMTOM_A_RX_LINE_CNP_DBG] = TOMTOM_A_RX_LINE_CNP_DBG__POR,
+ [TOMTOM_A_SPKR_DRV1_EN] = TOMTOM_A_SPKR_DRV1_EN__POR,
+ [TOMTOM_A_SPKR_DRV1_GAIN] = TOMTOM_A_SPKR_DRV1_GAIN__POR,
+ [TOMTOM_A_SPKR_DRV1_DAC_CTL] = TOMTOM_A_SPKR_DRV1_DAC_CTL__POR,
+ [TOMTOM_A_SPKR_DRV1_OCP_CTL] = TOMTOM_A_SPKR_DRV1_OCP_CTL__POR,
+ [TOMTOM_A_SPKR_DRV1_CLIP_DET] = TOMTOM_A_SPKR_DRV1_CLIP_DET__POR,
+ [TOMTOM_A_SPKR_DRV1_IEC] = TOMTOM_A_SPKR_DRV1_IEC__POR,
+ [TOMTOM_A_SPKR_DRV1_DBG_DAC] = TOMTOM_A_SPKR_DRV1_DBG_DAC__POR,
+ [TOMTOM_A_SPKR_DRV1_DBG_PA] = TOMTOM_A_SPKR_DRV1_DBG_PA__POR,
+ [TOMTOM_A_SPKR_DRV1_DBG_PWRSTG] = TOMTOM_A_SPKR_DRV1_DBG_PWRSTG__POR,
+ [TOMTOM_A_SPKR_DRV1_BIAS_LDO] = TOMTOM_A_SPKR_DRV1_BIAS_LDO__POR,
+ [TOMTOM_A_SPKR_DRV1_BIAS_INT] = TOMTOM_A_SPKR_DRV1_BIAS_INT__POR,
+ [TOMTOM_A_SPKR_DRV1_BIAS_PA] = TOMTOM_A_SPKR_DRV1_BIAS_PA__POR,
+ [TOMTOM_A_SPKR_DRV1_STATUS_OCP] = TOMTOM_A_SPKR_DRV1_STATUS_OCP__POR,
+ [TOMTOM_A_SPKR_DRV1_STATUS_PA] = TOMTOM_A_SPKR_DRV1_STATUS_PA__POR,
+ [TOMTOM_A_SPKR1_PROT_EN] = TOMTOM_A_SPKR1_PROT_EN__POR,
+ [TOMTOM_A_SPKR1_PROT_ADC_TEST_EN] =
+ TOMTOM_A_SPKR1_PROT_ADC_TEST_EN__POR,
+ [TOMTOM_A_SPKR1_PROT_ATEST] = TOMTOM_A_SPKR1_PROT_ATEST__POR,
+ [TOMTOM_A_SPKR1_PROT_LDO_CTRL] = TOMTOM_A_SPKR1_PROT_LDO_CTRL__POR,
+ [TOMTOM_A_SPKR1_PROT_ISENSE_CTRL] =
+ TOMTOM_A_SPKR1_PROT_ISENSE_CTRL__POR,
+ [TOMTOM_A_SPKR1_PROT_VSENSE_CTRL] =
+ TOMTOM_A_SPKR1_PROT_VSENSE_CTRL__POR,
+ [TOMTOM_A_SPKR2_PROT_EN] = TOMTOM_A_SPKR2_PROT_EN__POR,
+ [TOMTOM_A_SPKR2_PROT_ADC_TEST_EN] =
+ TOMTOM_A_SPKR2_PROT_ADC_TEST_EN__POR,
+ [TOMTOM_A_SPKR2_PROT_ATEST] = TOMTOM_A_SPKR2_PROT_ATEST__POR,
+ [TOMTOM_A_SPKR2_PROT_LDO_CTRL] = TOMTOM_A_SPKR2_PROT_LDO_CTRL__POR,
+ [TOMTOM_A_SPKR2_PROT_ISENSE_CTRL] =
+ TOMTOM_A_SPKR2_PROT_ISENSE_CTRL__POR,
+ [TOMTOM_A_SPKR2_PROT_VSENSE_CTRL] =
+ TOMTOM_A_SPKR2_PROT_VSENSE_CTRL__POR,
+ [TOMTOM_A_MBHC_HPH] = TOMTOM_A_MBHC_HPH__POR,
+ [TOMTOM_A_CDC_ANC1_B1_CTL] = TOMTOM_A_CDC_ANC1_B1_CTL__POR,
+ [TOMTOM_A_CDC_ANC2_B1_CTL] = TOMTOM_A_CDC_ANC2_B1_CTL__POR,
+ [TOMTOM_A_CDC_ANC1_SHIFT] = TOMTOM_A_CDC_ANC1_SHIFT__POR,
+ [TOMTOM_A_CDC_ANC2_SHIFT] = TOMTOM_A_CDC_ANC2_SHIFT__POR,
+ [TOMTOM_A_CDC_ANC1_IIR_B1_CTL] = TOMTOM_A_CDC_ANC1_IIR_B1_CTL__POR,
+ [TOMTOM_A_CDC_ANC2_IIR_B1_CTL] = TOMTOM_A_CDC_ANC2_IIR_B1_CTL__POR,
+ [TOMTOM_A_CDC_ANC1_IIR_B2_CTL] = TOMTOM_A_CDC_ANC1_IIR_B2_CTL__POR,
+ [TOMTOM_A_CDC_ANC2_IIR_B2_CTL] = TOMTOM_A_CDC_ANC2_IIR_B2_CTL__POR,
+ [TOMTOM_A_CDC_ANC1_IIR_B3_CTL] = TOMTOM_A_CDC_ANC1_IIR_B3_CTL__POR,
+ [TOMTOM_A_CDC_ANC2_IIR_B3_CTL] = TOMTOM_A_CDC_ANC2_IIR_B3_CTL__POR,
+ [TOMTOM_A_CDC_ANC1_LPF_B1_CTL] = TOMTOM_A_CDC_ANC1_LPF_B1_CTL__POR,
+ [TOMTOM_A_CDC_ANC2_LPF_B1_CTL] = TOMTOM_A_CDC_ANC2_LPF_B1_CTL__POR,
+ [TOMTOM_A_CDC_ANC1_LPF_B2_CTL] = TOMTOM_A_CDC_ANC1_LPF_B2_CTL__POR,
+ [TOMTOM_A_CDC_ANC2_LPF_B2_CTL] = TOMTOM_A_CDC_ANC2_LPF_B2_CTL__POR,
+ [TOMTOM_A_CDC_ANC1_SPARE] = TOMTOM_A_CDC_ANC1_SPARE__POR,
+ [TOMTOM_A_CDC_ANC2_SPARE] = TOMTOM_A_CDC_ANC2_SPARE__POR,
+ [TOMTOM_A_CDC_ANC1_SMLPF_CTL] = TOMTOM_A_CDC_ANC1_SMLPF_CTL__POR,
+ [TOMTOM_A_CDC_ANC2_SMLPF_CTL] = TOMTOM_A_CDC_ANC2_SMLPF_CTL__POR,
+ [TOMTOM_A_CDC_ANC1_DCFLT_CTL] = TOMTOM_A_CDC_ANC1_DCFLT_CTL__POR,
+ [TOMTOM_A_CDC_ANC2_DCFLT_CTL] = TOMTOM_A_CDC_ANC2_DCFLT_CTL__POR,
+ [TOMTOM_A_CDC_ANC1_GAIN_CTL] = TOMTOM_A_CDC_ANC1_GAIN_CTL__POR,
+ [TOMTOM_A_CDC_ANC2_GAIN_CTL] = TOMTOM_A_CDC_ANC2_GAIN_CTL__POR,
+ [TOMTOM_A_CDC_ANC1_B2_CTL] = TOMTOM_A_CDC_ANC1_B2_CTL__POR,
+ [TOMTOM_A_CDC_ANC2_B2_CTL] = TOMTOM_A_CDC_ANC2_B2_CTL__POR,
+ [TOMTOM_A_CDC_TX1_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX1_VOL_CTL_TIMER__POR,
+ [TOMTOM_A_CDC_TX2_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX2_VOL_CTL_TIMER__POR,
+ [TOMTOM_A_CDC_TX3_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX3_VOL_CTL_TIMER__POR,
+ [TOMTOM_A_CDC_TX4_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX4_VOL_CTL_TIMER__POR,
+ [TOMTOM_A_CDC_TX5_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX5_VOL_CTL_TIMER__POR,
+ [TOMTOM_A_CDC_TX6_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX6_VOL_CTL_TIMER__POR,
+ [TOMTOM_A_CDC_TX7_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX7_VOL_CTL_TIMER__POR,
+ [TOMTOM_A_CDC_TX8_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX8_VOL_CTL_TIMER__POR,
+ [TOMTOM_A_CDC_TX9_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX9_VOL_CTL_TIMER__POR,
+ [TOMTOM_A_CDC_TX10_VOL_CTL_TIMER] =
+ TOMTOM_A_CDC_TX10_VOL_CTL_TIMER__POR,
+ [TOMTOM_A_CDC_TX1_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX1_VOL_CTL_GAIN__POR,
+ [TOMTOM_A_CDC_TX2_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX2_VOL_CTL_GAIN__POR,
+ [TOMTOM_A_CDC_TX3_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX3_VOL_CTL_GAIN__POR,
+ [TOMTOM_A_CDC_TX4_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX4_VOL_CTL_GAIN__POR,
+ [TOMTOM_A_CDC_TX5_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX5_VOL_CTL_GAIN__POR,
+ [TOMTOM_A_CDC_TX6_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX6_VOL_CTL_GAIN__POR,
+ [TOMTOM_A_CDC_TX7_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX7_VOL_CTL_GAIN__POR,
+ [TOMTOM_A_CDC_TX8_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX8_VOL_CTL_GAIN__POR,
+ [TOMTOM_A_CDC_TX9_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX9_VOL_CTL_GAIN__POR,
+ [TOMTOM_A_CDC_TX10_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX10_VOL_CTL_GAIN__POR,
+ [TOMTOM_A_CDC_TX1_VOL_CTL_CFG] = TOMTOM_A_CDC_TX1_VOL_CTL_CFG__POR,
+ [TOMTOM_A_CDC_TX2_VOL_CTL_CFG] = TOMTOM_A_CDC_TX2_VOL_CTL_CFG__POR,
+ [TOMTOM_A_CDC_TX3_VOL_CTL_CFG] = TOMTOM_A_CDC_TX3_VOL_CTL_CFG__POR,
+ [TOMTOM_A_CDC_TX4_VOL_CTL_CFG] = TOMTOM_A_CDC_TX4_VOL_CTL_CFG__POR,
+ [TOMTOM_A_CDC_TX5_VOL_CTL_CFG] = TOMTOM_A_CDC_TX5_VOL_CTL_CFG__POR,
+ [TOMTOM_A_CDC_TX6_VOL_CTL_CFG] = TOMTOM_A_CDC_TX6_VOL_CTL_CFG__POR,
+ [TOMTOM_A_CDC_TX7_VOL_CTL_CFG] = TOMTOM_A_CDC_TX7_VOL_CTL_CFG__POR,
+ [TOMTOM_A_CDC_TX8_VOL_CTL_CFG] = TOMTOM_A_CDC_TX8_VOL_CTL_CFG__POR,
+ [TOMTOM_A_CDC_TX9_VOL_CTL_CFG] = TOMTOM_A_CDC_TX9_VOL_CTL_CFG__POR,
+ [TOMTOM_A_CDC_TX10_VOL_CTL_CFG] = TOMTOM_A_CDC_TX10_VOL_CTL_CFG__POR,
+ [TOMTOM_A_CDC_TX1_MUX_CTL] = TOMTOM_A_CDC_TX1_MUX_CTL__POR,
+ [TOMTOM_A_CDC_TX2_MUX_CTL] = TOMTOM_A_CDC_TX2_MUX_CTL__POR,
+ [TOMTOM_A_CDC_TX3_MUX_CTL] = TOMTOM_A_CDC_TX3_MUX_CTL__POR,
+ [TOMTOM_A_CDC_TX4_MUX_CTL] = TOMTOM_A_CDC_TX4_MUX_CTL__POR,
+ [TOMTOM_A_CDC_TX5_MUX_CTL] = TOMTOM_A_CDC_TX5_MUX_CTL__POR,
+ [TOMTOM_A_CDC_TX6_MUX_CTL] = TOMTOM_A_CDC_TX6_MUX_CTL__POR,
+ [TOMTOM_A_CDC_TX7_MUX_CTL] = TOMTOM_A_CDC_TX7_MUX_CTL__POR,
+ [TOMTOM_A_CDC_TX8_MUX_CTL] = TOMTOM_A_CDC_TX8_MUX_CTL__POR,
+ [TOMTOM_A_CDC_TX9_MUX_CTL] = TOMTOM_A_CDC_TX9_MUX_CTL__POR,
+ [TOMTOM_A_CDC_TX10_MUX_CTL] = TOMTOM_A_CDC_TX10_MUX_CTL__POR,
+ [TOMTOM_A_CDC_TX1_CLK_FS_CTL] = TOMTOM_A_CDC_TX1_CLK_FS_CTL__POR,
+ [TOMTOM_A_CDC_TX2_CLK_FS_CTL] = TOMTOM_A_CDC_TX2_CLK_FS_CTL__POR,
+ [TOMTOM_A_CDC_TX3_CLK_FS_CTL] = TOMTOM_A_CDC_TX3_CLK_FS_CTL__POR,
+ [TOMTOM_A_CDC_TX4_CLK_FS_CTL] = TOMTOM_A_CDC_TX4_CLK_FS_CTL__POR,
+ [TOMTOM_A_CDC_TX5_CLK_FS_CTL] = TOMTOM_A_CDC_TX5_CLK_FS_CTL__POR,
+ [TOMTOM_A_CDC_TX6_CLK_FS_CTL] = TOMTOM_A_CDC_TX6_CLK_FS_CTL__POR,
+ [TOMTOM_A_CDC_TX7_CLK_FS_CTL] = TOMTOM_A_CDC_TX7_CLK_FS_CTL__POR,
+ [TOMTOM_A_CDC_TX8_CLK_FS_CTL] = TOMTOM_A_CDC_TX8_CLK_FS_CTL__POR,
+ [TOMTOM_A_CDC_TX9_CLK_FS_CTL] = TOMTOM_A_CDC_TX9_CLK_FS_CTL__POR,
+ [TOMTOM_A_CDC_TX10_CLK_FS_CTL] = TOMTOM_A_CDC_TX10_CLK_FS_CTL__POR,
+ [TOMTOM_A_CDC_TX1_DMIC_CTL] = TOMTOM_A_CDC_TX1_DMIC_CTL__POR,
+ [TOMTOM_A_CDC_TX2_DMIC_CTL] = TOMTOM_A_CDC_TX2_DMIC_CTL__POR,
+ [TOMTOM_A_CDC_TX3_DMIC_CTL] = TOMTOM_A_CDC_TX3_DMIC_CTL__POR,
+ [TOMTOM_A_CDC_TX4_DMIC_CTL] = TOMTOM_A_CDC_TX4_DMIC_CTL__POR,
+ [TOMTOM_A_CDC_TX5_DMIC_CTL] = TOMTOM_A_CDC_TX5_DMIC_CTL__POR,
+ [TOMTOM_A_CDC_TX6_DMIC_CTL] = TOMTOM_A_CDC_TX6_DMIC_CTL__POR,
+ [TOMTOM_A_CDC_TX7_DMIC_CTL] = TOMTOM_A_CDC_TX7_DMIC_CTL__POR,
+ [TOMTOM_A_CDC_TX8_DMIC_CTL] = TOMTOM_A_CDC_TX8_DMIC_CTL__POR,
+ [TOMTOM_A_CDC_TX9_DMIC_CTL] = TOMTOM_A_CDC_TX9_DMIC_CTL__POR,
+ [TOMTOM_A_CDC_TX10_DMIC_CTL] = TOMTOM_A_CDC_TX10_DMIC_CTL__POR,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL0] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL0__POR,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL1] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL1__POR,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL2] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL2__POR,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL3] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL3__POR,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL4] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL4__POR,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL5] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL5__POR,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL6] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL6__POR,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_VAL7] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL7__POR,
+ [TOMTOM_A_CDC_DEBUG_B1_CTL] = TOMTOM_A_CDC_DEBUG_B1_CTL__POR,
+ [TOMTOM_A_CDC_DEBUG_B2_CTL] = TOMTOM_A_CDC_DEBUG_B2_CTL__POR,
+ [TOMTOM_A_CDC_DEBUG_B3_CTL] = TOMTOM_A_CDC_DEBUG_B3_CTL__POR,
+ [TOMTOM_A_CDC_DEBUG_B4_CTL] = TOMTOM_A_CDC_DEBUG_B4_CTL__POR,
+ [TOMTOM_A_CDC_DEBUG_B5_CTL] = TOMTOM_A_CDC_DEBUG_B5_CTL__POR,
+ [TOMTOM_A_CDC_DEBUG_B6_CTL] = TOMTOM_A_CDC_DEBUG_B6_CTL__POR,
+ [TOMTOM_A_CDC_DEBUG_B7_CTL] = TOMTOM_A_CDC_DEBUG_B7_CTL__POR,
+ [TOMTOM_A_CDC_SRC1_PDA_CFG] = TOMTOM_A_CDC_SRC1_PDA_CFG__POR,
+ [TOMTOM_A_CDC_SRC2_PDA_CFG] = TOMTOM_A_CDC_SRC2_PDA_CFG__POR,
+ [TOMTOM_A_CDC_SRC1_FS_CTL] = TOMTOM_A_CDC_SRC1_FS_CTL__POR,
+ [TOMTOM_A_CDC_SRC2_FS_CTL] = TOMTOM_A_CDC_SRC2_FS_CTL__POR,
+ [TOMTOM_A_CDC_RX1_B1_CTL] = TOMTOM_A_CDC_RX1_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX2_B1_CTL] = TOMTOM_A_CDC_RX2_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX3_B1_CTL] = TOMTOM_A_CDC_RX3_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX4_B1_CTL] = TOMTOM_A_CDC_RX4_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX5_B1_CTL] = TOMTOM_A_CDC_RX5_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX6_B1_CTL] = TOMTOM_A_CDC_RX6_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX7_B1_CTL] = TOMTOM_A_CDC_RX7_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX1_B2_CTL] = TOMTOM_A_CDC_RX1_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX2_B2_CTL] = TOMTOM_A_CDC_RX2_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX3_B2_CTL] = TOMTOM_A_CDC_RX3_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX4_B2_CTL] = TOMTOM_A_CDC_RX4_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX5_B2_CTL] = TOMTOM_A_CDC_RX5_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX6_B2_CTL] = TOMTOM_A_CDC_RX6_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX7_B2_CTL] = TOMTOM_A_CDC_RX7_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX1_B3_CTL] = TOMTOM_A_CDC_RX1_B3_CTL__POR,
+ [TOMTOM_A_CDC_RX2_B3_CTL] = TOMTOM_A_CDC_RX2_B3_CTL__POR,
+ [TOMTOM_A_CDC_RX3_B3_CTL] = TOMTOM_A_CDC_RX3_B3_CTL__POR,
+ [TOMTOM_A_CDC_RX4_B3_CTL] = TOMTOM_A_CDC_RX4_B3_CTL__POR,
+ [TOMTOM_A_CDC_RX5_B3_CTL] = TOMTOM_A_CDC_RX5_B3_CTL__POR,
+ [TOMTOM_A_CDC_RX6_B3_CTL] = TOMTOM_A_CDC_RX6_B3_CTL__POR,
+ [TOMTOM_A_CDC_RX7_B3_CTL] = TOMTOM_A_CDC_RX7_B3_CTL__POR,
+ [TOMTOM_A_CDC_RX1_B4_CTL] = TOMTOM_A_CDC_RX1_B4_CTL__POR,
+ [TOMTOM_A_CDC_RX2_B4_CTL] = TOMTOM_A_CDC_RX2_B4_CTL__POR,
+ [TOMTOM_A_CDC_RX3_B4_CTL] = TOMTOM_A_CDC_RX3_B4_CTL__POR,
+ [TOMTOM_A_CDC_RX4_B4_CTL] = TOMTOM_A_CDC_RX4_B4_CTL__POR,
+ [TOMTOM_A_CDC_RX5_B4_CTL] = TOMTOM_A_CDC_RX5_B4_CTL__POR,
+ [TOMTOM_A_CDC_RX6_B4_CTL] = TOMTOM_A_CDC_RX6_B4_CTL__POR,
+ [TOMTOM_A_CDC_RX7_B4_CTL] = TOMTOM_A_CDC_RX7_B4_CTL__POR,
+ [TOMTOM_A_CDC_RX1_B5_CTL] = TOMTOM_A_CDC_RX1_B5_CTL__POR,
+ [TOMTOM_A_CDC_RX2_B5_CTL] = TOMTOM_A_CDC_RX2_B5_CTL__POR,
+ [TOMTOM_A_CDC_RX3_B5_CTL] = TOMTOM_A_CDC_RX3_B5_CTL__POR,
+ [TOMTOM_A_CDC_RX4_B5_CTL] = TOMTOM_A_CDC_RX4_B5_CTL__POR,
+ [TOMTOM_A_CDC_RX5_B5_CTL] = TOMTOM_A_CDC_RX5_B5_CTL__POR,
+ [TOMTOM_A_CDC_RX6_B5_CTL] = TOMTOM_A_CDC_RX6_B5_CTL__POR,
+ [TOMTOM_A_CDC_RX7_B5_CTL] = TOMTOM_A_CDC_RX7_B5_CTL__POR,
+ [TOMTOM_A_CDC_RX1_B6_CTL] = TOMTOM_A_CDC_RX1_B6_CTL__POR,
+ [TOMTOM_A_CDC_RX2_B6_CTL] = TOMTOM_A_CDC_RX2_B6_CTL__POR,
+ [TOMTOM_A_CDC_RX3_B6_CTL] = TOMTOM_A_CDC_RX3_B6_CTL__POR,
+ [TOMTOM_A_CDC_RX4_B6_CTL] = TOMTOM_A_CDC_RX4_B6_CTL__POR,
+ [TOMTOM_A_CDC_RX5_B6_CTL] = TOMTOM_A_CDC_RX5_B6_CTL__POR,
+ [TOMTOM_A_CDC_RX6_B6_CTL] = TOMTOM_A_CDC_RX6_B6_CTL__POR,
+ [TOMTOM_A_CDC_RX7_B6_CTL] = TOMTOM_A_CDC_RX7_B6_CTL__POR,
+ [TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL] =
+ TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL] =
+ TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL] =
+ TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL] =
+ TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL] =
+ TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL] =
+ TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL] =
+ TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL] =
+ TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL] =
+ TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL] =
+ TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL] =
+ TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL] =
+ TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL] =
+ TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL] =
+ TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL__POR,
+ [TOMTOM_A_CDC_VBAT_CFG] = TOMTOM_A_CDC_VBAT_CFG__POR,
+ [TOMTOM_A_CDC_VBAT_ADC_CAL1] = TOMTOM_A_CDC_VBAT_ADC_CAL1__POR,
+ [TOMTOM_A_CDC_VBAT_ADC_CAL2] = TOMTOM_A_CDC_VBAT_ADC_CAL2__POR,
+ [TOMTOM_A_CDC_VBAT_ADC_CAL3] = TOMTOM_A_CDC_VBAT_ADC_CAL3__POR,
+ [TOMTOM_A_CDC_VBAT_PK_EST1] = TOMTOM_A_CDC_VBAT_PK_EST1__POR,
+ [TOMTOM_A_CDC_VBAT_PK_EST2] = TOMTOM_A_CDC_VBAT_PK_EST2__POR,
+ [TOMTOM_A_CDC_VBAT_PK_EST3] = TOMTOM_A_CDC_VBAT_PK_EST3__POR,
+ [TOMTOM_A_CDC_VBAT_RF_PROC1] = TOMTOM_A_CDC_VBAT_RF_PROC1__POR,
+ [TOMTOM_A_CDC_VBAT_RF_PROC2] = TOMTOM_A_CDC_VBAT_RF_PROC2__POR,
+ [TOMTOM_A_CDC_VBAT_TAC1] = TOMTOM_A_CDC_VBAT_TAC1__POR,
+ [TOMTOM_A_CDC_VBAT_TAC2] = TOMTOM_A_CDC_VBAT_TAC2__POR,
+ [TOMTOM_A_CDC_VBAT_TAC3] = TOMTOM_A_CDC_VBAT_TAC3__POR,
+ [TOMTOM_A_CDC_VBAT_TAC4] = TOMTOM_A_CDC_VBAT_TAC4__POR,
+ [TOMTOM_A_CDC_VBAT_GAIN_UPD1] = TOMTOM_A_CDC_VBAT_GAIN_UPD1__POR,
+ [TOMTOM_A_CDC_VBAT_GAIN_UPD2] = TOMTOM_A_CDC_VBAT_GAIN_UPD2__POR,
+ [TOMTOM_A_CDC_VBAT_GAIN_UPD3] = TOMTOM_A_CDC_VBAT_GAIN_UPD3__POR,
+ [TOMTOM_A_CDC_VBAT_GAIN_UPD4] = TOMTOM_A_CDC_VBAT_GAIN_UPD4__POR,
+ [TOMTOM_A_CDC_VBAT_DEBUG1] = TOMTOM_A_CDC_VBAT_DEBUG1__POR,
+ [TOMTOM_A_CDC_VBAT_GAIN_UPD_MON] = TOMTOM_A_CDC_VBAT_GAIN_UPD_MON__POR,
+ [TOMTOM_A_CDC_VBAT_GAIN_MON_VAL] = TOMTOM_A_CDC_VBAT_GAIN_MON_VAL__POR,
+ [TOMTOM_A_CDC_CLK_ANC_RESET_CTL] = TOMTOM_A_CDC_CLK_ANC_RESET_CTL__POR,
+ [TOMTOM_A_CDC_CLK_RX_RESET_CTL] = TOMTOM_A_CDC_CLK_RX_RESET_CTL__POR,
+ [TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL] =
+ TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL__POR,
+ [TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL] =
+ TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL__POR,
+ [TOMTOM_A_CDC_CLK_RX_I2S_CTL] = TOMTOM_A_CDC_CLK_RX_I2S_CTL__POR,
+ [TOMTOM_A_CDC_CLK_TX_I2S_CTL] = TOMTOM_A_CDC_CLK_TX_I2S_CTL__POR,
+ [TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL] =
+ TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL__POR,
+ [TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL] =
+ TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL__POR,
+ [TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL] =
+ TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR,
+ [TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL] =
+ TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL__POR,
+ [TOMTOM_A_CDC_CLK_OTHR_CTL] = TOMTOM_A_CDC_CLK_OTHR_CTL__POR,
+ [TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL] =
+ TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL__POR,
+ [TOMTOM_A_CDC_CLK_RX_B1_CTL] = TOMTOM_A_CDC_CLK_RX_B1_CTL__POR,
+ [TOMTOM_A_CDC_CLK_RX_B2_CTL] = TOMTOM_A_CDC_CLK_RX_B2_CTL__POR,
+ [TOMTOM_A_CDC_CLK_MCLK_CTL] = TOMTOM_A_CDC_CLK_MCLK_CTL__POR,
+ [TOMTOM_A_CDC_CLK_PDM_CTL] = TOMTOM_A_CDC_CLK_PDM_CTL__POR,
+ [TOMTOM_A_CDC_CLK_SD_CTL] = TOMTOM_A_CDC_CLK_SD_CTL__POR,
+ [TOMTOM_A_CDC_CLSH_B1_CTL] = TOMTOM_A_CDC_CLSH_B1_CTL__POR,
+ [TOMTOM_A_CDC_CLSH_B2_CTL] = TOMTOM_A_CDC_CLSH_B2_CTL__POR,
+ [TOMTOM_A_CDC_CLSH_B3_CTL] = TOMTOM_A_CDC_CLSH_B3_CTL__POR,
+ [TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS] =
+ TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS__POR,
+ [TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD] =
+ TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD__POR,
+ [TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD] =
+ TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD__POR,
+ [TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD] =
+ TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR,
+ [TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD] =
+ TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR,
+ [TOMTOM_A_CDC_CLSH_K_ADDR] = TOMTOM_A_CDC_CLSH_K_ADDR__POR,
+ [TOMTOM_A_CDC_CLSH_K_DATA] = TOMTOM_A_CDC_CLSH_K_DATA__POR,
+ [TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L] =
+ TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L__POR,
+ [TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U] =
+ TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U__POR,
+ [TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L] =
+ TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L__POR,
+ [TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U] =
+ TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U__POR,
+ [TOMTOM_A_CDC_CLSH_V_PA_HD_EAR] = TOMTOM_A_CDC_CLSH_V_PA_HD_EAR__POR,
+ [TOMTOM_A_CDC_CLSH_V_PA_HD_HPH] = TOMTOM_A_CDC_CLSH_V_PA_HD_HPH__POR,
+ [TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR] = TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR__POR,
+ [TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH] = TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH__POR,
+ [TOMTOM_A_CDC_IIR1_GAIN_B1_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B1_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_GAIN_B1_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B1_CTL__POR,
+ [TOMTOM_A_CDC_IIR1_GAIN_B2_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B2_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_GAIN_B2_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B2_CTL__POR,
+ [TOMTOM_A_CDC_IIR1_GAIN_B3_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B3_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_GAIN_B3_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B3_CTL__POR,
+ [TOMTOM_A_CDC_IIR1_GAIN_B4_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B4_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_GAIN_B4_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B4_CTL__POR,
+ [TOMTOM_A_CDC_IIR1_GAIN_B5_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B5_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_GAIN_B5_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B5_CTL__POR,
+ [TOMTOM_A_CDC_IIR1_GAIN_B6_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B6_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_GAIN_B6_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B6_CTL__POR,
+ [TOMTOM_A_CDC_IIR1_GAIN_B7_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B7_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_GAIN_B7_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B7_CTL__POR,
+ [TOMTOM_A_CDC_IIR1_GAIN_B8_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B8_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_GAIN_B8_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B8_CTL__POR,
+ [TOMTOM_A_CDC_IIR1_CTL] = TOMTOM_A_CDC_IIR1_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_CTL] = TOMTOM_A_CDC_IIR2_CTL__POR,
+ [TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL] =
+ TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL] =
+ TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL__POR,
+ [TOMTOM_A_CDC_IIR1_COEF_B1_CTL] = TOMTOM_A_CDC_IIR1_COEF_B1_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_COEF_B1_CTL] = TOMTOM_A_CDC_IIR2_COEF_B1_CTL__POR,
+ [TOMTOM_A_CDC_IIR1_COEF_B2_CTL] = TOMTOM_A_CDC_IIR1_COEF_B2_CTL__POR,
+ [TOMTOM_A_CDC_IIR2_COEF_B2_CTL] = TOMTOM_A_CDC_IIR2_COEF_B2_CTL__POR,
+ [TOMTOM_A_CDC_TOP_GAIN_UPDATE] = TOMTOM_A_CDC_TOP_GAIN_UPDATE__POR,
+ [TOMTOM_A_CDC_PA_RAMP_B1_CTL] = TOMTOM_A_CDC_PA_RAMP_B1_CTL__POR,
+ [TOMTOM_A_CDC_PA_RAMP_B2_CTL] = TOMTOM_A_CDC_PA_RAMP_B2_CTL__POR,
+ [TOMTOM_A_CDC_PA_RAMP_B3_CTL] = TOMTOM_A_CDC_PA_RAMP_B3_CTL__POR,
+ [TOMTOM_A_CDC_PA_RAMP_B4_CTL] = TOMTOM_A_CDC_PA_RAMP_B4_CTL__POR,
+ [TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL] =
+ TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL__POR,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL] =
+ TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL__POR,
+ [TOMTOM_A_CDC_COMP0_B1_CTL] = TOMTOM_A_CDC_COMP0_B1_CTL__POR,
+ [TOMTOM_A_CDC_COMP1_B1_CTL] = TOMTOM_A_CDC_COMP1_B1_CTL__POR,
+ [TOMTOM_A_CDC_COMP2_B1_CTL] = TOMTOM_A_CDC_COMP2_B1_CTL__POR,
+ [TOMTOM_A_CDC_COMP0_B2_CTL] = TOMTOM_A_CDC_COMP0_B2_CTL__POR,
+ [TOMTOM_A_CDC_COMP1_B2_CTL] = TOMTOM_A_CDC_COMP1_B2_CTL__POR,
+ [TOMTOM_A_CDC_COMP2_B2_CTL] = TOMTOM_A_CDC_COMP2_B2_CTL__POR,
+ [TOMTOM_A_CDC_COMP0_B3_CTL] = TOMTOM_A_CDC_COMP0_B3_CTL__POR,
+ [TOMTOM_A_CDC_COMP1_B3_CTL] = TOMTOM_A_CDC_COMP1_B3_CTL__POR,
+ [TOMTOM_A_CDC_COMP2_B3_CTL] = TOMTOM_A_CDC_COMP2_B3_CTL__POR,
+ [TOMTOM_A_CDC_COMP0_B4_CTL] = TOMTOM_A_CDC_COMP0_B4_CTL__POR,
+ [TOMTOM_A_CDC_COMP1_B4_CTL] = TOMTOM_A_CDC_COMP1_B4_CTL__POR,
+ [TOMTOM_A_CDC_COMP2_B4_CTL] = TOMTOM_A_CDC_COMP2_B4_CTL__POR,
+ [TOMTOM_A_CDC_COMP0_B5_CTL] = TOMTOM_A_CDC_COMP0_B5_CTL__POR,
+ [TOMTOM_A_CDC_COMP1_B5_CTL] = TOMTOM_A_CDC_COMP1_B5_CTL__POR,
+ [TOMTOM_A_CDC_COMP2_B5_CTL] = TOMTOM_A_CDC_COMP2_B5_CTL__POR,
+ [TOMTOM_A_CDC_COMP0_B6_CTL] = TOMTOM_A_CDC_COMP0_B6_CTL__POR,
+ [TOMTOM_A_CDC_COMP1_B6_CTL] = TOMTOM_A_CDC_COMP1_B6_CTL__POR,
+ [TOMTOM_A_CDC_COMP2_B6_CTL] = TOMTOM_A_CDC_COMP2_B6_CTL__POR,
+ [TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS] =
+ TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS__POR,
+ [TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS] =
+ TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS__POR,
+ [TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS] =
+ TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS__POR,
+ [TOMTOM_A_CDC_COMP0_FS_CFG] = TOMTOM_A_CDC_COMP0_FS_CFG__POR,
+ [TOMTOM_A_CDC_COMP1_FS_CFG] = TOMTOM_A_CDC_COMP1_FS_CFG__POR,
+ [TOMTOM_A_CDC_COMP2_FS_CFG] = TOMTOM_A_CDC_COMP2_FS_CFG__POR,
+ [TOMTOM_A_CDC_CONN_RX1_B1_CTL] = TOMTOM_A_CDC_CONN_RX1_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX1_B2_CTL] = TOMTOM_A_CDC_CONN_RX1_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX1_B3_CTL] = TOMTOM_A_CDC_CONN_RX1_B3_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX2_B1_CTL] = TOMTOM_A_CDC_CONN_RX2_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX2_B2_CTL] = TOMTOM_A_CDC_CONN_RX2_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX2_B3_CTL] = TOMTOM_A_CDC_CONN_RX2_B3_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX3_B1_CTL] = TOMTOM_A_CDC_CONN_RX3_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX3_B2_CTL] = TOMTOM_A_CDC_CONN_RX3_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX4_B1_CTL] = TOMTOM_A_CDC_CONN_RX4_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX4_B2_CTL] = TOMTOM_A_CDC_CONN_RX4_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX5_B1_CTL] = TOMTOM_A_CDC_CONN_RX5_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX5_B2_CTL] = TOMTOM_A_CDC_CONN_RX5_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX6_B1_CTL] = TOMTOM_A_CDC_CONN_RX6_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX6_B2_CTL] = TOMTOM_A_CDC_CONN_RX6_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX7_B1_CTL] = TOMTOM_A_CDC_CONN_RX7_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX7_B2_CTL] = TOMTOM_A_CDC_CONN_RX7_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX7_B3_CTL] = TOMTOM_A_CDC_CONN_RX7_B3_CTL__POR,
+ [TOMTOM_A_CDC_CONN_ANC_B1_CTL] = TOMTOM_A_CDC_CONN_ANC_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_ANC_B2_CTL] = TOMTOM_A_CDC_CONN_ANC_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_B1_CTL] = TOMTOM_A_CDC_CONN_TX_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_B2_CTL] = TOMTOM_A_CDC_CONN_TX_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_B3_CTL] = TOMTOM_A_CDC_CONN_TX_B3_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_B4_CTL] = TOMTOM_A_CDC_CONN_TX_B4_CTL__POR,
+ [TOMTOM_A_CDC_CONN_EQ1_B1_CTL] = TOMTOM_A_CDC_CONN_EQ1_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_EQ1_B2_CTL] = TOMTOM_A_CDC_CONN_EQ1_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_EQ1_B3_CTL] = TOMTOM_A_CDC_CONN_EQ1_B3_CTL__POR,
+ [TOMTOM_A_CDC_CONN_EQ1_B4_CTL] = TOMTOM_A_CDC_CONN_EQ1_B4_CTL__POR,
+ [TOMTOM_A_CDC_CONN_EQ2_B1_CTL] = TOMTOM_A_CDC_CONN_EQ2_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_EQ2_B2_CTL] = TOMTOM_A_CDC_CONN_EQ2_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_EQ2_B3_CTL] = TOMTOM_A_CDC_CONN_EQ2_B3_CTL__POR,
+ [TOMTOM_A_CDC_CONN_EQ2_B4_CTL] = TOMTOM_A_CDC_CONN_EQ2_B4_CTL__POR,
+ [TOMTOM_A_CDC_CONN_SRC1_B1_CTL] = TOMTOM_A_CDC_CONN_SRC1_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_SRC1_B2_CTL] = TOMTOM_A_CDC_CONN_SRC1_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_SRC2_B1_CTL] = TOMTOM_A_CDC_CONN_SRC2_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_SRC2_B2_CTL] = TOMTOM_A_CDC_CONN_SRC2_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_SB_B1_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_SB_B2_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_SB_B3_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B3_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_SB_B4_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B4_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_SB_B5_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B5_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_SB_B6_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B6_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_SB_B7_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B7_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_SB_B8_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B8_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_SB_B9_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B9_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_SB_B10_CTL] =
+ TOMTOM_A_CDC_CONN_TX_SB_B10_CTL__POR,
+ [TOMTOM_A_CDC_CONN_TX_SB_B11_CTL] =
+ TOMTOM_A_CDC_CONN_TX_SB_B11_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX_SB_B1_CTL] = TOMTOM_A_CDC_CONN_RX_SB_B1_CTL__POR,
+ [TOMTOM_A_CDC_CONN_RX_SB_B2_CTL] = TOMTOM_A_CDC_CONN_RX_SB_B2_CTL__POR,
+ [TOMTOM_A_CDC_CONN_CLSH_CTL] = TOMTOM_A_CDC_CONN_CLSH_CTL__POR,
+ [TOMTOM_A_CDC_CONN_MISC] = TOMTOM_A_CDC_CONN_MISC__POR,
+ [TOMTOM_A_CDC_CONN_RX8_B1_CTL] = TOMTOM_A_CDC_CONN_RX8_B1_CTL__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK__POR,
+ [TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING] =
+ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING__POR,
+ [TOMTOM_A_CDC_MBHC_EN_CTL] = TOMTOM_A_CDC_MBHC_EN_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_FIR_B1_CFG] = TOMTOM_A_CDC_MBHC_FIR_B1_CFG__POR,
+ [TOMTOM_A_CDC_MBHC_FIR_B2_CFG] = TOMTOM_A_CDC_MBHC_FIR_B2_CFG__POR,
+ [TOMTOM_A_CDC_MBHC_TIMER_B1_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B1_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_TIMER_B2_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B2_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_TIMER_B3_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B3_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_TIMER_B4_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B4_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_TIMER_B5_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B5_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_TIMER_B6_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B6_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_B1_STATUS] = TOMTOM_A_CDC_MBHC_B1_STATUS__POR,
+ [TOMTOM_A_CDC_MBHC_B2_STATUS] = TOMTOM_A_CDC_MBHC_B2_STATUS__POR,
+ [TOMTOM_A_CDC_MBHC_B3_STATUS] = TOMTOM_A_CDC_MBHC_B3_STATUS__POR,
+ [TOMTOM_A_CDC_MBHC_B4_STATUS] = TOMTOM_A_CDC_MBHC_B4_STATUS__POR,
+ [TOMTOM_A_CDC_MBHC_B5_STATUS] = TOMTOM_A_CDC_MBHC_B5_STATUS__POR,
+ [TOMTOM_A_CDC_MBHC_B1_CTL] = TOMTOM_A_CDC_MBHC_B1_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_B2_CTL] = TOMTOM_A_CDC_MBHC_B2_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B1_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B1_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B2_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B2_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B3_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B3_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B4_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B4_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B5_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B5_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B6_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B6_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B7_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B7_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B8_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B8_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B9_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B9_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B10_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B10_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B11_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B11_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_VOLT_B12_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B12_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_CLK_CTL] = TOMTOM_A_CDC_MBHC_CLK_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_INT_CTL] = TOMTOM_A_CDC_MBHC_INT_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_DEBUG_CTL] = TOMTOM_A_CDC_MBHC_DEBUG_CTL__POR,
+ [TOMTOM_A_CDC_MBHC_SPARE] = TOMTOM_A_CDC_MBHC_SPARE__POR,
+ [TOMTOM_A_CDC_RX8_B1_CTL] = TOMTOM_A_CDC_RX8_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX8_B2_CTL] = TOMTOM_A_CDC_RX8_B2_CTL__POR,
+ [TOMTOM_A_CDC_RX8_B3_CTL] = TOMTOM_A_CDC_RX8_B3_CTL__POR,
+ [TOMTOM_A_CDC_RX8_B4_CTL] = TOMTOM_A_CDC_RX8_B4_CTL__POR,
+ [TOMTOM_A_CDC_RX8_B5_CTL] = TOMTOM_A_CDC_RX8_B5_CTL__POR,
+ [TOMTOM_A_CDC_RX8_B6_CTL] = TOMTOM_A_CDC_RX8_B6_CTL__POR,
+ [TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL] =
+ TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL__POR,
+ [TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL] =
+ TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL__POR,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0] =
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0__POR,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1] =
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1__POR,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2] =
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2__POR,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3] =
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3__POR,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4] =
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4__POR,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5] =
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5__POR,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6] =
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6__POR,
+ [TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7] =
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7__POR,
+ [TOMTOM_A_CDC_BOOST_MODE_CTL] = TOMTOM_A_CDC_BOOST_MODE_CTL__POR,
+ [TOMTOM_A_CDC_BOOST_THRESHOLD] = TOMTOM_A_CDC_BOOST_THRESHOLD__POR,
+ [TOMTOM_A_CDC_BOOST_TAP_SEL] = TOMTOM_A_CDC_BOOST_TAP_SEL__POR,
+ [TOMTOM_A_CDC_BOOST_HOLD_TIME] = TOMTOM_A_CDC_BOOST_HOLD_TIME__POR,
+ [TOMTOM_A_CDC_BOOST_TRGR_EN] = TOMTOM_A_CDC_BOOST_TRGR_EN__POR,
+};
diff --git a/sound/soc/codecs/wcd9330.c b/sound/soc/codecs/wcd9330.c
new file mode 100644
index 000000000000..1258c83e26a1
--- /dev/null
+++ b/sound/soc/codecs/wcd9330.c
@@ -0,0 +1,9111 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9330_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include "wcd9330.h"
+#include "wcd9xxx-resmgr.h"
+#include "wcd9xxx-common.h"
+#include "wcdcal-hwdep.h"
+#include "wcd_cpe_core.h"
+
+enum {
+ BUS_DOWN,
+ ADC1_TXFE,
+ ADC2_TXFE,
+ ADC3_TXFE,
+ ADC4_TXFE,
+ ADC5_TXFE,
+ ADC6_TXFE,
+ HPH_DELAY,
+};
+
+#define TOMTOM_MAD_SLIMBUS_TX_PORT 12
+#define TOMTOM_MAD_AUDIO_FIRMWARE_PATH "wcd9320/wcd9320_mad_audio.bin"
+#define TOMTOM_VALIDATE_RX_SBPORT_RANGE(port) ((port >= 16) && (port <= 23))
+#define TOMTOM_VALIDATE_TX_SBPORT_RANGE(port) ((port >= 0) && (port <= 15))
+#define TOMTOM_CONVERT_RX_SBPORT_ID(port) (port - 16) /* RX1 port ID = 0 */
+#define TOMTOM_BIT_ADJ_SHIFT_PORT1_6 4
+#define TOMTOM_BIT_ADJ_SHIFT_PORT7_10 5
+
+#define TOMTOM_HPH_PA_SETTLE_COMP_ON 10000
+#define TOMTOM_HPH_PA_SETTLE_COMP_OFF 13000
+#define TOMTOM_HPH_PA_RAMP_DELAY 30000
+
+#define TOMTOM_SVASS_INT_STATUS_RCO_WDOG 0x20
+#define TOMTOM_SVASS_INT_STATUS_WDOG_BITE 0x02
+
+/* Add any SVA IRQs that are to be treated as FATAL */
+#define TOMTOM_CPE_FATAL_IRQS \
+ (TOMTOM_SVASS_INT_STATUS_RCO_WDOG | \
+ TOMTOM_SVASS_INT_STATUS_WDOG_BITE)
+
+#define DAPM_MICBIAS2_EXTERNAL_STANDALONE "MIC BIAS2 External Standalone"
+
+/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
+#define TOMTOM_WG_TIME_FACTOR_US 240
+
+#define RX8_PATH 8
+#define HPH_PA_ENABLE true
+#define HPH_PA_DISABLE false
+
+#define SLIM_BW_CLK_GEAR_9 6200000
+#define SLIM_BW_UNVOTE 0
+
+static int cpe_debug_mode;
+module_param(cpe_debug_mode, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(cpe_debug_mode, "boot cpe in debug mode");
+
+static atomic_t kp_tomtom_priv;
+
+static int high_perf_mode;
+module_param(high_perf_mode, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(high_perf_mode, "enable/disable class AB config for hph");
+
+static struct afe_param_slimbus_slave_port_cfg tomtom_slimbus_slave_port_cfg = {
+ .minor_version = 1,
+ .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1,
+ .slave_dev_pgd_la = 0,
+ .slave_dev_intfdev_la = 0,
+ .bit_width = 16,
+ .data_format = 0,
+ .num_channels = 1
+};
+
+static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = {
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_MAD_MAIN_CTL_1),
+ HW_MAD_AUDIO_ENABLE, 0x1, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_MAD_AUDIO_CTL_3),
+ HW_MAD_AUDIO_SLEEP_TIME, 0xF, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_MAD_AUDIO_CTL_4),
+ HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR_MODE),
+ MAD_AUDIO_INT_DEST_SELECT_REG, 0x4, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_MASK0),
+ MAD_AUDIO_INT_MASK_REG, 0x2, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_STATUS0),
+ MAD_AUDIO_INT_STATUS_REG, 0x2, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_CLEAR0),
+ MAD_AUDIO_INT_CLEAR_REG, 0x2, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_TX_BASE),
+ SB_PGD_PORT_TX_WATERMARK_N, 0x1E, 8, 0x1
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_TX_BASE),
+ SB_PGD_PORT_TX_ENABLE_N, 0x1, 8, 0x1
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_RX_BASE),
+ SB_PGD_PORT_RX_WATERMARK_N, 0x1E, 8, 0x1
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_RX_BASE),
+ SB_PGD_PORT_RX_ENABLE_N, 0x1, 8, 0x1
+ },
+ { 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_ANC1_IIR_B1_CTL),
+ AANC_FF_GAIN_ADAPTIVE, 0x4, 8, 0
+ },
+ { 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_ANC1_IIR_B1_CTL),
+ AANC_FFGAIN_ADAPTIVE_EN, 0x8, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_ANC1_GAIN_CTL),
+ AANC_GAIN_CONTROL, 0xFF, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_MASK0),
+ MAD_CLIP_INT_MASK_REG, 0x10, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_MASK0),
+ MAD2_CLIP_INT_MASK_REG, 0x20, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_STATUS0),
+ MAD_CLIP_INT_STATUS_REG, 0x10, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_STATUS0),
+ MAD2_CLIP_INT_STATUS_REG, 0x20, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_CLEAR0),
+ MAD_CLIP_INT_CLEAR_REG, 0x10, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_CLEAR0),
+ MAD2_CLIP_INT_CLEAR_REG, 0x20, 8, 0
+ },
+};
+
+static struct afe_param_cdc_reg_cfg clip_reg_cfg[] = {
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL),
+ SPKR_CLIP_PIPE_BANK_SEL, 0x3, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR_CLIPDET_VAL0),
+ SPKR_CLIPDET_VAL0, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR_CLIPDET_VAL1),
+ SPKR_CLIPDET_VAL1, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR_CLIPDET_VAL2),
+ SPKR_CLIPDET_VAL2, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR_CLIPDET_VAL3),
+ SPKR_CLIPDET_VAL3, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR_CLIPDET_VAL4),
+ SPKR_CLIPDET_VAL4, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR_CLIPDET_VAL5),
+ SPKR_CLIPDET_VAL5, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR_CLIPDET_VAL6),
+ SPKR_CLIPDET_VAL6, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR_CLIPDET_VAL7),
+ SPKR_CLIPDET_VAL7, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL),
+ SPKR2_CLIP_PIPE_BANK_SEL, 0x3, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0),
+ SPKR2_CLIPDET_VAL0, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1),
+ SPKR2_CLIPDET_VAL1, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2),
+ SPKR2_CLIPDET_VAL2, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3),
+ SPKR2_CLIPDET_VAL3, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4),
+ SPKR2_CLIPDET_VAL4, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5),
+ SPKR2_CLIPDET_VAL5, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6),
+ SPKR2_CLIPDET_VAL6, 0xff, 8, 0
+ },
+ {
+ 1,
+ (TOMTOM_REGISTER_START_OFFSET +
+ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7),
+ SPKR2_CLIPDET_VAL7, 0xff, 8, 0
+ },
+};
+
+static struct afe_param_cdc_reg_cfg_data tomtom_audio_reg_cfg = {
+ .num_registers = ARRAY_SIZE(audio_reg_cfg),
+ .reg_data = audio_reg_cfg,
+};
+
+static struct afe_param_cdc_reg_cfg_data tomtom_clip_reg_cfg = {
+ .num_registers = ARRAY_SIZE(clip_reg_cfg),
+ .reg_data = clip_reg_cfg,
+};
+
+static struct afe_param_id_cdc_aanc_version tomtom_cdc_aanc_version = {
+ .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION,
+ .aanc_hw_version = AANC_HW_BLOCK_VERSION_2,
+};
+
+static struct afe_param_id_clip_bank_sel clip_bank_sel = {
+ .minor_version = AFE_API_VERSION_CLIP_BANK_SEL_CFG,
+ .num_banks = AFE_CLIP_MAX_BANKS,
+ .bank_map = {0, 1, 2, 3},
+};
+
+#define WCD9330_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define NUM_DECIMATORS 10
+#define NUM_INTERPOLATORS 8
+#define BITS_PER_REG 8
+#define TOMTOM_TX_PORT_NUMBER 16
+#define TOMTOM_RX_PORT_START_NUMBER 16
+
+#define TOMTOM_I2S_MASTER_MODE_MASK 0x08
+
+#define TOMTOM_SLIM_CLOSE_TIMEOUT 1000
+#define TOMTOM_SLIM_IRQ_OVERFLOW (1 << 0)
+#define TOMTOM_SLIM_IRQ_UNDERFLOW (1 << 1)
+#define TOMTOM_SLIM_IRQ_PORT_CLOSED (1 << 2)
+#define TOMTOM_MCLK_CLK_12P288MHZ 12288000
+#define TOMTOM_MCLK_CLK_9P6MHZ 9600000
+
+#define TOMTOM_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FORMAT_S24_LE)
+
+#define TOMTOM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+#define TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 (TOMTOM_SLIM_PGD_PORT_INT_EN0 + 2)
+#define TOMTOM_ZDET_BOX_CAR_AVG_LOOP_COUNT 1
+#define TOMTOM_ZDET_MUL_FACTOR_1X 7218
+#define TOMTOM_ZDET_MUL_FACTOR_10X (TOMTOM_ZDET_MUL_FACTOR_1X * 10)
+#define TOMTOM_ZDET_MUL_FACTOR_100X (TOMTOM_ZDET_MUL_FACTOR_1X * 100)
+#define TOMTOM_ZDET_ERROR_APPROX_MUL_FACTOR 655
+#define TOMTOM_ZDET_ERROR_APPROX_SHIFT 16
+#define TOMTOM_ZDET_ZONE_3_DEFAULT_VAL 1000000
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ AIF2_PB,
+ AIF2_CAP,
+ AIF3_PB,
+ AIF3_CAP,
+ AIF4_VIFEED,
+ AIF4_MAD_TX,
+ NUM_CODEC_DAIS,
+};
+
+enum {
+ RX_MIX1_INP_SEL_ZERO = 0,
+ RX_MIX1_INP_SEL_SRC1,
+ RX_MIX1_INP_SEL_SRC2,
+ RX_MIX1_INP_SEL_IIR1,
+ RX_MIX1_INP_SEL_IIR2,
+ RX_MIX1_INP_SEL_RX1,
+ RX_MIX1_INP_SEL_RX2,
+ RX_MIX1_INP_SEL_RX3,
+ RX_MIX1_INP_SEL_RX4,
+ RX_MIX1_INP_SEL_RX5,
+ RX_MIX1_INP_SEL_RX6,
+ RX_MIX1_INP_SEL_RX7,
+ RX_MIX1_INP_SEL_AUXRX,
+};
+enum {
+ RX8_MIX1_INP_SEL_ZERO = 0,
+ RX8_MIX1_INP_SEL_IIR1,
+ RX8_MIX1_INP_SEL_IIR2,
+ RX8_MIX1_INP_SEL_RX1,
+ RX8_MIX1_INP_SEL_RX2,
+ RX8_MIX1_INP_SEL_RX3,
+ RX8_MIX1_INP_SEL_RX4,
+ RX8_MIX1_INP_SEL_RX5,
+ RX8_MIX1_INP_SEL_RX6,
+ RX8_MIX1_INP_SEL_RX7,
+ RX8_MIX1_INP_SEL_RX8,
+};
+
+#define TOMTOM_COMP_DIGITAL_GAIN_OFFSET 3
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+static struct snd_soc_dai_driver tomtom_dai[];
+static const DECLARE_TLV_DB_SCALE(aux_pga_gain, 0, 2, 0);
+
+/* Codec supports 2 IIR filters */
+enum {
+ IIR1 = 0,
+ IIR2,
+ IIR_MAX,
+};
+/* Codec supports 5 bands */
+enum {
+ BAND1 = 0,
+ BAND2,
+ BAND3,
+ BAND4,
+ BAND5,
+ BAND_MAX,
+};
+
+enum {
+ COMPANDER_0,
+ COMPANDER_1,
+ COMPANDER_2,
+ COMPANDER_MAX,
+};
+
+enum {
+ COMPANDER_FS_8KHZ = 0,
+ COMPANDER_FS_16KHZ,
+ COMPANDER_FS_32KHZ,
+ COMPANDER_FS_48KHZ,
+ COMPANDER_FS_96KHZ,
+ COMPANDER_FS_192KHZ,
+ COMPANDER_FS_MAX,
+};
+
+struct comp_sample_dependent_params {
+ u32 peak_det_timeout;
+ u32 rms_meter_div_fact;
+ u32 rms_meter_resamp_fact;
+};
+
+struct hpf_work {
+ struct tomtom_priv *tomtom;
+ u32 decimator;
+ u8 tx_hpf_cut_of_freq;
+ bool tx_hpf_bypass;
+ struct delayed_work dwork;
+};
+
+static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
+
+static const struct wcd9xxx_ch tomtom_rx_chs[TOMTOM_RX_MAX] = {
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER, 0),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 1, 1),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 2, 2),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 3, 3),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 4, 4),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 5, 5),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 6, 6),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 7, 7),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 8, 8),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 9, 9),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 10, 10),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 11, 11),
+ WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 12, 12),
+};
+
+static const struct wcd9xxx_ch tomtom_tx_chs[TOMTOM_TX_MAX] = {
+ WCD9XXX_CH(0, 0),
+ WCD9XXX_CH(1, 1),
+ WCD9XXX_CH(2, 2),
+ WCD9XXX_CH(3, 3),
+ WCD9XXX_CH(4, 4),
+ WCD9XXX_CH(5, 5),
+ WCD9XXX_CH(6, 6),
+ WCD9XXX_CH(7, 7),
+ WCD9XXX_CH(8, 8),
+ WCD9XXX_CH(9, 9),
+ WCD9XXX_CH(10, 10),
+ WCD9XXX_CH(11, 11),
+ WCD9XXX_CH(12, 12),
+ WCD9XXX_CH(13, 13),
+ WCD9XXX_CH(14, 14),
+ WCD9XXX_CH(15, 15),
+};
+
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+ 0, /* AIF1_PB */
+ (1 << AIF2_CAP) | (1 << AIF3_CAP), /* AIF1_CAP */
+ 0, /* AIF2_PB */
+ (1 << AIF1_CAP) | (1 << AIF3_CAP), /* AIF2_CAP */
+ 0, /* AIF3_PB */
+ (1 << AIF1_CAP) | (1 << AIF2_CAP), /* AIF3_CAP */
+};
+
+static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = {
+ 0, /* AIF1_PB */
+ 0, /* AIF1_CAP */
+ 0, /* AIF2_PB */
+ 0, /* AIF2_CAP */
+};
+
+/*
+ * Interrupt table for v3 corresponds to newer version
+ * codecs (wcd9330)
+ */
+static const struct intr_data wcd9330_intr_tbl[] = {
+ {WCD9XXX_IRQ_SLIMBUS, false},
+ {WCD9XXX_IRQ_MBHC_INSERTION, true},
+ {WCD9XXX_IRQ_MBHC_POTENTIAL, true},
+ {WCD9XXX_IRQ_MBHC_RELEASE, true},
+ {WCD9XXX_IRQ_MBHC_PRESS, true},
+ {WCD9XXX_IRQ_MBHC_SHORT_TERM, true},
+ {WCD9XXX_IRQ_MBHC_REMOVAL, true},
+ {WCD9330_IRQ_MBHC_JACK_SWITCH, true},
+ {WCD9XXX_IRQ_BG_PRECHARGE, false},
+ {WCD9XXX_IRQ_PA1_STARTUP, false},
+ {WCD9XXX_IRQ_PA2_STARTUP, false},
+ {WCD9XXX_IRQ_PA3_STARTUP, false},
+ {WCD9XXX_IRQ_PA4_STARTUP, false},
+ {WCD9XXX_IRQ_PA5_STARTUP, false},
+ {WCD9XXX_IRQ_MICBIAS1_PRECHARGE, false},
+ {WCD9XXX_IRQ_MICBIAS2_PRECHARGE, false},
+ {WCD9XXX_IRQ_MICBIAS3_PRECHARGE, false},
+ {WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, false},
+ {WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, false},
+ {WCD9XXX_IRQ_EAR_PA_OCPL_FAULT, false},
+ {WCD9XXX_IRQ_HPH_L_PA_STARTUP, false},
+ {WCD9XXX_IRQ_HPH_R_PA_STARTUP, false},
+ {WCD9320_IRQ_EAR_PA_STARTUP, false},
+ {WCD9330_IRQ_SVASS_ERR_EXCEPTION, false},
+ {WCD9330_IRQ_SVASS_ENGINE, true},
+ {WCD9330_IRQ_MAD_AUDIO, false},
+ {WCD9330_IRQ_MAD_BEACON, false},
+ {WCD9330_IRQ_MAD_ULTRASOUND, false},
+ {WCD9330_IRQ_SPEAKER1_CLIPPING, false},
+ {WCD9330_IRQ_SPEAKER2_CLIPPING, false},
+ {WCD9330_IRQ_VBAT_MONITOR_ATTACK, false},
+ {WCD9330_IRQ_VBAT_MONITOR_RELEASE, false},
+};
+
+struct tomtom_priv {
+ struct snd_soc_codec *codec;
+ u32 adc_count;
+ u32 rx_bias_count;
+ s32 dmic_1_2_clk_cnt;
+ s32 dmic_3_4_clk_cnt;
+ s32 dmic_5_6_clk_cnt;
+ s32 ldo_h_users;
+ s32 micb_2_users;
+
+ u32 anc_slot;
+ bool anc_func;
+
+ /* cal info for codec */
+ struct fw_info *fw_data;
+
+ /*track tomtom interface type*/
+ u8 intf_type;
+
+ /* num of slim ports required */
+ struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
+
+ /*compander*/
+ int comp_enabled[COMPANDER_MAX];
+ u32 comp_fs[COMPANDER_MAX];
+
+ /* Maintain the status of AUX PGA */
+ int aux_pga_cnt;
+ u8 aux_l_gain;
+ u8 aux_r_gain;
+
+ bool spkr_pa_widget_on;
+ struct regulator *spkdrv_reg;
+ struct regulator *spkdrv2_reg;
+
+ bool mbhc_started;
+
+ struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg;
+
+ /* resmgr module */
+ struct wcd9xxx_resmgr resmgr;
+ /* mbhc module */
+ struct wcd9xxx_mbhc mbhc;
+
+ /* class h specific data */
+ struct wcd9xxx_clsh_cdc_data clsh_d;
+
+ int (*machine_codec_event_cb)(struct snd_soc_codec *codec,
+ enum wcd9xxx_codec_event);
+ int (*codec_ext_clk_en_cb)(struct snd_soc_codec *codec,
+ int enable, bool dapm);
+ int (*codec_get_ext_clk_cnt) (void);
+ /*
+ * list used to save/restore registers at start and
+ * end of impedance measurement
+ */
+ struct list_head reg_save_restore;
+
+ /* handle to cpe core */
+ struct wcd_cpe_core *cpe_core;
+
+ /* UHQA (class AB) mode */
+ u8 uhqa_mode;
+
+ /* Multiplication factor used for impedance detection */
+ int zdet_gain_mul_fact;
+
+ /* to track the status */
+ unsigned long status_mask;
+
+ int ext_clk_users;
+ struct clk *wcd_ext_clk;
+
+ /* Port values for Rx and Tx codec_dai */
+ unsigned int rx_port_value;
+ unsigned int tx_port_value;
+
+ struct mutex codec_mutex;
+};
+
+static const u32 comp_shift[] = {
+ 4, /* Compander 0's clock source is on interpolator 7 */
+ 0,
+ 2,
+};
+
+static const int comp_rx_path[] = {
+ COMPANDER_1,
+ COMPANDER_1,
+ COMPANDER_2,
+ COMPANDER_2,
+ COMPANDER_2,
+ COMPANDER_2,
+ COMPANDER_0,
+ COMPANDER_0,
+ COMPANDER_MAX,
+};
+
+static const struct comp_sample_dependent_params comp_samp_params[] = {
+ {
+ /* 8 Khz */
+ .peak_det_timeout = 0x06,
+ .rms_meter_div_fact = 0x09,
+ .rms_meter_resamp_fact = 0x06,
+ },
+ {
+ /* 16 Khz */
+ .peak_det_timeout = 0x07,
+ .rms_meter_div_fact = 0x0A,
+ .rms_meter_resamp_fact = 0x0C,
+ },
+ {
+ /* 32 Khz */
+ .peak_det_timeout = 0x08,
+ .rms_meter_div_fact = 0x0B,
+ .rms_meter_resamp_fact = 0x1E,
+ },
+ {
+ /* 48 Khz */
+ .peak_det_timeout = 0x09,
+ .rms_meter_div_fact = 0x0B,
+ .rms_meter_resamp_fact = 0x28,
+ },
+ {
+ /* 96 Khz */
+ .peak_det_timeout = 0x0A,
+ .rms_meter_div_fact = 0x0C,
+ .rms_meter_resamp_fact = 0x50,
+ },
+ {
+ /* 192 Khz */
+ .peak_det_timeout = 0x0B,
+ .rms_meter_div_fact = 0xC,
+ .rms_meter_resamp_fact = 0xA0,
+ },
+};
+
+static unsigned short rx_digital_gain_reg[] = {
+ TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL,
+ TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL,
+ TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL,
+ TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL,
+ TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL,
+ TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL,
+ TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL,
+ TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL,
+};
+
+
+static unsigned short tx_digital_gain_reg[] = {
+ TOMTOM_A_CDC_TX1_VOL_CTL_GAIN,
+ TOMTOM_A_CDC_TX2_VOL_CTL_GAIN,
+ TOMTOM_A_CDC_TX3_VOL_CTL_GAIN,
+ TOMTOM_A_CDC_TX4_VOL_CTL_GAIN,
+ TOMTOM_A_CDC_TX5_VOL_CTL_GAIN,
+ TOMTOM_A_CDC_TX6_VOL_CTL_GAIN,
+ TOMTOM_A_CDC_TX7_VOL_CTL_GAIN,
+ TOMTOM_A_CDC_TX8_VOL_CTL_GAIN,
+ TOMTOM_A_CDC_TX9_VOL_CTL_GAIN,
+ TOMTOM_A_CDC_TX10_VOL_CTL_GAIN,
+};
+
+/*
+ * wcd9330_get_codec_info: Get codec specific information
+ *
+ * @wcd9xxx: pointer to wcd9xxx structure
+ * @wcd_type: pointer to wcd9xxx_codec_type structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9330_get_codec_info(struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_codec_type *wcd_type)
+{
+ u16 id_minor, id_major;
+ struct regmap *wcd_regmap;
+ int rc, val, version = 0;
+
+ if (!wcd9xxx || !wcd_type)
+ return -EINVAL;
+
+ if (!wcd9xxx->regmap) {
+ dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n",
+ __func__);
+ return -EINVAL;
+ }
+ wcd_regmap = wcd9xxx->regmap;
+ rc = regmap_bulk_read(wcd_regmap, TOMTOM_A_CHIP_ID_BYTE_0,
+ (u8 *)&id_minor, sizeof(u16));
+ if (rc)
+ return -EINVAL;
+
+ rc = regmap_bulk_read(wcd_regmap, TOMTOM_A_CHIP_ID_BYTE_2,
+ (u8 *)&id_major, sizeof(u16));
+ if (rc)
+ return -EINVAL;
+
+ dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n",
+ __func__, id_major, id_minor);
+
+ if (id_minor == cpu_to_le16(0x1))
+ version = 2;
+ else if (id_minor == cpu_to_le16(0x0))
+ version = 1;
+ else
+ dev_err(wcd9xxx->dev, "%s: wcd9330 version unknown (major 0x%x, minor 0x%x)\n",
+ __func__, id_major, id_minor);
+
+ /* Fill codec type info */
+ wcd_type->id_major = id_major;
+ wcd_type->id_minor = id_minor;
+ wcd_type->num_irqs = WCD9330_NUM_IRQS;
+ wcd_type->version = version;
+ wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1;
+ wcd_type->i2c_chip_status = 0x01;
+ wcd_type->intr_tbl = wcd9330_intr_tbl;
+ wcd_type->intr_tbl_size = ARRAY_SIZE(wcd9330_intr_tbl);
+
+ wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] =
+ TOMTOM_A_INTR1_STATUS0;
+ wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] =
+ TOMTOM_A_INTR1_CLEAR0;
+ wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] =
+ TOMTOM_A_INTR1_MASK0;
+ wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] =
+ TOMTOM_A_INTR1_LEVEL0;
+ wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] =
+ TOMTOM_A_INTR_MODE;
+
+ return rc;
+}
+EXPORT_SYMBOL(wcd9330_get_codec_info);
+
+/*
+ * wcd9330_bringdown: Bringdown WCD Codec
+ *
+ * @wcd9xxx: Pointer to wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9330_bringdown(struct wcd9xxx *wcd9xxx)
+{
+ if (!wcd9xxx || !wcd9xxx->regmap)
+ return -EINVAL;
+
+ regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x7);
+ regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x6);
+ regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0xe);
+ regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x8);
+
+ return 0;
+}
+EXPORT_SYMBOL(wcd9330_bringdown);
+
+/*
+ * wcd9330_bringup: Bring up WCD Codec
+ *
+ * @wcd9xxx: Pointer to wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9330_bringup(struct wcd9xxx *wcd9xxx)
+{
+ if (!wcd9xxx || !wcd9xxx->regmap)
+ return -EINVAL;
+
+ regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x4);
+ regmap_write(wcd9xxx->regmap, TOMTOM_A_CDC_CTL, 0x0);
+ /* wait for 5ms after codec reset for it to complete */
+ usleep_range(5000, 5100);
+ regmap_write(wcd9xxx->regmap, TOMTOM_A_CDC_CTL, 0x1);
+ regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x3);
+ regmap_write(wcd9xxx->regmap, TOMTOM_A_CDC_CTL, 0x3);
+
+ return 0;
+}
+EXPORT_SYMBOL(wcd9330_bringup);
+
+int tomtom_enable_qfuse_sensing(struct snd_soc_codec *codec)
+{
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ if (tomtom->wcd_ext_clk)
+ tomtom_codec_mclk_enable(codec, true, false);
+
+ snd_soc_write(codec, TOMTOM_A_QFUSE_CTL, 0x03);
+ /*
+ * 5ms sleep required after enabling qfuse control
+ * before checking the status.
+ */
+ usleep_range(5000, 5500);
+ if ((snd_soc_read(codec, TOMTOM_A_QFUSE_STATUS) & (0x03)) != 0x03)
+ WARN(1, "%s: Qfuse sense is not complete\n", __func__);
+
+ if (tomtom->wcd_ext_clk)
+ tomtom_codec_mclk_enable(codec, false, false);
+ return 0;
+}
+EXPORT_SYMBOL(tomtom_enable_qfuse_sensing);
+
+static int tomtom_get_sample_rate(struct snd_soc_codec *codec, int path)
+{
+ if (path == RX8_PATH)
+ return snd_soc_read(codec, TOMTOM_A_CDC_RX8_B5_CTL);
+ else
+ return snd_soc_read(codec,
+ (TOMTOM_A_CDC_RX1_B5_CTL + 8 * (path - 1)));
+}
+
+static int tomtom_compare_bit_format(struct snd_soc_codec *codec,
+ int bit_format)
+{
+ int i = 0;
+ int ret = 0;
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+ for (i = 0; i < NUM_CODEC_DAIS; i++) {
+ if (tomtom_p->dai[i].bit_width == bit_format) {
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+static int tomtom_update_uhqa_mode(struct snd_soc_codec *codec, int path)
+{
+ int ret = 0;
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+ /* UHQA path has fs=192KHz & bit=24 bit */
+ if (((tomtom_get_sample_rate(codec, path) & 0xE0) == 0xA0) &&
+ (tomtom_compare_bit_format(codec, 24))) {
+ tomtom_p->uhqa_mode = 1;
+ } else {
+ tomtom_p->uhqa_mode = 0;
+ }
+ dev_dbg(codec->dev, "%s: uhqa_mode=%d", __func__, tomtom_p->uhqa_mode);
+ return ret;
+}
+
+static int tomtom_get_anc_slot(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ ucontrol->value.integer.value[0] = tomtom->anc_slot;
+ return 0;
+}
+
+static int tomtom_put_anc_slot(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ tomtom->anc_slot = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int tomtom_get_anc_func(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = (tomtom->anc_func == true ? 1 : 0);
+ return 0;
+}
+
+static int tomtom_put_anc_func(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_codec_get_dapm(codec);
+
+ mutex_lock(&tomtom->codec_mutex);
+ tomtom->anc_func = (!ucontrol->value.integer.value[0] ? false : true);
+
+ dev_dbg(codec->dev, "%s: anc_func %x", __func__, tomtom->anc_func);
+
+ if (tomtom->anc_func == true) {
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_enable_pin(dapm, "ANC HEADPHONE");
+ snd_soc_dapm_enable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_enable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_disable_pin(dapm, "HPHR");
+ snd_soc_dapm_disable_pin(dapm, "HPHL");
+ snd_soc_dapm_disable_pin(dapm, "HEADPHONE");
+ snd_soc_dapm_disable_pin(dapm, "EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "EAR");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_disable_pin(dapm, "ANC HEADPHONE");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_enable_pin(dapm, "HPHR");
+ snd_soc_dapm_enable_pin(dapm, "HPHL");
+ snd_soc_dapm_enable_pin(dapm, "HEADPHONE");
+ snd_soc_dapm_enable_pin(dapm, "EAR PA");
+ snd_soc_dapm_enable_pin(dapm, "EAR");
+ }
+ mutex_unlock(&tomtom->codec_mutex);
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static int tomtom_get_iir_enable_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] =
+ (snd_soc_read(codec, (TOMTOM_A_CDC_IIR1_CTL + 16 * iir_idx)) &
+ (1 << band_idx)) != 0;
+
+ pr_debug("%s: IIR #%d band #%d enable %d\n", __func__,
+ iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int tomtom_put_iir_enable_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+
+ /* Mask first 5 bits, 6-8 are reserved */
+ snd_soc_update_bits(codec, (TOMTOM_A_CDC_IIR1_CTL + 16 * iir_idx),
+ (1 << band_idx), (value << band_idx));
+
+ pr_debug("%s: IIR #%d band #%d enable %d\n", __func__,
+ iir_idx, band_idx,
+ ((snd_soc_read(codec, (TOMTOM_A_CDC_IIR1_CTL + 16 * iir_idx)) &
+ (1 << band_idx)) != 0));
+ return 0;
+}
+static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec,
+ int iir_idx, int band_idx,
+ int coeff_idx)
+{
+ uint32_t value = 0;
+
+ /* Address does not automatically update if reading */
+ snd_soc_write(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t)) & 0x7F);
+
+ value |= snd_soc_read(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx));
+
+ snd_soc_write(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 1) & 0x7F);
+
+ value |= (snd_soc_read(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) << 8);
+
+ snd_soc_write(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 2) & 0x7F);
+
+ value |= (snd_soc_read(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) << 16);
+
+ snd_soc_write(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 3) & 0x7F);
+
+ /* Mask bits top 2 bits since they are reserved */
+ value |= ((snd_soc_read(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) & 0x3F) << 24);
+
+ return value;
+}
+
+static int tomtom_get_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 0);
+ ucontrol->value.integer.value[1] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 1);
+ ucontrol->value.integer.value[2] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 2);
+ ucontrol->value.integer.value[3] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 3);
+ ucontrol->value.integer.value[4] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 4);
+
+ pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+ "%s: IIR #%d band #%d b1 = 0x%x\n"
+ "%s: IIR #%d band #%d b2 = 0x%x\n"
+ "%s: IIR #%d band #%d a1 = 0x%x\n"
+ "%s: IIR #%d band #%d a2 = 0x%x\n",
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[0],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[1],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[2],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[3],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[4]);
+ return 0;
+}
+
+static void set_iir_band_coeff(struct snd_soc_codec *codec,
+ int iir_idx, int band_idx,
+ uint32_t value)
+{
+ snd_soc_write(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+ (value & 0xFF));
+
+ snd_soc_write(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+ (value >> 8) & 0xFF);
+
+ snd_soc_write(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+ (value >> 16) & 0xFF);
+
+ /* Mask top 2 bits, 7-8 are reserved */
+ snd_soc_write(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+ (value >> 24) & 0x3F);
+}
+
+static int tomtom_put_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ /* Mask top bit it is reserved */
+ /* Updates addr automatically for each B2 write */
+ snd_soc_write(codec,
+ (TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+ (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[0]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[1]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[2]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[3]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[4]);
+
+ pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+ "%s: IIR #%d band #%d b1 = 0x%x\n"
+ "%s: IIR #%d band #%d b2 = 0x%x\n"
+ "%s: IIR #%d band #%d a1 = 0x%x\n"
+ "%s: IIR #%d band #%d a2 = 0x%x\n",
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 0),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 1),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 2),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 3),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 4));
+ return 0;
+}
+
+static int tomtom_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int comp = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tomtom->comp_enabled[comp];
+ return 0;
+}
+
+static int tomtom_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ int comp = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: Compander %d enable current %d, new %d\n",
+ __func__, comp, tomtom->comp_enabled[comp], value);
+ tomtom->comp_enabled[comp] = value;
+
+ if (comp == COMPANDER_1 &&
+ tomtom->comp_enabled[comp] == 1) {
+ /* Wavegen to 5 msec */
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xDB);
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_TIME, 0x2A);
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_BIAS_WG_OCP, 0x2A);
+
+ /* Enable Chopper */
+ snd_soc_update_bits(codec,
+ TOMTOM_A_RX_HPH_CHOP_CTL, 0x80, 0x80);
+
+ snd_soc_write(codec, TOMTOM_A_NCP_DTEST, 0x20);
+ pr_debug("%s: Enabled Chopper and set wavegen to 5 msec\n",
+ __func__);
+ } else if (comp == COMPANDER_1 &&
+ tomtom->comp_enabled[comp] == 0) {
+ /* Wavegen to 20 msec */
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xDB);
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_TIME, 0x58);
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_BIAS_WG_OCP, 0x1A);
+
+ /* Disable CHOPPER block */
+ snd_soc_update_bits(codec,
+ TOMTOM_A_RX_HPH_CHOP_CTL, 0x80, 0x00);
+
+ snd_soc_write(codec, TOMTOM_A_NCP_DTEST, 0x10);
+ pr_debug("%s: Disabled Chopper and set wavegen to 20 msec\n",
+ __func__);
+ }
+ return 0;
+}
+
+static int tomtom_config_gain_compander(struct snd_soc_codec *codec,
+ int comp, bool enable)
+{
+ int ret = 0;
+
+ switch (comp) {
+ case COMPANDER_0:
+ snd_soc_update_bits(codec, TOMTOM_A_SPKR_DRV1_GAIN,
+ 1 << 2, !enable << 2);
+ snd_soc_update_bits(codec, TOMTOM_A_SPKR_DRV2_GAIN,
+ 1 << 2, !enable << 2);
+ break;
+ case COMPANDER_1:
+ snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_L_GAIN,
+ 1 << 5, !enable << 5);
+ snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_R_GAIN,
+ 1 << 5, !enable << 5);
+ break;
+ case COMPANDER_2:
+ snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_1_GAIN,
+ 1 << 5, !enable << 5);
+ snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_3_GAIN,
+ 1 << 5, !enable << 5);
+ snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_2_GAIN,
+ 1 << 5, !enable << 5);
+ snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_4_GAIN,
+ 1 << 5, !enable << 5);
+ break;
+ default:
+ WARN_ON(1);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void tomtom_discharge_comp(struct snd_soc_codec *codec, int comp)
+{
+ /* Level meter DIV Factor to 5*/
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_COMP0_B2_CTL + (comp * 8), 0xF0,
+ 0x05 << 4);
+ /* RMS meter Sampling to 0x01 */
+ snd_soc_write(codec, TOMTOM_A_CDC_COMP0_B3_CTL + (comp * 8), 0x01);
+
+ /* Worst case timeout for compander CnP sleep timeout */
+ usleep_range(3000, 3100);
+}
+
+static enum wcd9xxx_buck_volt tomtom_codec_get_buck_mv(
+ struct snd_soc_codec *codec)
+{
+ int buck_volt = WCD9XXX_CDC_BUCK_UNSUPPORTED;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_pdata *pdata = tomtom->resmgr.pdata;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+ if (!strcmp(pdata->regulator[i].name,
+ WCD9XXX_SUPPLY_BUCK_NAME)) {
+ if ((pdata->regulator[i].min_uV ==
+ WCD9XXX_CDC_BUCK_MV_1P8) ||
+ (pdata->regulator[i].min_uV ==
+ WCD9XXX_CDC_BUCK_MV_2P15))
+ buck_volt = pdata->regulator[i].min_uV;
+ break;
+ }
+ }
+ return buck_volt;
+}
+
+static int tomtom_config_compander(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int mask, enable_mask;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ const int comp = w->shift;
+ const u32 rate = tomtom->comp_fs[comp];
+ const struct comp_sample_dependent_params *comp_params =
+ &comp_samp_params[rate];
+ enum wcd9xxx_buck_volt buck_mv;
+
+ pr_debug("%s: %s event %d compander %d, enabled %d", __func__,
+ w->name, event, comp, tomtom->comp_enabled[comp]);
+
+ if (!tomtom->comp_enabled[comp])
+ return 0;
+
+ /* Compander 0 has two channels */
+ mask = enable_mask = 0x03;
+ buck_mv = tomtom_codec_get_buck_mv(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Set compander Sample rate */
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_COMP0_FS_CFG + (comp * 8),
+ 0x07, rate);
+ /* Set the static gain offset for HPH Path */
+ if (comp == COMPANDER_1) {
+ if (buck_mv == WCD9XXX_CDC_BUCK_MV_2P15) {
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_COMP0_B4_CTL + (comp * 8),
+ 0x80, 0x00);
+ } else {
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_COMP0_B4_CTL + (comp * 8),
+ 0x80, 0x80);
+ }
+ }
+ /* Enable RX interpolation path compander clocks */
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_B2_CTL,
+ mask << comp_shift[comp],
+ mask << comp_shift[comp]);
+ /* Toggle compander reset bits */
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL,
+ mask << comp_shift[comp],
+ mask << comp_shift[comp]);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL,
+ mask << comp_shift[comp], 0);
+
+ /* Set gain source to compander */
+ tomtom_config_gain_compander(codec, comp, true);
+
+ /* Compander enable */
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_COMP0_B1_CTL +
+ (comp * 8), enable_mask, enable_mask);
+
+ tomtom_discharge_comp(codec, comp);
+
+ /* Set sample rate dependent paramater */
+ snd_soc_write(codec, TOMTOM_A_CDC_COMP0_B3_CTL + (comp * 8),
+ comp_params->rms_meter_resamp_fact);
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_COMP0_B2_CTL + (comp * 8),
+ 0xF0, comp_params->rms_meter_div_fact << 4);
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_COMP0_B2_CTL + (comp * 8),
+ 0x0F, comp_params->peak_det_timeout);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Disable compander */
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_COMP0_B1_CTL + (comp * 8),
+ enable_mask, 0x00);
+
+ /* Toggle compander reset bits */
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL,
+ mask << comp_shift[comp],
+ mask << comp_shift[comp]);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL,
+ mask << comp_shift[comp], 0);
+
+ /* Turn off the clock for compander in pair */
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_B2_CTL,
+ mask << comp_shift[comp], 0);
+
+ /* Set gain source to register */
+ tomtom_config_gain_compander(codec, comp, false);
+ break;
+ }
+ return 0;
+}
+
+
+
+static const char *const tomtom_anc_func_text[] = {"OFF", "ON"};
+static const struct soc_enum tomtom_anc_func_enum =
+ SOC_ENUM_SINGLE_EXT(2, tomtom_anc_func_text);
+
+static const char *const tabla_ear_pa_gain_text[] = {"POS_6_DB", "POS_2_DB"};
+static const struct soc_enum tabla_ear_pa_gain_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, tabla_ear_pa_gain_text),
+};
+
+/*cut of frequency for high pass filter*/
+static const char * const cf_text[] = {
+ "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz"
+};
+
+static const char * const rx_cf_text[] = {
+ "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz",
+ "MIN_3DB_0P48Hz"
+};
+
+static const struct soc_enum cf_dec1_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX1_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec2_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX2_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec3_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX3_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec4_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX4_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec5_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX5_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec6_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX6_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec7_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX7_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec8_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX8_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec9_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX9_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec10_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX10_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_rxmix1_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX1_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix2_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX2_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix3_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX3_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix4_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX4_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix5_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX5_B4_CTL, 0, 4, rx_cf_text)
+;
+static const struct soc_enum cf_rxmix6_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX6_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix7_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX7_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix8_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX8_B4_CTL, 0, 4, rx_cf_text);
+
+static const char * const class_h_dsm_text[] = {
+ "ZERO", "DSM_HPHL_RX1", "DSM_SPKR_RX7"
+};
+
+static const struct soc_enum class_h_dsm_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_CLSH_CTL, 4, 3, class_h_dsm_text);
+
+static const struct snd_kcontrol_new class_h_dsm_mux =
+ SOC_DAPM_ENUM("CLASS_H_DSM MUX Mux", class_h_dsm_enum);
+
+static const char * const rx1_interp_text[] = {
+ "ZERO", "RX1 MIX2"
+};
+
+static const struct soc_enum rx1_interp_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CLK_RX_B1_CTL, 0, 2, rx1_interp_text);
+
+static const struct snd_kcontrol_new rx1_interp_mux =
+ SOC_DAPM_ENUM("RX1 INTERP MUX Mux", rx1_interp_enum);
+
+static const char * const rx2_interp_text[] = {
+ "ZERO", "RX2 MIX2"
+};
+
+static const struct soc_enum rx2_interp_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CLK_RX_B1_CTL, 1, 2, rx2_interp_text);
+
+static const struct snd_kcontrol_new rx2_interp_mux =
+ SOC_DAPM_ENUM("RX2 INTERP MUX Mux", rx2_interp_enum);
+
+static const char *const tomtom_conn_mad_text[] = {
+ "ADC_MB", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6", "NOTUSED1",
+ "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6", "NOTUSED2",
+ "NOTUSED3"};
+
+static const struct soc_enum tomtom_conn_mad_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tomtom_conn_mad_text),
+ tomtom_conn_mad_text);
+
+
+static int tomtom_mad_input_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 tomtom_mad_input;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ tomtom_mad_input = snd_soc_read(codec, TOMTOM_A_CDC_MAD_INP_SEL);
+
+ tomtom_mad_input = tomtom_mad_input & 0x0F;
+
+ ucontrol->value.integer.value[0] = tomtom_mad_input;
+
+ pr_debug("%s: tomtom_mad_input = %s\n", __func__,
+ tomtom_conn_mad_text[tomtom_mad_input]);
+
+ return 0;
+}
+
+static int tomtom_mad_input_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 tomtom_mad_input;
+ u16 micb_int_reg, micb_4_int_reg;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_card *card = codec->component.card;
+ char mad_amic_input_widget[6];
+ u32 adc;
+ const char *mad_input_widget;
+ const char *source_widget = NULL;
+ u32 mic_bias_found = 0;
+ u32 i;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ char *mad_input;
+
+ tomtom_mad_input = ucontrol->value.integer.value[0];
+ micb_4_int_reg = tomtom->resmgr.reg_addr->micb_4_int_rbias;
+
+ if (tomtom_mad_input >= ARRAY_SIZE(tomtom_conn_mad_text)) {
+ dev_err(codec->dev,
+ "%s: tomtom_mad_input = %d out of bounds\n",
+ __func__, tomtom_mad_input);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: tomtom_mad_input = %s\n", __func__,
+ tomtom_conn_mad_text[tomtom_mad_input]);
+
+ if (!strcmp(tomtom_conn_mad_text[tomtom_mad_input], "NOTUSED1") ||
+ !strcmp(tomtom_conn_mad_text[tomtom_mad_input], "NOTUSED2") ||
+ !strcmp(tomtom_conn_mad_text[tomtom_mad_input], "NOTUSED3") ||
+ !strcmp(tomtom_conn_mad_text[tomtom_mad_input], "ADC_MB")) {
+ pr_info("%s: tomtom mad input is set to unsupported input = %s\n",
+ __func__, tomtom_conn_mad_text[tomtom_mad_input]);
+ return -EINVAL;
+ }
+
+ if (strnstr(tomtom_conn_mad_text[tomtom_mad_input],
+ "ADC", sizeof("ADC"))) {
+ mad_input = strpbrk(tomtom_conn_mad_text[tomtom_mad_input],
+ "123456");
+ if (!mad_input) {
+ dev_err(codec->dev, "%s: Invalid MAD input %s\n",
+ __func__, tomtom_conn_mad_text[tomtom_mad_input]);
+ return -EINVAL;
+ }
+ ret = kstrtouint(mad_input, 10, &adc);
+ if ((ret < 0) || (adc > 6)) {
+ pr_err("%s: Invalid ADC = %s\n", __func__,
+ tomtom_conn_mad_text[tomtom_mad_input]);
+ ret = -EINVAL;
+ }
+
+ snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc);
+
+ mad_input_widget = mad_amic_input_widget;
+ pr_debug("%s: tomtom amic input widget = %s\n", __func__,
+ mad_amic_input_widget);
+ } else {
+ /* DMIC type input widget*/
+ mad_input_widget = tomtom_conn_mad_text[tomtom_mad_input];
+ }
+
+ pr_debug("%s: tomtom input widget = %s\n", __func__, mad_input_widget);
+
+ for (i = 0; i < card->num_dapm_routes; i++) {
+
+ if (!strcmp(card->dapm_routes[i].sink, mad_input_widget)) {
+
+ source_widget = card->dapm_routes[i].source;
+ if (!source_widget) {
+ dev_err(codec->dev,
+ "%s: invalid source widget\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (strnstr(source_widget,
+ "MIC BIAS1", sizeof("MIC BIAS1"))) {
+ mic_bias_found = 1;
+ micb_int_reg = TOMTOM_A_MICB_1_INT_RBIAS;
+ break;
+ } else if (strnstr(source_widget,
+ "MIC BIAS2", sizeof("MIC BIAS2"))) {
+ mic_bias_found = 2;
+ micb_int_reg = TOMTOM_A_MICB_2_INT_RBIAS;
+ break;
+ } else if (strnstr(source_widget,
+ "MIC BIAS3", sizeof("MIC BIAS3"))) {
+ mic_bias_found = 3;
+ micb_int_reg = TOMTOM_A_MICB_3_INT_RBIAS;
+ break;
+ } else if (strnstr(source_widget,
+ "MIC BIAS4", sizeof("MIC BIAS4"))) {
+ mic_bias_found = 4;
+ micb_int_reg = micb_4_int_reg;
+ break;
+ }
+ }
+ }
+
+ if (mic_bias_found) {
+ pr_debug("%s: source mic bias = %s. sink = %s\n", __func__,
+ card->dapm_routes[i].source,
+ card->dapm_routes[i].sink);
+
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_INP_SEL,
+ 0x0F, tomtom_mad_input);
+ snd_soc_update_bits(codec, TOMTOM_A_MAD_ANA_CTRL,
+ 0x07, mic_bias_found);
+
+ /* Setup internal micbias */
+
+ if (strnstr(source_widget, "Internal1", strlen(source_widget)))
+ snd_soc_update_bits(codec,
+ micb_int_reg,
+ 0xE0, 0xE0);
+ else if (strnstr(source_widget, "Internal2",
+ strlen(source_widget)))
+ snd_soc_update_bits(codec,
+ micb_int_reg,
+ 0x1C, 0x1C);
+ else if (strnstr(source_widget, "Internal3",
+ strlen(source_widget)))
+ snd_soc_update_bits(codec,
+ micb_int_reg,
+ 0x3, 0x3);
+ else
+ /*
+ * If not internal, make sure to write the
+ * register to default value
+ */
+ snd_soc_write(codec, micb_int_reg, 0x24);
+ return 0;
+ } else {
+ pr_err("%s: mic bias source not found for input = %s\n",
+ __func__, mad_input_widget);
+ return -EINVAL;
+ }
+}
+
+static int tomtom_tx_hpf_bypass_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 tx_index;
+
+ tx_index = (u32)kcontrol->private_value;
+
+ if (tx_index > NUM_DECIMATORS) {
+ pr_err("%s: Invalid TX decimator %d\n", __func__,
+ tx_index);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] =
+ tx_hpf_work[tx_index-1].tx_hpf_bypass;
+
+ return 0;
+}
+
+static int tomtom_tx_hpf_bypass_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ bool tx_hpf_bypass_cfg;
+ u32 tx_index;
+
+ tx_hpf_bypass_cfg = (bool)ucontrol->value.integer.value[0];
+
+ pr_debug("%s: tx_hpf_bypass = %d\n", __func__,
+ tx_hpf_bypass_cfg);
+
+ tx_index = (u32)kcontrol->private_value;
+
+ if (tx_index > NUM_DECIMATORS) {
+ pr_err("%s: Invalid TX decimator %d\n", __func__,
+ tx_index);
+ return -EINVAL;
+ }
+ if (tx_hpf_work[tx_index-1].tx_hpf_bypass != tx_hpf_bypass_cfg)
+ tx_hpf_work[tx_index-1].tx_hpf_bypass = tx_hpf_bypass_cfg;
+
+ pr_debug("%s: Set TX%d HPF bypass configuration %d",
+ __func__, tx_index,
+ tx_hpf_work[tx_index-1].tx_hpf_bypass);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new tomtom_snd_controls[] = {
+
+ SOC_SINGLE_SX_TLV("RX1 Digital Volume", TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX2 Digital Volume", TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX3 Digital Volume", TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX4 Digital Volume", TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX5 Digital Volume", TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX6 Digital Volume", TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX7 Digital Volume", TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX8 Digital Volume", TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL,
+ 0, -84, 40, digital_gain),
+
+ SOC_SINGLE_SX_TLV("DEC1 Volume", TOMTOM_A_CDC_TX1_VOL_CTL_GAIN, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC2 Volume", TOMTOM_A_CDC_TX2_VOL_CTL_GAIN, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC3 Volume", TOMTOM_A_CDC_TX3_VOL_CTL_GAIN, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC4 Volume", TOMTOM_A_CDC_TX4_VOL_CTL_GAIN, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC5 Volume", TOMTOM_A_CDC_TX5_VOL_CTL_GAIN, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC6 Volume", TOMTOM_A_CDC_TX6_VOL_CTL_GAIN, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC7 Volume", TOMTOM_A_CDC_TX7_VOL_CTL_GAIN, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC8 Volume", TOMTOM_A_CDC_TX8_VOL_CTL_GAIN, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC9 Volume", TOMTOM_A_CDC_TX9_VOL_CTL_GAIN, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC10 Volume", TOMTOM_A_CDC_TX10_VOL_CTL_GAIN, 0,
+ -84, 40, digital_gain),
+
+ SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", TOMTOM_A_CDC_IIR1_GAIN_B1_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", TOMTOM_A_CDC_IIR1_GAIN_B2_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", TOMTOM_A_CDC_IIR1_GAIN_B3_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP4 Volume", TOMTOM_A_CDC_IIR1_GAIN_B4_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR2 INP1 Volume", TOMTOM_A_CDC_IIR2_GAIN_B1_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR2 INP2 Volume", TOMTOM_A_CDC_IIR2_GAIN_B2_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR2 INP3 Volume", TOMTOM_A_CDC_IIR2_GAIN_B3_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR2 INP4 Volume", TOMTOM_A_CDC_IIR2_GAIN_B4_CTL, 0,
+ -84, 40, digital_gain),
+
+ SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tomtom_get_anc_slot,
+ tomtom_put_anc_slot),
+ SOC_ENUM_EXT("ANC Function", tomtom_anc_func_enum, tomtom_get_anc_func,
+ tomtom_put_anc_func),
+
+ SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
+ SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
+ SOC_ENUM("TX3 HPF cut off", cf_dec3_enum),
+ SOC_ENUM("TX4 HPF cut off", cf_dec4_enum),
+ SOC_ENUM("TX5 HPF cut off", cf_dec5_enum),
+ SOC_ENUM("TX6 HPF cut off", cf_dec6_enum),
+ SOC_ENUM("TX7 HPF cut off", cf_dec7_enum),
+ SOC_ENUM("TX8 HPF cut off", cf_dec8_enum),
+ SOC_ENUM("TX9 HPF cut off", cf_dec9_enum),
+ SOC_ENUM("TX10 HPF cut off", cf_dec10_enum),
+
+ SOC_SINGLE_BOOL_EXT("TX1 HPF Switch", 1,
+ tomtom_tx_hpf_bypass_get,
+ tomtom_tx_hpf_bypass_put),
+ SOC_SINGLE_BOOL_EXT("TX2 HPF Switch", 2,
+ tomtom_tx_hpf_bypass_get,
+ tomtom_tx_hpf_bypass_put),
+ SOC_SINGLE_BOOL_EXT("TX3 HPF Switch", 3,
+ tomtom_tx_hpf_bypass_get,
+ tomtom_tx_hpf_bypass_put),
+ SOC_SINGLE_BOOL_EXT("TX4 HPF Switch", 4,
+ tomtom_tx_hpf_bypass_get,
+ tomtom_tx_hpf_bypass_put),
+ SOC_SINGLE_BOOL_EXT("TX5 HPF Switch", 5,
+ tomtom_tx_hpf_bypass_get,
+ tomtom_tx_hpf_bypass_put),
+ SOC_SINGLE_BOOL_EXT("TX6 HPF Switch", 6,
+ tomtom_tx_hpf_bypass_get,
+ tomtom_tx_hpf_bypass_put),
+ SOC_SINGLE_BOOL_EXT("TX7 HPF Switch", 7,
+ tomtom_tx_hpf_bypass_get,
+ tomtom_tx_hpf_bypass_put),
+ SOC_SINGLE_BOOL_EXT("TX8 HPF Switch", 8,
+ tomtom_tx_hpf_bypass_get,
+ tomtom_tx_hpf_bypass_put),
+ SOC_SINGLE_BOOL_EXT("TX9 HPF Switch", 9,
+ tomtom_tx_hpf_bypass_get,
+ tomtom_tx_hpf_bypass_put),
+ SOC_SINGLE_BOOL_EXT("TX10 HPF Switch", 10,
+ tomtom_tx_hpf_bypass_get,
+ tomtom_tx_hpf_bypass_put),
+
+ SOC_SINGLE("RX1 HPF Switch", TOMTOM_A_CDC_RX1_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX2 HPF Switch", TOMTOM_A_CDC_RX2_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX3 HPF Switch", TOMTOM_A_CDC_RX3_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX4 HPF Switch", TOMTOM_A_CDC_RX4_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX5 HPF Switch", TOMTOM_A_CDC_RX5_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX6 HPF Switch", TOMTOM_A_CDC_RX6_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX7 HPF Switch", TOMTOM_A_CDC_RX7_B5_CTL, 2, 1, 0),
+ SOC_SINGLE("RX8 HPF Switch", TOMTOM_A_CDC_RX8_B5_CTL, 2, 1, 0),
+
+ SOC_ENUM("RX1 HPF cut off", cf_rxmix1_enum),
+ SOC_ENUM("RX2 HPF cut off", cf_rxmix2_enum),
+ SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum),
+ SOC_ENUM("RX4 HPF cut off", cf_rxmix4_enum),
+ SOC_ENUM("RX5 HPF cut off", cf_rxmix5_enum),
+ SOC_ENUM("RX6 HPF cut off", cf_rxmix6_enum),
+ SOC_ENUM("RX7 HPF cut off", cf_rxmix7_enum),
+ SOC_ENUM("RX8 HPF cut off", cf_rxmix8_enum),
+
+ SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0,
+ tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0,
+ tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0,
+ tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0,
+ tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0,
+ tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band1", IIR2, BAND1, 1, 0,
+ tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band2", IIR2, BAND2, 1, 0,
+ tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band3", IIR2, BAND3, 1, 0,
+ tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band4", IIR2, BAND4, 1, 0,
+ tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band5", IIR2, BAND5, 1, 0,
+ tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+
+ SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5,
+ tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5,
+ tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5,
+ tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5,
+ tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5,
+ tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band1", IIR2, BAND1, 255, 0, 5,
+ tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band2", IIR2, BAND2, 255, 0, 5,
+ tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band3", IIR2, BAND3, 255, 0, 5,
+ tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band4", IIR2, BAND4, 255, 0, 5,
+ tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5,
+ tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+
+ SOC_SINGLE_EXT("COMP0 Switch", SND_SOC_NOPM, COMPANDER_0, 1, 0,
+ tomtom_get_compander, tomtom_set_compander),
+ SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0,
+ tomtom_get_compander, tomtom_set_compander),
+ SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
+ tomtom_get_compander, tomtom_set_compander),
+
+ SOC_ENUM_EXT("MAD Input", tomtom_conn_mad_enum,
+ tomtom_mad_input_get, tomtom_mad_input_put),
+
+};
+
+static int tomtom_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 ear_pa_gain;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ ear_pa_gain = snd_soc_read(codec, TOMTOM_A_RX_EAR_GAIN);
+
+ ear_pa_gain = ear_pa_gain >> 5;
+
+ ucontrol->value.integer.value[0] = ear_pa_gain;
+
+ pr_debug("%s: ear_pa_gain = 0x%x\n", __func__, ear_pa_gain);
+
+ return 0;
+}
+
+static int tomtom_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 ear_pa_gain;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ pr_debug("%s: ucontrol->value.integer.value[0] = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ ear_pa_gain = ucontrol->value.integer.value[0] << 5;
+
+ snd_soc_update_bits(codec, TOMTOM_A_RX_EAR_GAIN, 0xE0, ear_pa_gain);
+ return 0;
+}
+
+static const char * const tomtom_1_x_ear_pa_gain_text[] = {
+ "POS_6_DB", "POS_4P5_DB", "POS_3_DB", "POS_1P5_DB",
+ "POS_0_DB", "NEG_2P5_DB", "UNDEFINED", "NEG_12_DB"
+};
+
+static const struct soc_enum tomtom_1_x_ear_pa_gain_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tomtom_1_x_ear_pa_gain_text),
+ tomtom_1_x_ear_pa_gain_text);
+
+static const struct snd_kcontrol_new tomtom_1_x_analog_gain_controls[] = {
+
+ SOC_ENUM_EXT("EAR PA Gain", tomtom_1_x_ear_pa_gain_enum,
+ tomtom_pa_gain_get, tomtom_pa_gain_put),
+
+ SOC_SINGLE_TLV("HPHL Volume", TOMTOM_A_RX_HPH_L_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", TOMTOM_A_RX_HPH_R_GAIN, 0, 20, 1,
+ line_gain),
+
+ SOC_SINGLE_TLV("LINEOUT1 Volume", TOMTOM_A_RX_LINE_1_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("LINEOUT2 Volume", TOMTOM_A_RX_LINE_2_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("LINEOUT3 Volume", TOMTOM_A_RX_LINE_3_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("LINEOUT4 Volume", TOMTOM_A_RX_LINE_4_GAIN, 0, 20, 1,
+ line_gain),
+
+ SOC_SINGLE_TLV("SPK DRV Volume", TOMTOM_A_SPKR_DRV1_GAIN, 3, 8, 1,
+ line_gain),
+ SOC_SINGLE_TLV("SPK DRV2 Volume", TOMTOM_A_SPKR_DRV2_GAIN, 3, 8, 1,
+ line_gain),
+
+ SOC_SINGLE_TLV("ADC1 Volume", TOMTOM_A_TX_1_GAIN, 2, 19, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", TOMTOM_A_TX_2_GAIN, 2, 19, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", TOMTOM_A_TX_3_GAIN, 2, 19, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC4 Volume", TOMTOM_A_TX_4_GAIN, 2, 19, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC5 Volume", TOMTOM_A_TX_5_GAIN, 2, 19, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC6 Volume", TOMTOM_A_TX_6_GAIN, 2, 19, 0,
+ analog_gain),
+};
+
+static int tomtom_hph_impedance_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t zl, zr;
+ bool hphr;
+ struct soc_multi_mixer_control *mc;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+
+ hphr = mc->shift;
+ wcd9xxx_mbhc_get_impedance(&priv->mbhc, &zl, &zr);
+ pr_debug("%s: zl %u, zr %u\n", __func__, zl, zr);
+ ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+ SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+ tomtom_hph_impedance_get, NULL),
+ SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+ tomtom_hph_impedance_get, NULL),
+};
+
+static int tomtom_get_hph_type(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_mbhc *mbhc;
+
+ if (!priv) {
+ pr_debug("%s: wcd9330 private data is NULL\n", __func__);
+ return 0;
+ }
+
+ mbhc = &priv->mbhc;
+ if (!mbhc) {
+ pr_debug("%s: mbhc not initialized\n", __func__);
+ return 0;
+ }
+
+ ucontrol->value.integer.value[0] = (u32) mbhc->hph_type;
+ pr_debug("%s: hph_type = %u\n", __func__, mbhc->hph_type);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+ SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+ tomtom_get_hph_type, NULL),
+};
+
+static const char * const rx_mix1_text[] = {
+ "ZERO", "SRC1", "SRC2", "IIR1", "IIR2", "RX1", "RX2", "RX3", "RX4",
+ "RX5", "RX6", "RX7"
+};
+
+static const char * const rx8_mix1_text[] = {
+ "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3", "RX4",
+ "RX5", "RX6", "RX7", "RX8"
+};
+
+static const char * const rx_mix2_text[] = {
+ "ZERO", "SRC1", "SRC2", "IIR1", "IIR2"
+};
+
+static const char * const rx_rdac5_text[] = {
+ "DEM4", "DEM3_INV"
+};
+
+static const char * const rx_rdac7_text[] = {
+ "DEM6", "DEM5_INV"
+};
+
+static const char * const mad_sel_text[] = {
+ "SPE", "MSM"
+};
+
+static const char * const sb_tx1_mux_text[] = {
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+ "DEC1", "RMIX8"
+};
+
+static const char * const sb_tx2_mux_text[] = {
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+ "DEC2", "RMIX8"
+};
+
+static const char * const sb_tx3_mux_text[] = {
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+ "DEC3", "RMIX8"
+};
+
+static const char * const sb_tx4_mux_text[] = {
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+ "DEC4", "RMIX8"
+};
+
+static const char * const sb_tx5_mux_text[] = {
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+ "DEC5", "RMIX8"
+};
+
+static const char * const sb_tx6_mux_text[] = {
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+ "DEC6", "RMIX8"
+};
+
+static const char * const sb_tx7_to_tx10_mux_text[] = {
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+ "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+ "DEC9", "DEC10"
+};
+
+static const char * const dec1_mux_text[] = {
+ "ZERO", "DMIC1", "ADC6",
+};
+
+static const char * const dec2_mux_text[] = {
+ "ZERO", "DMIC2", "ADC5",
+};
+
+static const char * const dec3_mux_text[] = {
+ "ZERO", "DMIC3", "ADC4",
+};
+
+static const char * const dec4_mux_text[] = {
+ "ZERO", "DMIC4", "ADC3",
+};
+
+static const char * const dec5_mux_text[] = {
+ "ZERO", "DMIC5", "ADC2",
+};
+
+static const char * const dec6_mux_text[] = {
+ "ZERO", "DMIC6", "ADC1",
+};
+
+static const char * const dec7_mux_text[] = {
+ "ZERO", "DMIC1", "DMIC6", "ADC1", "ADC6", "ANC1_FB", "ANC2_FB",
+};
+
+static const char * const dec8_mux_text[] = {
+ "ZERO", "DMIC2", "DMIC5", "ADC2", "ADC5", "ANC1_FB", "ANC2_FB",
+};
+
+static const char * const dec9_mux_text[] = {
+ "ZERO", "DMIC4", "DMIC5", "ADC2", "ADC3", "ADCMB", "ANC1_FB", "ANC2_FB",
+};
+
+static const char * const dec10_mux_text[] = {
+ "ZERO", "DMIC3", "DMIC6", "ADC1", "ADC4", "ADCMB", "ANC1_FB", "ANC2_FB",
+};
+
+static const char * const anc_mux_text[] = {
+ "ZERO", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6", "ADC_MB",
+ "RSVD_1", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6"
+};
+
+static const char * const anc1_fb_mux_text[] = {
+ "ZERO", "EAR_HPH_L", "EAR_LINE_1",
+};
+
+static const char * const iir_inp1_text[] = {
+ "ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+ "DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const iir_inp2_text[] = {
+ "ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+ "DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const iir_inp3_text[] = {
+ "ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+ "DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const iir_inp4_text[] = {
+ "ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+ "DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const struct soc_enum rx_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx_mix1_inp3_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B2_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx2_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx2_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx3_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX3_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx3_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX3_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx4_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX4_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx4_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX4_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx5_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX5_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx5_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX5_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx6_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX6_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx6_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX6_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx7_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx7_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx8_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX8_B1_CTL, 0, 11, rx8_mix1_text);
+
+static const struct soc_enum rx8_mix1_inp2_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX8_B1_CTL, 4, 11, rx8_mix1_text);
+
+static const struct soc_enum rx1_mix2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B3_CTL, 0, 5, rx_mix2_text);
+
+static const struct soc_enum rx1_mix2_inp2_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B3_CTL, 3, 5, rx_mix2_text);
+
+static const struct soc_enum rx2_mix2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B3_CTL, 0, 5, rx_mix2_text);
+
+static const struct soc_enum rx2_mix2_inp2_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B3_CTL, 3, 5, rx_mix2_text);
+
+static const struct soc_enum rx7_mix2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B3_CTL, 0, 5, rx_mix2_text);
+
+static const struct soc_enum rx7_mix2_inp2_chain_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B3_CTL, 3, 5, rx_mix2_text);
+
+static const struct soc_enum rx_rdac5_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_MISC, 2, 2, rx_rdac5_text);
+
+static const struct soc_enum rx_rdac7_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_MISC, 1, 2, rx_rdac7_text);
+
+static const struct soc_enum mad_sel_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_SVASS_CFG, 0, 2, mad_sel_text);
+
+static const struct soc_enum sb_tx1_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B1_CTL, 0, 10, sb_tx1_mux_text);
+
+static const struct soc_enum sb_tx2_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B2_CTL, 0, 10, sb_tx2_mux_text);
+
+static const struct soc_enum sb_tx3_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B3_CTL, 0, 10, sb_tx3_mux_text);
+
+static const struct soc_enum sb_tx4_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B4_CTL, 0, 10, sb_tx4_mux_text);
+
+static const struct soc_enum sb_tx5_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B5_CTL, 0, 10, sb_tx5_mux_text);
+
+static const struct soc_enum sb_tx6_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B6_CTL, 0, 10, sb_tx6_mux_text);
+
+static const struct soc_enum sb_tx7_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B7_CTL, 0, 18,
+ sb_tx7_to_tx10_mux_text);
+
+static const struct soc_enum sb_tx8_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B8_CTL, 0, 18,
+ sb_tx7_to_tx10_mux_text);
+
+static const struct soc_enum sb_tx9_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B9_CTL, 0, 18,
+ sb_tx7_to_tx10_mux_text);
+
+static const struct soc_enum sb_tx10_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B10_CTL, 0, 18,
+ sb_tx7_to_tx10_mux_text);
+
+static const struct soc_enum dec1_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 0, 3, dec1_mux_text);
+
+static const struct soc_enum dec2_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 2, 3, dec2_mux_text);
+
+static const struct soc_enum dec3_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 4, 3, dec3_mux_text);
+
+static const struct soc_enum dec4_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 6, 3, dec4_mux_text);
+
+static const struct soc_enum dec5_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B2_CTL, 0, 3, dec5_mux_text);
+
+static const struct soc_enum dec6_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B2_CTL, 2, 3, dec6_mux_text);
+
+static const struct soc_enum dec7_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B2_CTL, 4, 7, dec7_mux_text);
+
+static const struct soc_enum dec8_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B3_CTL, 0, 7, dec8_mux_text);
+
+static const struct soc_enum dec9_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B3_CTL, 3, 8, dec9_mux_text);
+
+static const struct soc_enum dec10_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B4_CTL, 0, 8, dec10_mux_text);
+
+static const struct soc_enum anc1_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_ANC_B1_CTL, 0, 15, anc_mux_text);
+
+static const struct soc_enum anc2_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_ANC_B1_CTL, 4, 15, anc_mux_text);
+
+static const struct soc_enum anc1_fb_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_ANC_B2_CTL, 0, 3, anc1_fb_mux_text);
+
+static const struct soc_enum iir1_inp1_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B1_CTL, 0, 18, iir_inp1_text);
+
+static const struct soc_enum iir2_inp1_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B1_CTL, 0, 18, iir_inp1_text);
+
+static const struct soc_enum iir1_inp2_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B2_CTL, 0, 18, iir_inp2_text);
+
+static const struct soc_enum iir2_inp2_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B2_CTL, 0, 18, iir_inp2_text);
+
+static const struct soc_enum iir1_inp3_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B3_CTL, 0, 18, iir_inp3_text);
+
+static const struct soc_enum iir2_inp3_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B3_CTL, 0, 18, iir_inp3_text);
+
+static const struct soc_enum iir1_inp4_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B4_CTL, 0, 18, iir_inp4_text);
+
+static const struct soc_enum iir2_inp4_mux_enum =
+ SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B4_CTL, 0, 18, iir_inp4_text);
+
+static const struct snd_kcontrol_new rx_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_mix1_inp3_mux =
+ SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp3_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX2 MIX1 INP2 Mux", rx2_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx3_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx3_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx4_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX4 MIX1 INP1 Mux", rx4_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx4_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX4 MIX1 INP2 Mux", rx4_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx5_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX5 MIX1 INP1 Mux", rx5_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx5_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX5 MIX1 INP2 Mux", rx5_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx6_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX6 MIX1 INP1 Mux", rx6_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx6_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX6 MIX1 INP2 Mux", rx6_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx7_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX7 MIX1 INP1 Mux", rx7_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx7_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX7 MIX1 INP2 Mux", rx7_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx8_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX8 MIX1 INP1 Mux", rx8_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx8_mix1_inp2_mux =
+ SOC_DAPM_ENUM("RX8 MIX1 INP2 Mux", rx8_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx1_mix2_inp1_mux =
+ SOC_DAPM_ENUM("RX1 MIX2 INP1 Mux", rx1_mix2_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx1_mix2_inp2_mux =
+ SOC_DAPM_ENUM("RX1 MIX2 INP2 Mux", rx1_mix2_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix2_inp1_mux =
+ SOC_DAPM_ENUM("RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix2_inp2_mux =
+ SOC_DAPM_ENUM("RX2 MIX2 INP2 Mux", rx2_mix2_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx7_mix2_inp1_mux =
+ SOC_DAPM_ENUM("RX7 MIX2 INP1 Mux", rx7_mix2_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx7_mix2_inp2_mux =
+ SOC_DAPM_ENUM("RX7 MIX2 INP2 Mux", rx7_mix2_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_dac5_mux =
+ SOC_DAPM_ENUM("RDAC5 MUX Mux", rx_rdac5_enum);
+
+static const struct snd_kcontrol_new rx_dac7_mux =
+ SOC_DAPM_ENUM("RDAC7 MUX Mux", rx_rdac7_enum);
+
+static const struct snd_kcontrol_new mad_sel_mux =
+ SOC_DAPM_ENUM("MAD_SEL MUX Mux", mad_sel_enum);
+
+static const struct snd_kcontrol_new sb_tx1_mux =
+ SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx2_mux =
+ SOC_DAPM_ENUM("SLIM TX2 MUX Mux", sb_tx2_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx3_mux =
+ SOC_DAPM_ENUM("SLIM TX3 MUX Mux", sb_tx3_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx4_mux =
+ SOC_DAPM_ENUM("SLIM TX4 MUX Mux", sb_tx4_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx5_mux =
+ SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx6_mux =
+ SOC_DAPM_ENUM("SLIM TX6 MUX Mux", sb_tx6_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx7_mux =
+ SOC_DAPM_ENUM("SLIM TX7 MUX Mux", sb_tx7_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx8_mux =
+ SOC_DAPM_ENUM("SLIM TX8 MUX Mux", sb_tx8_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx9_mux =
+ SOC_DAPM_ENUM("SLIM TX9 MUX Mux", sb_tx9_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx10_mux =
+ SOC_DAPM_ENUM("SLIM TX10 MUX Mux", sb_tx10_mux_enum);
+
+
+static int wcd9330_put_dec_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *w = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int dec_mux, decimator;
+ char *dec_name = NULL;
+ char *widget_name = NULL;
+ char *temp;
+ u16 tx_mux_ctl_reg;
+ u8 adc_dmic_sel = 0x0;
+ int ret = 0;
+ char *dec;
+
+ if (ucontrol->value.enumerated.item[0] >= e->items)
+ return -EINVAL;
+
+ dec_mux = ucontrol->value.enumerated.item[0];
+
+ widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+ if (!widget_name)
+ return -ENOMEM;
+ temp = widget_name;
+
+ dec_name = strsep(&widget_name, " ");
+ widget_name = temp;
+ if (!dec_name) {
+ pr_err("%s: Invalid decimator = %s\n", __func__, w->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ dec = strpbrk(dec_name, "123456789");
+ if (!dec) {
+ dev_err(w->dapm->dev, "%s: decimator index not found\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = kstrtouint(dec, 10, &decimator);
+ if (ret < 0) {
+ pr_err("%s: Invalid decimator = %s\n", __func__, dec_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dev_dbg(w->dapm->dev, "%s(): widget = %s decimator = %u dec_mux = %u\n"
+ , __func__, w->name, decimator, dec_mux);
+
+
+ switch (decimator) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ if (dec_mux == 1)
+ adc_dmic_sel = 0x1;
+ else
+ adc_dmic_sel = 0x0;
+ break;
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ if ((dec_mux == 1) || (dec_mux == 2))
+ adc_dmic_sel = 0x1;
+ else
+ adc_dmic_sel = 0x0;
+ break;
+ default:
+ pr_err("%s: Invalid Decimator = %u\n", __func__, decimator);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ tx_mux_ctl_reg = TOMTOM_A_CDC_TX1_MUX_CTL + 8 * (decimator - 1);
+
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x1, adc_dmic_sel);
+
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+out:
+ kfree(widget_name);
+ return ret;
+}
+
+#define WCD9330_DEC_ENUM(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_dapm_get_enum_double, \
+ .put = wcd9330_put_dec_enum, \
+ .private_value = (unsigned long)&xenum }
+
+static const struct snd_kcontrol_new dec1_mux =
+ WCD9330_DEC_ENUM("DEC1 MUX Mux", dec1_mux_enum);
+
+static const struct snd_kcontrol_new dec2_mux =
+ WCD9330_DEC_ENUM("DEC2 MUX Mux", dec2_mux_enum);
+
+static const struct snd_kcontrol_new dec3_mux =
+ WCD9330_DEC_ENUM("DEC3 MUX Mux", dec3_mux_enum);
+
+static const struct snd_kcontrol_new dec4_mux =
+ WCD9330_DEC_ENUM("DEC4 MUX Mux", dec4_mux_enum);
+
+static const struct snd_kcontrol_new dec5_mux =
+ WCD9330_DEC_ENUM("DEC5 MUX Mux", dec5_mux_enum);
+
+static const struct snd_kcontrol_new dec6_mux =
+ WCD9330_DEC_ENUM("DEC6 MUX Mux", dec6_mux_enum);
+
+static const struct snd_kcontrol_new dec7_mux =
+ WCD9330_DEC_ENUM("DEC7 MUX Mux", dec7_mux_enum);
+
+static const struct snd_kcontrol_new dec8_mux =
+ WCD9330_DEC_ENUM("DEC8 MUX Mux", dec8_mux_enum);
+
+static const struct snd_kcontrol_new dec9_mux =
+ WCD9330_DEC_ENUM("DEC9 MUX Mux", dec9_mux_enum);
+
+static const struct snd_kcontrol_new dec10_mux =
+ WCD9330_DEC_ENUM("DEC10 MUX Mux", dec10_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp1_mux =
+ SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum);
+
+static const struct snd_kcontrol_new iir2_inp1_mux =
+ SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp2_mux =
+ SOC_DAPM_ENUM("IIR1 INP2 Mux", iir1_inp2_mux_enum);
+
+static const struct snd_kcontrol_new iir2_inp2_mux =
+ SOC_DAPM_ENUM("IIR2 INP2 Mux", iir2_inp2_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp3_mux =
+ SOC_DAPM_ENUM("IIR1 INP3 Mux", iir1_inp3_mux_enum);
+
+static const struct snd_kcontrol_new iir2_inp3_mux =
+ SOC_DAPM_ENUM("IIR2 INP3 Mux", iir2_inp3_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp4_mux =
+ SOC_DAPM_ENUM("IIR1 INP4 Mux", iir1_inp4_mux_enum);
+
+static const struct snd_kcontrol_new iir2_inp4_mux =
+ SOC_DAPM_ENUM("IIR2 INP4 Mux", iir2_inp4_mux_enum);
+
+static const struct snd_kcontrol_new anc1_mux =
+ SOC_DAPM_ENUM("ANC1 MUX Mux", anc1_mux_enum);
+
+static const struct snd_kcontrol_new anc2_mux =
+ SOC_DAPM_ENUM("ANC2 MUX Mux", anc2_mux_enum);
+
+static const struct snd_kcontrol_new anc1_fb_mux =
+ SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum);
+
+static const struct snd_kcontrol_new dac1_switch[] = {
+ SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_EAR_EN, 5, 1, 0)
+};
+static const struct snd_kcontrol_new hphl_switch[] = {
+ SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_HPH_L_DAC_CTL, 6, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphl_pa_mix[] = {
+ SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+ 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new hphr_pa_mix[] = {
+ SOC_DAPM_SINGLE("AUX_PGA_R Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+ 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new ear_pa_mix[] = {
+ SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+ 5, 1, 0),
+};
+static const struct snd_kcontrol_new lineout1_pa_mix[] = {
+ SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+ 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new lineout2_pa_mix[] = {
+ SOC_DAPM_SINGLE("AUX_PGA_R Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+ 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new lineout3_pa_mix[] = {
+ SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+ 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new lineout4_pa_mix[] = {
+ SOC_DAPM_SINGLE("AUX_PGA_R Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+ 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new lineout3_ground_switch =
+ SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_LINE_3_DAC_CTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new lineout4_ground_switch =
+ SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new aif4_mad_switch =
+ SOC_DAPM_SINGLE("Switch", TOMTOM_A_SVASS_CLKRST_CTL, 0, 1, 0);
+
+static const struct snd_kcontrol_new aif4_vi_switch =
+ SOC_DAPM_SINGLE("Switch", TOMTOM_A_SPKR1_PROT_EN, 3, 1, 0);
+
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tomtom_p->tx_port_value;
+ return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct snd_soc_dapm_update *update = NULL;
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+ u32 vtable = vport_check_table[dai_id];
+
+
+ pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+ widget->name, ucontrol->id.name, tomtom_p->tx_port_value,
+ widget->shift, ucontrol->value.integer.value[0]);
+
+ mutex_lock(&tomtom_p->codec_mutex);
+
+ if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (dai_id != AIF1_CAP) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&tomtom_p->codec_mutex);
+ return -EINVAL;
+ }
+ }
+ switch (dai_id) {
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ /* only add to the list if value not set
+ */
+ if (enable && !(tomtom_p->tx_port_value & 1 << port_id)) {
+
+ if (tomtom_p->intf_type ==
+ WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ vtable = vport_check_table[dai_id];
+ if (tomtom_p->intf_type ==
+ WCD9XXX_INTERFACE_TYPE_I2C)
+ vtable = vport_i2s_check_table[dai_id];
+
+ if (wcd9xxx_tx_vport_validation(
+ vtable,
+ port_id,
+ tomtom_p->dai, NUM_CODEC_DAIS)) {
+ dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
+ __func__, port_id + 1);
+ mutex_unlock(&tomtom_p->codec_mutex);
+ return 0;
+ }
+ tomtom_p->tx_port_value |= 1 << port_id;
+ list_add_tail(&core->tx_chs[port_id].list,
+ &tomtom_p->dai[dai_id].wcd9xxx_ch_list
+ );
+ } else if (!enable && (tomtom_p->tx_port_value &
+ 1 << port_id)) {
+ tomtom_p->tx_port_value &= ~(1 << port_id);
+ list_del_init(&core->tx_chs[port_id].list);
+ } else {
+ if (enable)
+ dev_dbg(codec->dev, "%s: TX%u port is used by\n"
+ "this virtual port\n",
+ __func__, port_id + 1);
+ else
+ dev_dbg(codec->dev, "%s: TX%u port is not used by\n"
+ "this virtual port\n",
+ __func__, port_id + 1);
+ /* avoid update power function */
+ mutex_unlock(&tomtom_p->codec_mutex);
+ return 0;
+ }
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", dai_id);
+ mutex_unlock(&tomtom_p->codec_mutex);
+ return -EINVAL;
+ }
+ pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+ widget->name, widget->sname, tomtom_p->tx_port_value,
+ widget->shift);
+
+ mutex_unlock(&tomtom_p->codec_mutex);
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
+
+ return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = tomtom_p->rx_port_value;
+ return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+ u32 port_id = widget->shift;
+
+ pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+ widget->name, ucontrol->id.name, tomtom_p->rx_port_value,
+ widget->shift, ucontrol->value.integer.value[0]);
+
+ tomtom_p->rx_port_value = ucontrol->value.enumerated.item[0];
+
+ mutex_lock(&tomtom_p->codec_mutex);
+
+ if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (tomtom_p->rx_port_value > 2) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ goto err;
+ }
+ }
+ /* value need to match the Virtual port and AIF number
+ */
+ switch (tomtom_p->rx_port_value) {
+ case 0:
+ list_del_init(&core->rx_chs[port_id].list);
+ break;
+ case 1:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TOMTOM_RX_PORT_START_NUMBER,
+ &tomtom_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tomtom_p->dai[AIF1_PB].wcd9xxx_ch_list);
+ break;
+ case 2:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TOMTOM_RX_PORT_START_NUMBER,
+ &tomtom_p->dai[AIF2_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tomtom_p->dai[AIF2_PB].wcd9xxx_ch_list);
+ break;
+ case 3:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TOMTOM_RX_PORT_START_NUMBER,
+ &tomtom_p->dai[AIF3_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tomtom_p->dai[AIF3_PB].wcd9xxx_ch_list);
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", tomtom_p->rx_port_value);
+ goto err;
+ }
+rtn:
+ mutex_unlock(&tomtom_p->codec_mutex);
+ snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+ tomtom_p->rx_port_value, e, update);
+
+ return 0;
+err:
+ mutex_unlock(&tomtom_p->codec_mutex);
+ return -EINVAL;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TOMTOM_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX8 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif1_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TOMTOM_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TOMTOM_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TOMTOM_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TOMTOM_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TOMTOM_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TOMTOM_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TOMTOM_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TOMTOM_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TOMTOM_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TOMTOM_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif2_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TOMTOM_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TOMTOM_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TOMTOM_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TOMTOM_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TOMTOM_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TOMTOM_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TOMTOM_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TOMTOM_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TOMTOM_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TOMTOM_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif3_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TOMTOM_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TOMTOM_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TOMTOM_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TOMTOM_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TOMTOM_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TOMTOM_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TOMTOM_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TOMTOM_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TOMTOM_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TOMTOM_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static void tomtom_codec_enable_adc_block(struct snd_soc_codec *codec,
+ int enable)
+{
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s %d\n", __func__, enable);
+
+ if (enable) {
+ tomtom->adc_count++;
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+ 0x2, 0x2);
+ } else {
+ tomtom->adc_count--;
+ if (!tomtom->adc_count)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+ 0x2, 0x0);
+ }
+}
+
+static int tomtom_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+ u16 adc_reg;
+ u16 tx_fe_clkdiv_reg;
+ u8 tx_fe_clkdiv_mask;
+ u8 init_bit_shift;
+ u8 bit_pos;
+
+ pr_debug("%s %d\n", __func__, event);
+
+ switch (w->reg) {
+ case TOMTOM_A_TX_1_GAIN:
+ adc_reg = TOMTOM_A_TX_1_2_TEST_CTL;
+ tx_fe_clkdiv_reg = TOMTOM_A_TX_1_2_TXFE_CLKDIV;
+ tx_fe_clkdiv_mask = 0x0F;
+ init_bit_shift = 7;
+ bit_pos = ADC1_TXFE;
+ break;
+ case TOMTOM_A_TX_2_GAIN:
+ adc_reg = TOMTOM_A_TX_1_2_TEST_CTL;
+ tx_fe_clkdiv_reg = TOMTOM_A_TX_1_2_TXFE_CLKDIV;
+ tx_fe_clkdiv_mask = 0xF0;
+ init_bit_shift = 6;
+ bit_pos = ADC2_TXFE;
+ break;
+ case TOMTOM_A_TX_3_GAIN:
+ adc_reg = TOMTOM_A_TX_3_4_TEST_CTL;
+ init_bit_shift = 7;
+ tx_fe_clkdiv_reg = TOMTOM_A_TX_3_4_TXFE_CKDIV;
+ tx_fe_clkdiv_mask = 0x0F;
+ bit_pos = ADC3_TXFE;
+ break;
+ case TOMTOM_A_TX_4_GAIN:
+ adc_reg = TOMTOM_A_TX_3_4_TEST_CTL;
+ init_bit_shift = 6;
+ tx_fe_clkdiv_reg = TOMTOM_A_TX_3_4_TXFE_CKDIV;
+ tx_fe_clkdiv_mask = 0xF0;
+ bit_pos = ADC4_TXFE;
+ break;
+ case TOMTOM_A_TX_5_GAIN:
+ adc_reg = TOMTOM_A_TX_5_6_TEST_CTL;
+ init_bit_shift = 7;
+ tx_fe_clkdiv_reg = TOMTOM_A_TX_5_6_TXFE_CKDIV;
+ tx_fe_clkdiv_mask = 0x0F;
+ bit_pos = ADC5_TXFE;
+ break;
+ case TOMTOM_A_TX_6_GAIN:
+ adc_reg = TOMTOM_A_TX_5_6_TEST_CTL;
+ init_bit_shift = 6;
+ tx_fe_clkdiv_reg = TOMTOM_A_TX_5_6_TXFE_CKDIV;
+ tx_fe_clkdiv_mask = 0xF0;
+ bit_pos = ADC6_TXFE;
+ break;
+ default:
+ pr_err("%s: Error, invalid adc register\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, tx_fe_clkdiv_reg, tx_fe_clkdiv_mask,
+ 0x0);
+ set_bit(bit_pos, &priv->status_mask);
+ tomtom_codec_enable_adc_block(codec, 1);
+ snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift,
+ 1 << init_bit_shift);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tomtom_codec_enable_adc_block(codec, 0);
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_codec_ext_clk_en(struct snd_soc_codec *codec,
+ int enable, bool dapm)
+{
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ if (!tomtom->codec_ext_clk_en_cb) {
+ dev_err(codec->dev,
+ "%s: Invalid ext_clk_callback\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return tomtom->codec_ext_clk_en_cb(codec, enable, dapm);
+}
+
+static int __tomtom_mclk_enable(struct tomtom_priv *tomtom, int mclk_enable)
+{
+ int ret = 0;
+
+ WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+ if (mclk_enable) {
+ tomtom->ext_clk_users++;
+ if (tomtom->ext_clk_users > 1)
+ goto bg_clk_unlock;
+ ret = clk_prepare_enable(tomtom->wcd_ext_clk);
+ if (ret) {
+ pr_err("%s: ext clk enable failed\n",
+ __func__);
+ tomtom->ext_clk_users--;
+ goto bg_clk_unlock;
+ }
+ wcd9xxx_resmgr_get_bandgap(&tomtom->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr, WCD9XXX_CLK_MCLK);
+ } else {
+ tomtom->ext_clk_users--;
+ if (tomtom->ext_clk_users == 0) {
+ /* Put clock and BG */
+ wcd9xxx_resmgr_put_clk_block(&tomtom->resmgr,
+ WCD9XXX_CLK_MCLK);
+ wcd9xxx_resmgr_put_bandgap(&tomtom->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ clk_disable_unprepare(tomtom->wcd_ext_clk);
+ }
+ }
+bg_clk_unlock:
+ WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+
+ return ret;
+}
+
+int tomtom_codec_mclk_enable(struct snd_soc_codec *codec,
+ int enable, bool dapm)
+{
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ if (tomtom->wcd_ext_clk) {
+ dev_dbg(codec->dev, "%s: mclk_enable = %u, dapm = %d\n",
+ __func__, enable, dapm);
+ return __tomtom_mclk_enable(tomtom, enable);
+ } else if (tomtom->codec_ext_clk_en_cb)
+ return tomtom_codec_ext_clk_en(codec, enable, dapm);
+ else {
+ dev_err(codec->dev,
+ "%s: Cannot turn on MCLK\n",
+ __func__);
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(tomtom_codec_mclk_enable);
+
+static int tomtom_codec_get_ext_clk_users(struct tomtom_priv *tomtom)
+{
+ if (tomtom->wcd_ext_clk)
+ return tomtom->ext_clk_users;
+ else if (tomtom->codec_get_ext_clk_cnt)
+ return tomtom->codec_get_ext_clk_cnt();
+ else
+ return 0;
+}
+
+/* tomtom_codec_internal_rco_ctrl( )
+ * Make sure that BG_CLK_LOCK is not acquired. Exit if acquired to avoid
+ * potential deadlock as ext_clk_en_cb() also tries to acquire the same
+ * lock to enable MCLK for RCO calibration
+ */
+static int tomtom_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+ bool enable)
+{
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if (enable) {
+ if (wcd9xxx_resmgr_get_clk_type(&tomtom->resmgr) ==
+ WCD9XXX_CLK_RCO) {
+ WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+ wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr,
+ WCD9XXX_CLK_RCO);
+ WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+ } else {
+ tomtom_codec_mclk_enable(codec, true, false);
+ WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+ tomtom->resmgr.ext_clk_users =
+ tomtom_codec_get_ext_clk_users(tomtom);
+ wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr,
+ WCD9XXX_CLK_RCO);
+ WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+ tomtom_codec_mclk_enable(codec, false, false);
+ }
+
+ } else {
+ WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+ wcd9xxx_resmgr_put_clk_block(&tomtom->resmgr,
+ WCD9XXX_CLK_RCO);
+ WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+ }
+
+ return ret;
+}
+
+static int tomtom_codec_enable_aux_pga(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+ wcd9xxx_resmgr_get_bandgap(&tomtom->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+ /* AUX PGA requires RCO or MCLK */
+ tomtom_codec_internal_rco_ctrl(codec, true);
+ WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+ wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 1);
+ WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+ wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 0);
+ WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+ tomtom_codec_internal_rco_ctrl(codec, false);
+ WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+ wcd9xxx_resmgr_put_bandgap(&tomtom->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_codec_enable_lineout(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ u16 lineout_gain_reg;
+
+ pr_debug("%s %d %s\n", __func__, event, w->name);
+
+ switch (w->shift) {
+ case 0:
+ lineout_gain_reg = TOMTOM_A_RX_LINE_1_GAIN;
+ break;
+ case 1:
+ lineout_gain_reg = TOMTOM_A_RX_LINE_2_GAIN;
+ break;
+ case 2:
+ lineout_gain_reg = TOMTOM_A_RX_LINE_3_GAIN;
+ break;
+ case 3:
+ lineout_gain_reg = TOMTOM_A_RX_LINE_4_GAIN;
+ break;
+ default:
+ pr_err("%s: Error, incorrect lineout register value\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, lineout_gain_reg, 0x40, 0x40);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d,
+ WCD9XXX_CLSH_STATE_LO,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
+ pr_debug("%s: sleeping 5 ms after %s PA turn on\n",
+ __func__, w->name);
+ /* Wait for CnP time after PA enable */
+ usleep_range(5000, 5100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, lineout_gain_reg, 0x40, 0x00);
+ pr_debug("%s: sleeping 5 ms after %s PA turn off\n",
+ __func__, w->name);
+ /* Wait for CnP time after PA disable */
+ usleep_range(5000, 5100);
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_codec_enable_spk_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ u16 spk_drv_reg;
+
+ pr_debug("%s: %d %s\n", __func__, event, w->name);
+ if (strnstr(w->name, "SPK2 PA", sizeof("SPK2 PA")))
+ spk_drv_reg = TOMTOM_A_SPKR_DRV2_EN;
+ else
+ spk_drv_reg = TOMTOM_A_SPKR_DRV1_EN;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tomtom->spkr_pa_widget_on = true;
+ snd_soc_update_bits(codec, spk_drv_reg, 0x80, 0x80);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tomtom->spkr_pa_widget_on = false;
+ snd_soc_update_bits(codec, spk_drv_reg, 0x80, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static u8 tomtom_get_dmic_clk_val(struct snd_soc_codec *codec,
+ u32 mclk_rate, u32 dmic_clk_rate)
+{
+ u32 div_factor;
+ u8 dmic_ctl_val;
+
+ dev_dbg(codec->dev,
+ "%s: mclk_rate = %d, dmic_sample_rate = %d\n",
+ __func__, mclk_rate, dmic_clk_rate);
+
+ /* Default value to return in case of error */
+ if (mclk_rate == TOMTOM_MCLK_CLK_9P6MHZ)
+ dmic_ctl_val = WCD9330_DMIC_CLK_DIV_2;
+ else
+ dmic_ctl_val = WCD9330_DMIC_CLK_DIV_3;
+
+ if (dmic_clk_rate == 0) {
+ dev_err(codec->dev,
+ "%s: dmic_sample_rate cannot be 0\n",
+ __func__);
+ goto done;
+ }
+
+ div_factor = mclk_rate / dmic_clk_rate;
+ switch (div_factor) {
+ case 2:
+ dmic_ctl_val = WCD9330_DMIC_CLK_DIV_2;
+ break;
+ case 3:
+ dmic_ctl_val = WCD9330_DMIC_CLK_DIV_3;
+ break;
+ case 4:
+ dmic_ctl_val = WCD9330_DMIC_CLK_DIV_4;
+ break;
+ case 6:
+ dmic_ctl_val = WCD9330_DMIC_CLK_DIV_6;
+ break;
+ case 16:
+ dmic_ctl_val = WCD9330_DMIC_CLK_DIV_16;
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n",
+ __func__, div_factor, mclk_rate, dmic_clk_rate);
+ break;
+ }
+
+done:
+ return dmic_ctl_val;
+}
+
+static int tomtom_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_pdata *pdata = tomtom->resmgr.pdata;
+ u8 dmic_clk_en;
+ u16 dmic_clk_reg;
+ s32 *dmic_clk_cnt;
+ u8 dmic_rate_val, dmic_rate_shift;
+ unsigned int dmic;
+ int ret;
+ char *wname;
+
+ wname = strpbrk(w->name, "123456");
+ if (!wname) {
+ dev_err(codec->dev, "%s: widget not found\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(wname, 10, &dmic);
+ if (ret < 0) {
+ pr_err("%s: Invalid DMIC line on the codec\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (dmic) {
+ case 1:
+ case 2:
+ dmic_clk_en = 0x01;
+ dmic_clk_cnt = &(tomtom->dmic_1_2_clk_cnt);
+ dmic_clk_reg = TOMTOM_A_DMIC_B1_CTL;
+ dmic_rate_shift = 5;
+ pr_debug("%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n",
+ __func__, event, dmic, *dmic_clk_cnt);
+
+ break;
+
+ case 3:
+ case 4:
+ dmic_clk_en = 0x02;
+ dmic_clk_cnt = &(tomtom->dmic_3_4_clk_cnt);
+ dmic_clk_reg = TOMTOM_A_DMIC_B2_CTL;
+ dmic_rate_shift = 1;
+ pr_debug("%s() event %d DMIC%d dmic_3_4_clk_cnt %d\n",
+ __func__, event, dmic, *dmic_clk_cnt);
+ break;
+
+ case 5:
+ case 6:
+ dmic_clk_en = 0x04;
+ dmic_clk_cnt = &(tomtom->dmic_5_6_clk_cnt);
+ dmic_clk_reg = TOMTOM_A_DMIC_B2_CTL;
+ dmic_rate_shift = 4;
+ pr_debug("%s() event %d DMIC%d dmic_5_6_clk_cnt %d\n",
+ __func__, event, dmic, *dmic_clk_cnt);
+
+ break;
+
+ default:
+ pr_err("%s: Invalid DMIC Selection\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+
+ dmic_rate_val =
+ tomtom_get_dmic_clk_val(codec,
+ pdata->mclk_rate,
+ pdata->dmic_sample_rate);
+
+ (*dmic_clk_cnt)++;
+ if (*dmic_clk_cnt == 1) {
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ 0x07 << dmic_rate_shift,
+ dmic_rate_val << dmic_rate_shift);
+ snd_soc_update_bits(codec, TOMTOM_A_DMIC_B1_CTL,
+ dmic_clk_en, dmic_clk_en);
+ }
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+
+ dmic_rate_val =
+ tomtom_get_dmic_clk_val(codec,
+ pdata->mclk_rate,
+ pdata->mad_dmic_sample_rate);
+ (*dmic_clk_cnt)--;
+ if (*dmic_clk_cnt == 0) {
+ snd_soc_update_bits(codec, TOMTOM_A_DMIC_B1_CTL,
+ dmic_clk_en, 0);
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ 0x07 << dmic_rate_shift,
+ dmic_rate_val << dmic_rate_shift);
+ }
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_codec_config_mad(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ const struct firmware *fw;
+ struct firmware_cal *hwdep_cal = NULL;
+ struct mad_audio_cal *mad_cal;
+ const void *data;
+ const char *filename = TOMTOM_MAD_AUDIO_FIRMWARE_PATH;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ size_t cal_size;
+ int idx;
+
+ pr_debug("%s: enter\n", __func__);
+
+ if (!tomtom->fw_data) {
+ dev_err(codec->dev, "%s: invalid cal data\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ hwdep_cal = wcdcal_get_fw_cal(tomtom->fw_data, WCD9XXX_MAD_CAL);
+ if (hwdep_cal) {
+ data = hwdep_cal->data;
+ cal_size = hwdep_cal->size;
+ dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+ __func__);
+ } else {
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (ret != 0) {
+ pr_err("Failed to acquire MAD firwmare data %s: %d\n",
+ filename, ret);
+ return -ENODEV;
+ }
+ if (!fw) {
+ dev_err(codec->dev, "failed to get mad fw");
+ return -ENODEV;
+ }
+ data = fw->data;
+ cal_size = fw->size;
+ dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+ __func__);
+ }
+ if (cal_size < sizeof(struct mad_audio_cal)) {
+ pr_err("%s: incorrect hwdep cal size %zu\n",
+ __func__, cal_size);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ mad_cal = (struct mad_audio_cal *)(data);
+ if (!mad_cal) {
+ dev_err(codec->dev, "%s: Invalid calibration data\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_MAIN_CTL_2,
+ mad_cal->microphone_info.cycle_time);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_MAIN_CTL_1, 0xFF << 3,
+ ((uint16_t)mad_cal->microphone_info.settle_time)
+ << 3);
+
+ /* Audio */
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_8,
+ mad_cal->audio_info.rms_omit_samples);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_1,
+ 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_2, 0x03 << 2,
+ mad_cal->audio_info.detection_mechanism << 2);
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_7,
+ mad_cal->audio_info.rms_diff_threshold & 0x3F);
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_5,
+ mad_cal->audio_info.rms_threshold_lsb);
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_6,
+ mad_cal->audio_info.rms_threshold_msb);
+
+ for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients);
+ idx++) {
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR,
+ 0x3F, idx);
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL,
+ mad_cal->audio_info.iir_coefficients[idx]);
+ dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x",
+ __func__, idx,
+ mad_cal->audio_info.iir_coefficients[idx]);
+ }
+
+ /* Beacon */
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_8,
+ mad_cal->beacon_info.rms_omit_samples);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_1,
+ 0x07 << 4, mad_cal->beacon_info.rms_comp_time);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_2, 0x03 << 2,
+ mad_cal->beacon_info.detection_mechanism << 2);
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_7,
+ mad_cal->beacon_info.rms_diff_threshold & 0x1F);
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_5,
+ mad_cal->beacon_info.rms_threshold_lsb);
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_6,
+ mad_cal->beacon_info.rms_threshold_msb);
+
+ /* Ultrasound */
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_1,
+ 0x07 << 4, mad_cal->beacon_info.rms_comp_time);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_2, 0x03 << 2,
+ mad_cal->ultrasound_info.detection_mechanism);
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_7,
+ mad_cal->ultrasound_info.rms_diff_threshold & 0x1F);
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_5,
+ mad_cal->ultrasound_info.rms_threshold_lsb);
+ snd_soc_write(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_6,
+ mad_cal->ultrasound_info.rms_threshold_msb);
+
+ /* Set MAD intr time to 20 msec */
+ snd_soc_update_bits(codec, 0x4E, 0x01F, 0x13);
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+err:
+ if (!hwdep_cal)
+ release_firmware(fw);
+ return ret;
+}
+
+static int tomtom_codec_enable_mad(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ u8 mad_micb, mad_cfilt;
+ u16 mad_cfilt_reg;
+
+ mad_micb = snd_soc_read(codec, TOMTOM_A_MAD_ANA_CTRL) & 0x07;
+ switch (mad_micb) {
+ case 1:
+ mad_cfilt = tomtom->resmgr.pdata->micbias.bias1_cfilt_sel;
+ break;
+ case 2:
+ mad_cfilt = tomtom->resmgr.pdata->micbias.bias2_cfilt_sel;
+ break;
+ case 3:
+ mad_cfilt = tomtom->resmgr.pdata->micbias.bias3_cfilt_sel;
+ break;
+ case 4:
+ mad_cfilt = tomtom->resmgr.pdata->micbias.bias4_cfilt_sel;
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: Invalid micbias selection 0x%x\n",
+ __func__, mad_micb);
+ return -EINVAL;
+ }
+
+ switch (mad_cfilt) {
+ case WCD9XXX_CFILT1_SEL:
+ mad_cfilt_reg = TOMTOM_A_MICB_CFILT_1_VAL;
+ break;
+ case WCD9XXX_CFILT2_SEL:
+ mad_cfilt_reg = TOMTOM_A_MICB_CFILT_2_VAL;
+ break;
+ case WCD9XXX_CFILT3_SEL:
+ mad_cfilt_reg = TOMTOM_A_MICB_CFILT_3_VAL;
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: invalid cfilt 0x%x for micb 0x%x\n",
+ __func__, mad_cfilt, mad_micb);
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev,
+ "%s event = %d, mad_cfilt_reg = 0x%x\n",
+ __func__, event, mad_cfilt_reg);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Undo reset for MAD */
+ snd_soc_update_bits(codec, TOMTOM_A_SVASS_CLKRST_CTL,
+ 0x02, 0x00);
+
+ ret = tomtom_codec_config_mad(codec);
+ if (ret) {
+ pr_err("%s: Failed to config MAD\n", __func__);
+ break;
+ }
+
+ /* setup MAD micbias to VDDIO */
+ snd_soc_update_bits(codec, mad_cfilt_reg,
+ 0x02, 0x02);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Reset the MAD block */
+ snd_soc_update_bits(codec, TOMTOM_A_SVASS_CLKRST_CTL,
+ 0x02, 0x02);
+
+ /* Undo setup of MAD micbias to VDDIO */
+ snd_soc_update_bits(codec, mad_cfilt_reg,
+ 0x02, 0x00);
+ }
+ return ret;
+}
+
+static int tomtom_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ u16 micb_int_reg = 0, micb_ctl_reg = 0;
+ u8 cfilt_sel_val = 0;
+ char *internal1_text = "Internal1";
+ char *internal2_text = "Internal2";
+ char *internal3_text = "Internal3";
+ enum wcd9xxx_notify_event e_post_off, e_pre_on, e_post_on;
+
+ pr_debug("%s: w->name %s event %d\n", __func__, w->name, event);
+ if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) {
+ micb_ctl_reg = TOMTOM_A_MICB_1_CTL;
+ micb_int_reg = TOMTOM_A_MICB_1_INT_RBIAS;
+ cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias1_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_1_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_1_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_1_OFF;
+ } else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) {
+ micb_ctl_reg = TOMTOM_A_MICB_2_CTL;
+ micb_int_reg = TOMTOM_A_MICB_2_INT_RBIAS;
+ cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias2_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_2_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_2_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_2_OFF;
+ } else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) {
+ micb_ctl_reg = TOMTOM_A_MICB_3_CTL;
+ micb_int_reg = TOMTOM_A_MICB_3_INT_RBIAS;
+ cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias3_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_3_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_3_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_3_OFF;
+ } else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) {
+ micb_ctl_reg = TOMTOM_A_MICB_4_CTL;
+ micb_int_reg = tomtom->resmgr.reg_addr->micb_4_int_rbias;
+ cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias4_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_4_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_4_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_4_OFF;
+ } else {
+ pr_err("%s: Error, invalid micbias %s\n", __func__, w->name);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Let MBHC module know so micbias switch to be off */
+ wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_pre_on);
+
+ /* Get cfilt */
+ wcd9xxx_resmgr_cfilt_get(&tomtom->resmgr, cfilt_sel_val);
+
+ if (strnstr(w->name, internal1_text, 30))
+ snd_soc_update_bits(codec, micb_int_reg, 0xE0, 0xE0);
+ else if (strnstr(w->name, internal2_text, 30))
+ snd_soc_update_bits(codec, micb_int_reg, 0x1C, 0x1C);
+ else if (strnstr(w->name, internal3_text, 30))
+ snd_soc_update_bits(codec, micb_int_reg, 0x3, 0x3);
+ else
+ /*
+ * If not internal, make sure to write the
+ * register to default value
+ */
+ snd_soc_write(codec, micb_int_reg, 0x24);
+ if (tomtom->mbhc_started && micb_ctl_reg ==
+ TOMTOM_A_MICB_2_CTL) {
+ if (++tomtom->micb_2_users == 1) {
+ if (tomtom->resmgr.pdata->
+ micbias.bias2_is_headset_only)
+ wcd9xxx_resmgr_add_cond_update_bits(
+ &tomtom->resmgr,
+ WCD9XXX_COND_HPH_MIC,
+ micb_ctl_reg, w->shift,
+ false);
+ else
+ snd_soc_update_bits(codec, micb_ctl_reg,
+ 1 << w->shift,
+ 1 << w->shift);
+ }
+ pr_debug("%s: micb_2_users %d\n", __func__,
+ tomtom->micb_2_users);
+ } else {
+ snd_soc_update_bits(codec, micb_ctl_reg, 1 << w->shift,
+ 1 << w->shift);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ /* Let MBHC module know so micbias is on */
+ wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_post_on);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (tomtom->mbhc_started && micb_ctl_reg ==
+ TOMTOM_A_MICB_2_CTL) {
+ if (--tomtom->micb_2_users == 0) {
+ if (tomtom->resmgr.pdata->
+ micbias.bias2_is_headset_only)
+ wcd9xxx_resmgr_rm_cond_update_bits(
+ &tomtom->resmgr,
+ WCD9XXX_COND_HPH_MIC,
+ micb_ctl_reg, 7, false);
+ else
+ snd_soc_update_bits(codec, micb_ctl_reg,
+ 1 << w->shift, 0);
+ }
+ pr_debug("%s: micb_2_users %d\n", __func__,
+ tomtom->micb_2_users);
+ WARN(tomtom->micb_2_users < 0,
+ "Unexpected micbias users %d\n",
+ tomtom->micb_2_users);
+ } else {
+ snd_soc_update_bits(codec, micb_ctl_reg, 1 << w->shift,
+ 0);
+ }
+
+ /* Let MBHC module know so micbias switch to be off */
+ wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_post_off);
+
+ if (strnstr(w->name, internal1_text, 30))
+ snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00);
+ else if (strnstr(w->name, internal2_text, 30))
+ snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x00);
+ else if (strnstr(w->name, internal3_text, 30))
+ snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
+
+ /* Put cfilt */
+ wcd9xxx_resmgr_cfilt_put(&tomtom->resmgr, cfilt_sel_val);
+ break;
+ }
+
+ return 0;
+}
+
+/* called under codec_resource_lock acquisition */
+static int tomtom_enable_mbhc_micbias(struct snd_soc_codec *codec, bool enable,
+ enum wcd9xxx_micbias_num micb_num)
+{
+ int rc;
+
+ if (micb_num != MBHC_MICBIAS2) {
+ dev_err(codec->dev, "%s: Unsupported micbias, micb_num=%d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ }
+
+ if (enable)
+ rc = snd_soc_dapm_force_enable_pin(
+ snd_soc_codec_get_dapm(codec),
+ DAPM_MICBIAS2_EXTERNAL_STANDALONE);
+ else
+ rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec),
+ DAPM_MICBIAS2_EXTERNAL_STANDALONE);
+ if (!rc)
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+ pr_debug("%s: leave ret %d\n", __func__, rc);
+ return rc;
+}
+
+static void txfe_clkdiv_update(struct snd_soc_codec *codec)
+{
+ struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (test_bit(ADC1_TXFE, &priv->status_mask)) {
+ snd_soc_update_bits(codec, TOMTOM_A_TX_1_2_TXFE_CLKDIV,
+ 0x0F, 0x05);
+ clear_bit(ADC1_TXFE, &priv->status_mask);
+ }
+ if (test_bit(ADC2_TXFE, &priv->status_mask)) {
+ snd_soc_update_bits(codec, TOMTOM_A_TX_1_2_TXFE_CLKDIV,
+ 0xF0, 0x50);
+ clear_bit(ADC2_TXFE, &priv->status_mask);
+ }
+ if (test_bit(ADC3_TXFE, &priv->status_mask)) {
+ snd_soc_update_bits(codec, TOMTOM_A_TX_3_4_TXFE_CKDIV,
+ 0x0F, 0x05);
+ clear_bit(ADC3_TXFE, &priv->status_mask);
+ }
+ if (test_bit(ADC4_TXFE, &priv->status_mask)) {
+ snd_soc_update_bits(codec, TOMTOM_A_TX_3_4_TXFE_CKDIV,
+ 0xF0, 0x50);
+ clear_bit(ADC4_TXFE, &priv->status_mask);
+ }
+ if (test_bit(ADC5_TXFE, &priv->status_mask)) {
+ snd_soc_update_bits(codec, TOMTOM_A_TX_5_6_TXFE_CKDIV,
+ 0x0F, 0x05);
+ clear_bit(ADC5_TXFE, &priv->status_mask);
+ }
+ if (test_bit(ADC6_TXFE, &priv->status_mask)) {
+ snd_soc_update_bits(codec, TOMTOM_A_TX_5_6_TXFE_CKDIV,
+ 0xF0, 0x50);
+ clear_bit(ADC6_TXFE, &priv->status_mask);
+ }
+}
+
+static void tx_hpf_corner_freq_callback(struct work_struct *work)
+{
+ struct delayed_work *hpf_delayed_work;
+ struct hpf_work *hpf_work;
+ struct tomtom_priv *tomtom;
+ struct snd_soc_codec *codec;
+ u16 tx_mux_ctl_reg;
+ u8 hpf_cut_of_freq;
+
+ hpf_delayed_work = to_delayed_work(work);
+ hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
+ tomtom = hpf_work->tomtom;
+ codec = hpf_work->tomtom->codec;
+ hpf_cut_of_freq = hpf_work->tx_hpf_cut_of_freq;
+
+ tx_mux_ctl_reg = TOMTOM_A_CDC_TX1_MUX_CTL +
+ (hpf_work->decimator - 1) * 8;
+
+ pr_debug("%s(): decimator %u hpf_cut_of_freq 0x%x\n", __func__,
+ hpf_work->decimator, (unsigned int)hpf_cut_of_freq);
+
+ /*
+ * Restore TXFE ClkDiv registers to default.
+ * If any of these registers are modified during analog
+ * front-end enablement, they will be restored back to the
+ * default
+ */
+ txfe_clkdiv_update(codec);
+
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, hpf_cut_of_freq << 4);
+}
+
+#define TX_MUX_CTL_CUT_OFF_FREQ_MASK 0x30
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+
+static int tomtom_codec_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int decimator;
+ char *dec_name = NULL;
+ char *widget_name = NULL;
+ char *temp;
+ int ret = 0;
+ u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg;
+ u8 dec_hpf_cut_of_freq;
+ int offset;
+ char *dec;
+
+ pr_debug("%s %d\n", __func__, event);
+
+ widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+ if (!widget_name)
+ return -ENOMEM;
+ temp = widget_name;
+
+ dec_name = strsep(&widget_name, " ");
+ widget_name = temp;
+ if (!dec_name) {
+ pr_err("%s: Invalid decimator = %s\n", __func__, w->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dec = strpbrk(dec_name, "123456789");
+ if (!dec) {
+ dev_err(codec->dev, "%s: decimator index not found\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = kstrtouint(dec, 10, &decimator);
+ if (ret < 0) {
+ pr_err("%s: Invalid decimator = %s\n", __func__, dec_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pr_debug("%s(): widget = %s dec_name = %s decimator = %u\n", __func__,
+ w->name, dec_name, decimator);
+
+ if (w->reg == TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL) {
+ dec_reset_reg = TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL;
+ offset = 0;
+ } else if (w->reg == TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL) {
+ dec_reset_reg = TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL;
+ offset = 8;
+ } else {
+ pr_err("%s: Error, incorrect dec\n", __func__);
+ return -EINVAL;
+ }
+
+ tx_vol_ctl_reg = TOMTOM_A_CDC_TX1_VOL_CTL_CFG + 8 * (decimator - 1);
+ tx_mux_ctl_reg = TOMTOM_A_CDC_TX1_MUX_CTL + 8 * (decimator - 1);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+
+ /* Enableable TX digital mute */
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01);
+
+ snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift,
+ 1 << w->shift);
+ snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0);
+
+ pr_debug("%s: decimator = %u, bypass = %d\n", __func__,
+ decimator, tx_hpf_work[decimator - 1].tx_hpf_bypass);
+ if (tx_hpf_work[decimator - 1].tx_hpf_bypass != true) {
+ dec_hpf_cut_of_freq = snd_soc_read(codec,
+ tx_mux_ctl_reg);
+
+ dec_hpf_cut_of_freq = (dec_hpf_cut_of_freq & 0x30) >> 4;
+
+ tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq =
+ dec_hpf_cut_of_freq;
+
+ if ((dec_hpf_cut_of_freq != CF_MIN_3DB_150HZ)) {
+
+ /* set cut of freq to CF_MIN_3DB_150HZ (0x1); */
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30,
+ CF_MIN_3DB_150HZ << 4);
+ }
+
+ /* enable HPF */
+ snd_soc_update_bits(codec, tx_mux_ctl_reg , 0x08, 0x00);
+ } else
+ /* bypass HPF */
+ snd_soc_update_bits(codec, tx_mux_ctl_reg , 0x08, 0x08);
+
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+
+ /* Disable TX digital mute */
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00);
+
+ if ((tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq !=
+ CF_MIN_3DB_150HZ) &&
+ (tx_hpf_work[decimator - 1].tx_hpf_bypass != true)) {
+
+ schedule_delayed_work(&tx_hpf_work[decimator - 1].dwork,
+ msecs_to_jiffies(300));
+ }
+ /* apply the digital gain after the decimator is enabled*/
+ if ((w->shift + offset) < ARRAY_SIZE(tx_digital_gain_reg))
+ snd_soc_write(codec,
+ tx_digital_gain_reg[w->shift + offset],
+ snd_soc_read(codec,
+ tx_digital_gain_reg[w->shift + offset])
+ );
+
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01);
+ cancel_delayed_work_sync(&tx_hpf_work[decimator - 1].dwork);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08);
+ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30,
+ (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq) << 4);
+
+ break;
+ }
+out:
+ kfree(widget_name);
+ return ret;
+}
+
+static int tomtom_codec_enable_vdd_spkr(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: %d %s\n", __func__, event, w->name);
+
+ WARN_ONCE(!priv->spkdrv_reg, "SPKDRV supply %s isn't defined\n",
+ WCD9XXX_VDD_SPKDRV_NAME);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (priv->spkdrv_reg) {
+ ret = regulator_enable(priv->spkdrv_reg);
+ if (ret)
+ pr_err("%s: Failed to enable spkdrv_reg %s\n",
+ __func__, WCD9XXX_VDD_SPKDRV_NAME);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (priv->spkdrv_reg) {
+ ret = regulator_disable(priv->spkdrv_reg);
+ if (ret)
+ pr_err("%s: Failed to disable spkdrv_reg %s\n",
+ __func__, WCD9XXX_VDD_SPKDRV_NAME);
+ }
+ break;
+ }
+
+ return ret;
+}
+
+static int tomtom_codec_enable_vdd_spkr2(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: %d %s\n", __func__, event, w->name);
+
+ /*
+ * If on-demand voltage regulators of spkr1 and spkr2 has been derived
+ * from same power rail then same on-demand voltage regulator can be
+ * used by both spkr1 and spkr2, if a separate device tree entry has
+ * not been defined for on-demand voltage regulator for spkr2.
+ */
+ if (!priv->spkdrv2_reg) {
+ if (priv->spkdrv_reg) {
+ priv->spkdrv2_reg = priv->spkdrv_reg;
+ } else {
+ WARN_ONCE(!priv->spkdrv2_reg,
+ "SPKDRV2 supply %s isn't defined\n",
+ WCD9XXX_VDD_SPKDRV2_NAME);
+ return 0;
+ }
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (priv->spkdrv2_reg) {
+ ret = regulator_enable(priv->spkdrv2_reg);
+ if (ret)
+ pr_err("%s: Failed to enable spkdrv2_reg %s ret:%d\n",
+ __func__, WCD9XXX_VDD_SPKDRV2_NAME, ret);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (priv->spkdrv2_reg) {
+ ret = regulator_disable(priv->spkdrv2_reg);
+ if (ret)
+ pr_err("%s: Failed to disable spkdrv2_reg %s ret:%d\n",
+ __func__, WCD9XXX_VDD_SPKDRV2_NAME, ret);
+ }
+ break;
+ }
+
+ return ret;
+}
+
+static int tomtom_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ pr_debug("%s %d %s\n", __func__, event, w->name);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_RESET_CTL,
+ 1 << w->shift, 1 << w->shift);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_RESET_CTL,
+ 1 << w->shift, 0x0);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* apply the digital gain after the interpolator is enabled*/
+ if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg))
+ snd_soc_write(codec,
+ rx_digital_gain_reg[w->shift],
+ snd_soc_read(codec,
+ rx_digital_gain_reg[w->shift])
+ );
+ /* Check for Rx1 and Rx2 paths for uhqa mode update */
+ if (w->shift == 0 || w->shift == 1)
+ tomtom_update_uhqa_mode(codec, (1 << w->shift));
+
+ break;
+ }
+ return 0;
+}
+
+/* called under codec_resource_lock acquisition */
+static int __tomtom_codec_enable_ldo_h(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: enter\n", __func__);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /*
+ * ldo_h_users is protected by tomtom->codec_mutex, don't need
+ * additional mutex
+ */
+ if (++priv->ldo_h_users == 1) {
+ WCD9XXX_BG_CLK_LOCK(&priv->resmgr);
+ wcd9xxx_resmgr_get_bandgap(&priv->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ WCD9XXX_BG_CLK_UNLOCK(&priv->resmgr);
+ tomtom_codec_internal_rco_ctrl(codec, true);
+ snd_soc_update_bits(codec, TOMTOM_A_LDO_H_MODE_1,
+ 1 << 7, 1 << 7);
+ tomtom_codec_internal_rco_ctrl(codec, false);
+ pr_debug("%s: ldo_h_users %d\n", __func__,
+ priv->ldo_h_users);
+ /* LDO enable requires 1ms to settle down */
+ usleep_range(1000, 1100);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (--priv->ldo_h_users == 0) {
+ tomtom_codec_internal_rco_ctrl(codec, true);
+ snd_soc_update_bits(codec, TOMTOM_A_LDO_H_MODE_1,
+ 1 << 7, 0);
+ tomtom_codec_internal_rco_ctrl(codec, false);
+ WCD9XXX_BG_CLK_LOCK(&priv->resmgr);
+ wcd9xxx_resmgr_put_bandgap(&priv->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ WCD9XXX_BG_CLK_UNLOCK(&priv->resmgr);
+ pr_debug("%s: ldo_h_users %d\n", __func__,
+ priv->ldo_h_users);
+ }
+ WARN(priv->ldo_h_users < 0, "Unexpected ldo_h users %d\n",
+ priv->ldo_h_users);
+ break;
+ }
+ pr_debug("%s: leave\n", __func__);
+ return 0;
+}
+
+static int tomtom_codec_enable_ldo_h(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int rc;
+ rc = __tomtom_codec_enable_ldo_h(w, kcontrol, event);
+ return rc;
+}
+
+static int tomtom_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 0);
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_codec_enable_anc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ const char *filename;
+ const struct firmware *fw;
+ int i;
+ int ret = 0;
+ int num_anc_slots;
+ struct wcd9xxx_anc_header *anc_head;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ struct firmware_cal *hwdep_cal = NULL;
+ u32 anc_writes_size = 0;
+ u32 anc_cal_size = 0;
+ int anc_size_remaining;
+ u32 *anc_ptr;
+ u16 reg;
+ u8 mask, val, old_val;
+ size_t cal_size;
+ const void *data;
+
+ if (tomtom->anc_func == 0)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ filename = "wcd9320/wcd9320_anc.bin";
+
+ hwdep_cal = wcdcal_get_fw_cal(tomtom->fw_data, WCD9XXX_ANC_CAL);
+ if (hwdep_cal) {
+ data = hwdep_cal->data;
+ cal_size = hwdep_cal->size;
+ dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+ __func__);
+ } else {
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
+ ret);
+ return -ENODEV;
+ }
+ if (!fw) {
+ dev_err(codec->dev, "failed to get anc fw");
+ return -ENODEV;
+ }
+ data = fw->data;
+ cal_size = fw->size;
+ dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+ __func__);
+ }
+ if (cal_size < sizeof(struct wcd9xxx_anc_header)) {
+ dev_err(codec->dev, "Not enough data\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ /* First number is the number of register writes */
+ anc_head = (struct wcd9xxx_anc_header *)(data);
+ anc_ptr = (u32 *)(data +
+ sizeof(struct wcd9xxx_anc_header));
+ anc_size_remaining = cal_size -
+ sizeof(struct wcd9xxx_anc_header);
+ num_anc_slots = anc_head->num_anc_slots;
+
+ if (tomtom->anc_slot >= num_anc_slots) {
+ dev_err(codec->dev, "Invalid ANC slot selected\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ for (i = 0; i < num_anc_slots; i++) {
+ if (anc_size_remaining < TOMTOM_PACKED_REG_SIZE) {
+ dev_err(codec->dev, "Invalid register format\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ anc_writes_size = (u32)(*anc_ptr);
+ anc_size_remaining -= sizeof(u32);
+ anc_ptr += 1;
+
+ if (anc_writes_size * TOMTOM_PACKED_REG_SIZE
+ > anc_size_remaining) {
+ dev_err(codec->dev, "Invalid register format\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (tomtom->anc_slot == i)
+ break;
+
+ anc_size_remaining -= (anc_writes_size *
+ TOMTOM_PACKED_REG_SIZE);
+ anc_ptr += anc_writes_size;
+ }
+ if (i == num_anc_slots) {
+ dev_err(codec->dev, "Selected ANC slot not present\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ i = 0;
+ anc_cal_size = anc_writes_size;
+ if (w->reg == TOMTOM_A_RX_HPH_L_DAC_CTL) {
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x03, 0x03);
+ anc_writes_size = (anc_cal_size/2);
+ }
+
+ if (w->reg == TOMTOM_A_RX_HPH_R_DAC_CTL) {
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x0C, 0x0C);
+ i = (anc_cal_size/2);
+ anc_writes_size = anc_cal_size;
+ }
+
+ for (; i < anc_writes_size; i++) {
+ TOMTOM_CODEC_UNPACK_ENTRY(anc_ptr[i], reg,
+ mask, val);
+ /*
+ * ANC Soft reset register is ignored from ACDB
+ * because ANC left soft reset bits will be called
+ * while enabling ANC HPH Right DAC.
+ */
+ if ((reg == TOMTOM_A_CDC_CLK_ANC_RESET_CTL) &&
+ ((w->reg == TOMTOM_A_RX_HPH_L_DAC_CTL) ||
+ (w->reg == TOMTOM_A_RX_HPH_R_DAC_CTL))) {
+ continue;
+ }
+ old_val = snd_soc_read(codec, reg);
+ snd_soc_write(codec, reg, (old_val & ~mask) |
+ (val & mask));
+ }
+ if (w->reg == TOMTOM_A_RX_HPH_L_DAC_CTL)
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x03, 0x00);
+
+ if (w->reg == TOMTOM_A_RX_HPH_R_DAC_CTL)
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x0C, 0x00);
+ if (!hwdep_cal)
+ release_firmware(fw);
+ txfe_clkdiv_update(codec);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ msleep(40);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC1_B1_CTL, 0x01,
+ 0x00);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC2_B1_CTL, 0x02,
+ 0x00);
+ msleep(20);
+ snd_soc_write(codec, TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x0F);
+ snd_soc_write(codec, TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL, 0);
+ snd_soc_write(codec, TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x00);
+ break;
+ }
+ return 0;
+err:
+ if (!hwdep_cal)
+ release_firmware(fw);
+ return ret;
+}
+
+static int tomtom_hphl_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+ uint32_t impedl, impedr;
+ int ret = 0;
+
+ pr_debug("%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (tomtom_p->anc_func) {
+ tomtom_codec_enable_anc(w, kcontrol, event);
+ msleep(50);
+ }
+
+ if (!high_perf_mode && !tomtom_p->uhqa_mode) {
+ wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+ WCD9XXX_CLSH_STATE_HPHL,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_PRE_DAC);
+ } else {
+ wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d,
+ tomtom_p->uhqa_mode,
+ WCD9XXX_CLSAB_STATE_HPHL,
+ WCD9XXX_CLSAB_REQ_ENABLE);
+ }
+ ret = wcd9xxx_mbhc_get_impedance(&tomtom_p->mbhc,
+ &impedl, &impedr);
+ if (!ret)
+ wcd9xxx_clsh_imped_config(codec, impedl);
+ else
+ dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n",
+ __func__, ret);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B3_CTL, 0xBC, 0x94);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B4_CTL, 0x30, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B3_CTL, 0xBC, 0x00);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B4_CTL, 0x30, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!high_perf_mode && !tomtom_p->uhqa_mode) {
+ wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+ WCD9XXX_CLSH_STATE_HPHL,
+ WCD9XXX_CLSH_REQ_DISABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
+ } else {
+ wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d,
+ tomtom_p->uhqa_mode,
+ WCD9XXX_CLSAB_STATE_HPHL,
+ WCD9XXX_CLSAB_REQ_DISABLE);
+ }
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (tomtom_p->anc_func) {
+ tomtom_codec_enable_anc(w, kcontrol, event);
+ msleep(50);
+ }
+
+ snd_soc_update_bits(codec, w->reg, 0x40, 0x40);
+ if (!high_perf_mode && !tomtom_p->uhqa_mode) {
+ wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+ WCD9XXX_CLSH_STATE_HPHR,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_PRE_DAC);
+ } else {
+ wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d,
+ tomtom_p->uhqa_mode,
+ WCD9XXX_CLSAB_STATE_HPHR,
+ WCD9XXX_CLSAB_REQ_ENABLE);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B3_CTL, 0xBC, 0x94);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B4_CTL, 0x30, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B3_CTL, 0xBC, 0x00);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B4_CTL, 0x30, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, w->reg, 0x40, 0x00);
+ if (!high_perf_mode && !tomtom_p->uhqa_mode) {
+ wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+ WCD9XXX_CLSH_STATE_HPHR,
+ WCD9XXX_CLSH_REQ_DISABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
+ } else {
+ wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d,
+ tomtom_p->uhqa_mode,
+ WCD9XXX_CLSAB_STATE_HPHR,
+ WCD9XXX_CLSAB_REQ_DISABLE);
+ }
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_hph_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ enum wcd9xxx_notify_event e_pre_on, e_post_off;
+ u8 req_clsh_state;
+ u32 pa_settle_time = TOMTOM_HPH_PA_SETTLE_COMP_OFF;
+
+ pr_debug("%s: %s event = %d\n", __func__, w->name, event);
+ if (w->shift == 5) {
+ e_pre_on = WCD9XXX_EVENT_PRE_HPHL_PA_ON;
+ e_post_off = WCD9XXX_EVENT_POST_HPHL_PA_OFF;
+ req_clsh_state = WCD9XXX_CLSH_STATE_HPHL;
+ } else if (w->shift == 4) {
+ e_pre_on = WCD9XXX_EVENT_PRE_HPHR_PA_ON;
+ e_post_off = WCD9XXX_EVENT_POST_HPHR_PA_OFF;
+ req_clsh_state = WCD9XXX_CLSH_STATE_HPHR;
+ } else {
+ pr_err("%s: Invalid w->shift %d\n", __func__, w->shift);
+ return -EINVAL;
+ }
+
+ if (tomtom->comp_enabled[COMPANDER_1])
+ pa_settle_time = TOMTOM_HPH_PA_SETTLE_COMP_ON;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ set_bit(HPH_DELAY, &tomtom->status_mask);
+ /* Let MBHC module know PA is turning on */
+ wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_pre_on);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ if (test_bit(HPH_DELAY, &tomtom->status_mask)) {
+ /*
+ * Make sure to wait 10ms after enabling HPHR_HPHL
+ * in register 0x1AB
+ */
+ usleep_range(pa_settle_time, pa_settle_time + 1000);
+ clear_bit(HPH_DELAY, &tomtom->status_mask);
+ pr_debug("%s: sleep %d us after %s PA enable\n",
+ __func__, pa_settle_time, w->name);
+ }
+ if (!high_perf_mode && !tomtom->uhqa_mode) {
+ wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d,
+ req_clsh_state,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
+ }
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ set_bit(HPH_DELAY, &tomtom->status_mask);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ /* Let MBHC module know PA turned off */
+ wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_post_off);
+ if (test_bit(HPH_DELAY, &tomtom->status_mask)) {
+ /*
+ * Make sure to wait 10ms after disabling HPHR_HPHL
+ * in register 0x1AB
+ */
+ usleep_range(pa_settle_time, pa_settle_time + 1000);
+ clear_bit(HPH_DELAY, &tomtom->status_mask);
+ pr_debug("%s: sleep %d us after %s PA disable\n",
+ __func__, pa_settle_time, w->name);
+ }
+
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_codec_enable_anc_hph(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = tomtom_hph_pa_event(w, kcontrol, event);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if ((snd_soc_read(codec, TOMTOM_A_RX_HPH_L_DAC_CTL) & 0x80) &&
+ (snd_soc_read(codec, TOMTOM_A_RX_HPH_R_DAC_CTL)
+ & 0x80)) {
+ snd_soc_update_bits(codec,
+ TOMTOM_A_RX_HPH_CNP_EN, 0x30, 0x30);
+ msleep(30);
+ }
+ ret = tomtom_hph_pa_event(w, kcontrol, event);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (w->shift == 5) {
+ snd_soc_update_bits(codec,
+ TOMTOM_A_RX_HPH_CNP_EN, 0x30, 0x00);
+ msleep(40);
+ snd_soc_update_bits(codec,
+ TOMTOM_A_TX_7_MBHC_EN, 0x80, 00);
+ ret |= tomtom_codec_enable_anc(w, kcontrol, event);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = tomtom_hph_pa_event(w, kcontrol, event);
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget tomtom_dapm_i2s_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", TOMTOM_A_CDC_CLK_RX_I2S_CTL,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", TOMTOM_A_CDC_CLK_TX_I2S_CTL, 4,
+ 0, NULL, 0),
+};
+
+static int tomtom_lineout_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d,
+ WCD9XXX_CLSH_STATE_LO,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_PRE_DAC);
+ snd_soc_update_bits(codec, w->reg, 0x40, 0x40);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, w->reg, 0x40, 0x00);
+ wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d,
+ WCD9XXX_CLSH_STATE_LO,
+ WCD9XXX_CLSH_REQ_DISABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_spk_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ pr_debug("%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+ 0x80, 0x80);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if ((snd_soc_read(codec, w->reg) & 0x03) == 0)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+ 0x80, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_route audio_i2s_map[] = {
+ {"SLIM RX1", NULL, "RX_I2S_CLK"},
+ {"SLIM RX2", NULL, "RX_I2S_CLK"},
+ {"SLIM RX3", NULL, "RX_I2S_CLK"},
+ {"SLIM RX4", NULL, "RX_I2S_CLK"},
+
+ {"SLIM TX7 MUX", NULL, "TX_I2S_CLK"},
+ {"SLIM TX8 MUX", NULL, "TX_I2S_CLK"},
+ {"SLIM TX9 MUX", NULL, "TX_I2S_CLK"},
+ {"SLIM TX10 MUX", NULL, "TX_I2S_CLK"},
+
+ {"RX_I2S_CLK", NULL, "CDC_I2S_RX_CONN"},
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* SLIMBUS Connections */
+ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+ {"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+ {"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
+
+ /* VI Feedback */
+ {"AIF4 VI", NULL, "VIONOFF"},
+ {"VIONOFF", "Switch", "VIINPUT"},
+
+ /* MAD */
+ {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"},
+ {"MAD_SEL MUX", "MSM", "MADINPUT"},
+ {"MADONOFF", "Switch", "MAD_SEL MUX"},
+ {"AIF4 MAD", NULL, "MADONOFF"},
+
+ /* SLIM_MIXER("AIF1_CAP Mixer"),*/
+ {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ /* SLIM_MIXER("AIF2_CAP Mixer"),*/
+ {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ /* SLIM_MIXER("AIF3_CAP Mixer"),*/
+ {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+
+ {"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
+ {"SLIM TX1 MUX", "RMIX1", "RX1 MIX1"},
+ {"SLIM TX1 MUX", "RMIX2", "RX2 MIX1"},
+ {"SLIM TX1 MUX", "RMIX3", "RX3 MIX1"},
+ {"SLIM TX1 MUX", "RMIX4", "RX4 MIX1"},
+ {"SLIM TX1 MUX", "RMIX5", "RX5 MIX1"},
+ {"SLIM TX1 MUX", "RMIX6", "RX6 MIX1"},
+ {"SLIM TX1 MUX", "RMIX7", "RX7 MIX1"},
+ {"SLIM TX1 MUX", "RMIX8", "RX8 MIX1"},
+
+ {"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
+ {"SLIM TX2 MUX", "RMIX1", "RX1 MIX1"},
+ {"SLIM TX2 MUX", "RMIX2", "RX2 MIX1"},
+ {"SLIM TX2 MUX", "RMIX3", "RX3 MIX1"},
+ {"SLIM TX2 MUX", "RMIX4", "RX4 MIX1"},
+ {"SLIM TX2 MUX", "RMIX5", "RX5 MIX1"},
+ {"SLIM TX2 MUX", "RMIX6", "RX6 MIX1"},
+ {"SLIM TX2 MUX", "RMIX7", "RX7 MIX1"},
+ {"SLIM TX2 MUX", "RMIX8", "RX8 MIX1"},
+
+ {"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
+ {"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
+ {"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
+ {"SLIM TX3 MUX", "RMIX3", "RX3 MIX1"},
+ {"SLIM TX3 MUX", "RMIX4", "RX4 MIX1"},
+ {"SLIM TX3 MUX", "RMIX5", "RX5 MIX1"},
+ {"SLIM TX3 MUX", "RMIX6", "RX6 MIX1"},
+ {"SLIM TX3 MUX", "RMIX7", "RX7 MIX1"},
+ {"SLIM TX3 MUX", "RMIX8", "RX8 MIX1"},
+
+ {"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
+ {"SLIM TX4 MUX", "RMIX1", "RX1 MIX1"},
+ {"SLIM TX4 MUX", "RMIX2", "RX2 MIX1"},
+ {"SLIM TX4 MUX", "RMIX3", "RX3 MIX1"},
+ {"SLIM TX4 MUX", "RMIX4", "RX4 MIX1"},
+ {"SLIM TX4 MUX", "RMIX5", "RX5 MIX1"},
+ {"SLIM TX4 MUX", "RMIX6", "RX6 MIX1"},
+ {"SLIM TX4 MUX", "RMIX7", "RX7 MIX1"},
+ {"SLIM TX4 MUX", "RMIX8", "RX8 MIX1"},
+
+ {"SLIM TX5 MUX", "DEC5", "DEC5 MUX"},
+ {"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
+ {"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
+ {"SLIM TX5 MUX", "RMIX3", "RX3 MIX1"},
+ {"SLIM TX5 MUX", "RMIX4", "RX4 MIX1"},
+ {"SLIM TX5 MUX", "RMIX5", "RX5 MIX1"},
+ {"SLIM TX5 MUX", "RMIX6", "RX6 MIX1"},
+ {"SLIM TX5 MUX", "RMIX7", "RX7 MIX1"},
+ {"SLIM TX5 MUX", "RMIX8", "RX8 MIX1"},
+
+ {"SLIM TX6 MUX", "DEC6", "DEC6 MUX"},
+
+ {"SLIM TX7 MUX", "DEC1", "DEC1 MUX"},
+ {"SLIM TX7 MUX", "DEC2", "DEC2 MUX"},
+ {"SLIM TX7 MUX", "DEC3", "DEC3 MUX"},
+ {"SLIM TX7 MUX", "DEC4", "DEC4 MUX"},
+ {"SLIM TX7 MUX", "DEC5", "DEC5 MUX"},
+ {"SLIM TX7 MUX", "DEC6", "DEC6 MUX"},
+ {"SLIM TX7 MUX", "DEC7", "DEC7 MUX"},
+ {"SLIM TX7 MUX", "DEC8", "DEC8 MUX"},
+ {"SLIM TX7 MUX", "DEC9", "DEC9 MUX"},
+ {"SLIM TX7 MUX", "DEC10", "DEC10 MUX"},
+ {"SLIM TX7 MUX", "RMIX1", "RX1 MIX1"},
+ {"SLIM TX7 MUX", "RMIX2", "RX2 MIX1"},
+ {"SLIM TX7 MUX", "RMIX3", "RX3 MIX1"},
+ {"SLIM TX7 MUX", "RMIX4", "RX4 MIX1"},
+ {"SLIM TX7 MUX", "RMIX5", "RX5 MIX1"},
+ {"SLIM TX7 MUX", "RMIX6", "RX6 MIX1"},
+ {"SLIM TX7 MUX", "RMIX7", "RX7 MIX1"},
+
+ {"SLIM TX8 MUX", "DEC1", "DEC1 MUX"},
+ {"SLIM TX8 MUX", "DEC2", "DEC2 MUX"},
+ {"SLIM TX8 MUX", "DEC3", "DEC3 MUX"},
+ {"SLIM TX8 MUX", "DEC4", "DEC4 MUX"},
+ {"SLIM TX8 MUX", "DEC5", "DEC5 MUX"},
+ {"SLIM TX8 MUX", "DEC6", "DEC6 MUX"},
+ {"SLIM TX8 MUX", "DEC7", "DEC7 MUX"},
+ {"SLIM TX8 MUX", "DEC8", "DEC8 MUX"},
+ {"SLIM TX8 MUX", "DEC9", "DEC9 MUX"},
+ {"SLIM TX8 MUX", "DEC10", "DEC10 MUX"},
+
+ {"SLIM TX9 MUX", "DEC1", "DEC1 MUX"},
+ {"SLIM TX9 MUX", "DEC2", "DEC2 MUX"},
+ {"SLIM TX9 MUX", "DEC3", "DEC3 MUX"},
+ {"SLIM TX9 MUX", "DEC4", "DEC4 MUX"},
+ {"SLIM TX9 MUX", "DEC5", "DEC5 MUX"},
+ {"SLIM TX9 MUX", "DEC6", "DEC6 MUX"},
+ {"SLIM TX9 MUX", "DEC7", "DEC7 MUX"},
+ {"SLIM TX9 MUX", "DEC8", "DEC8 MUX"},
+ {"SLIM TX9 MUX", "DEC9", "DEC9 MUX"},
+ {"SLIM TX9 MUX", "DEC10", "DEC10 MUX"},
+
+ {"SLIM TX10 MUX", "DEC1", "DEC1 MUX"},
+ {"SLIM TX10 MUX", "DEC2", "DEC2 MUX"},
+ {"SLIM TX10 MUX", "DEC3", "DEC3 MUX"},
+ {"SLIM TX10 MUX", "DEC4", "DEC4 MUX"},
+ {"SLIM TX10 MUX", "DEC5", "DEC5 MUX"},
+ {"SLIM TX10 MUX", "DEC6", "DEC6 MUX"},
+ {"SLIM TX10 MUX", "DEC7", "DEC7 MUX"},
+ {"SLIM TX10 MUX", "DEC8", "DEC8 MUX"},
+ {"SLIM TX10 MUX", "DEC9", "DEC9 MUX"},
+ {"SLIM TX10 MUX", "DEC10", "DEC10 MUX"},
+
+ /* Earpiece (RX MIX1) */
+ {"EAR", NULL, "EAR PA"},
+ {"EAR PA", NULL, "EAR_PA_MIXER"},
+ {"EAR_PA_MIXER", NULL, "DAC1"},
+ {"DAC1", NULL, "RX_BIAS"},
+
+ {"ANC EAR", NULL, "ANC EAR PA"},
+ {"ANC EAR PA", NULL, "EAR_PA_MIXER"},
+ {"ANC1 FB MUX", "EAR_HPH_L", "RX1 MIX2"},
+ {"ANC1 FB MUX", "EAR_LINE_1", "RX2 MIX2"},
+
+ /* Headset (RX MIX1 and RX MIX2) */
+ {"HEADPHONE", NULL, "HPHL"},
+ {"HEADPHONE", NULL, "HPHR"},
+
+ {"HPHL", NULL, "HPHL_PA_MIXER"},
+ {"HPHL_PA_MIXER", NULL, "HPHL DAC"},
+ {"HPHL DAC", NULL, "RX_BIAS"},
+
+ {"HPHR", NULL, "HPHR_PA_MIXER"},
+ {"HPHR_PA_MIXER", NULL, "HPHR DAC"},
+ {"HPHR DAC", NULL, "RX_BIAS"},
+
+ {"ANC HEADPHONE", NULL, "ANC HPHL"},
+ {"ANC HEADPHONE", NULL, "ANC HPHR"},
+
+ {"ANC HPHL", NULL, "HPHL_PA_MIXER"},
+ {"ANC HPHR", NULL, "HPHR_PA_MIXER"},
+
+ {"ANC1 MUX", "ADC1", "ADC1"},
+ {"ANC1 MUX", "ADC2", "ADC2"},
+ {"ANC1 MUX", "ADC3", "ADC3"},
+ {"ANC1 MUX", "ADC4", "ADC4"},
+ {"ANC1 MUX", "ADC5", "ADC5"},
+ {"ANC1 MUX", "ADC6", "ADC6"},
+ {"ANC1 MUX", "DMIC1", "DMIC1"},
+ {"ANC1 MUX", "DMIC2", "DMIC2"},
+ {"ANC1 MUX", "DMIC3", "DMIC3"},
+ {"ANC1 MUX", "DMIC4", "DMIC4"},
+ {"ANC1 MUX", "DMIC5", "DMIC5"},
+ {"ANC1 MUX", "DMIC6", "DMIC6"},
+ {"ANC2 MUX", "ADC1", "ADC1"},
+ {"ANC2 MUX", "ADC2", "ADC2"},
+ {"ANC2 MUX", "ADC3", "ADC3"},
+ {"ANC2 MUX", "ADC4", "ADC4"},
+ {"ANC2 MUX", "ADC5", "ADC5"},
+ {"ANC2 MUX", "ADC6", "ADC6"},
+ {"ANC2 MUX", "DMIC1", "DMIC1"},
+ {"ANC2 MUX", "DMIC2", "DMIC2"},
+ {"ANC2 MUX", "DMIC3", "DMIC3"},
+ {"ANC2 MUX", "DMIC4", "DMIC4"},
+ {"ANC2 MUX", "DMIC5", "DMIC5"},
+ {"ANC2 MUX", "DMIC6", "DMIC6"},
+
+ {"ANC HPHR", NULL, "CDC_CONN"},
+
+ {"DAC1", "Switch", "CLASS_H_DSM MUX"},
+ {"HPHL DAC", "Switch", "CLASS_H_DSM MUX"},
+ {"HPHR DAC", NULL, "RX2 CHAIN"},
+
+ {"LINEOUT1", NULL, "LINEOUT1 PA"},
+ {"LINEOUT2", NULL, "LINEOUT2 PA"},
+ {"LINEOUT3", NULL, "LINEOUT3 PA"},
+ {"LINEOUT4", NULL, "LINEOUT4 PA"},
+ {"SPK_OUT", NULL, "SPK PA"},
+ {"SPK_OUT", NULL, "SPK2 PA"},
+
+ {"LINEOUT1 PA", NULL, "LINEOUT1_PA_MIXER"},
+ {"LINEOUT1_PA_MIXER", NULL, "LINEOUT1 DAC"},
+
+ {"LINEOUT2 PA", NULL, "LINEOUT2_PA_MIXER"},
+ {"LINEOUT2_PA_MIXER", NULL, "LINEOUT2 DAC"},
+
+ {"LINEOUT3 PA", NULL, "LINEOUT3_PA_MIXER"},
+ {"LINEOUT3_PA_MIXER", NULL, "LINEOUT3 DAC"},
+
+ {"LINEOUT4 PA", NULL, "LINEOUT4_PA_MIXER"},
+ {"LINEOUT4_PA_MIXER", NULL, "LINEOUT4 DAC"},
+
+ {"LINEOUT1 DAC", NULL, "RX3 MIX1"},
+
+ {"RDAC5 MUX", "DEM3_INV", "RX3 MIX1"},
+ {"RDAC5 MUX", "DEM4", "RX4 MIX1"},
+
+ {"LINEOUT3 DAC", NULL, "RDAC5 MUX"},
+
+ {"LINEOUT2 DAC", NULL, "RX5 MIX1"},
+
+ {"RDAC7 MUX", "DEM5_INV", "RX5 MIX1"},
+ {"RDAC7 MUX", "DEM6", "RX6 MIX1"},
+
+ {"LINEOUT4 DAC", NULL, "RDAC7 MUX"},
+
+ {"SPK PA", NULL, "SPK DAC"},
+ {"SPK DAC", NULL, "RX7 MIX2"},
+ {"SPK DAC", NULL, "VDD_SPKDRV"},
+
+ {"SPK2 PA", NULL, "SPK2 DAC"},
+ {"SPK2 DAC", NULL, "RX8 MIX1"},
+ {"SPK2 DAC", NULL, "VDD_SPKDRV2"},
+
+ {"CLASS_H_DSM MUX", "DSM_HPHL_RX1", "RX1 CHAIN"},
+
+ {"RX1 INTERP", NULL, "RX1 MIX2"},
+ {"RX1 CHAIN", NULL, "RX1 INTERP"},
+ {"RX2 INTERP", NULL, "RX2 MIX2"},
+ {"RX2 CHAIN", NULL, "RX2 INTERP"},
+ {"RX1 MIX2", NULL, "ANC1 MUX"},
+ {"RX2 MIX2", NULL, "ANC2 MUX"},
+
+ {"LINEOUT1 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT2 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT3 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT4 DAC", NULL, "RX_BIAS"},
+ {"SPK DAC", NULL, "RX_BIAS"},
+ {"SPK2 DAC", NULL, "RX_BIAS"},
+
+ {"RX7 MIX1", NULL, "COMP0_CLK"},
+ {"RX8 MIX1", NULL, "COMP0_CLK"},
+ {"RX1 MIX1", NULL, "COMP1_CLK"},
+ {"RX2 MIX1", NULL, "COMP1_CLK"},
+ {"RX3 MIX1", NULL, "COMP2_CLK"},
+ {"RX5 MIX1", NULL, "COMP2_CLK"},
+
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP1"},
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP2"},
+ {"RX1 MIX1", NULL, "RX1 MIX1 INP3"},
+ {"RX2 MIX1", NULL, "RX2 MIX1 INP1"},
+ {"RX2 MIX1", NULL, "RX2 MIX1 INP2"},
+ {"RX3 MIX1", NULL, "RX3 MIX1 INP1"},
+ {"RX3 MIX1", NULL, "RX3 MIX1 INP2"},
+ {"RX4 MIX1", NULL, "RX4 MIX1 INP1"},
+ {"RX4 MIX1", NULL, "RX4 MIX1 INP2"},
+ {"RX5 MIX1", NULL, "RX5 MIX1 INP1"},
+ {"RX5 MIX1", NULL, "RX5 MIX1 INP2"},
+ {"RX6 MIX1", NULL, "RX6 MIX1 INP1"},
+ {"RX6 MIX1", NULL, "RX6 MIX1 INP2"},
+ {"RX7 MIX1", NULL, "RX7 MIX1 INP1"},
+ {"RX7 MIX1", NULL, "RX7 MIX1 INP2"},
+ {"RX8 MIX1", NULL, "RX8 MIX1 INP1"},
+ {"RX8 MIX1", NULL, "RX8 MIX1 INP2"},
+ {"RX1 MIX2", NULL, "RX1 MIX1"},
+ {"RX1 MIX2", NULL, "RX1 MIX2 INP1"},
+ {"RX1 MIX2", NULL, "RX1 MIX2 INP2"},
+ {"RX2 MIX2", NULL, "RX2 MIX1"},
+ {"RX2 MIX2", NULL, "RX2 MIX2 INP1"},
+ {"RX2 MIX2", NULL, "RX2 MIX2 INP2"},
+ {"RX7 MIX2", NULL, "RX7 MIX1"},
+ {"RX7 MIX2", NULL, "RX7 MIX2 INP1"},
+ {"RX7 MIX2", NULL, "RX7 MIX2 INP2"},
+
+ /* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+ {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX8 MUX", "AIF1_PB", "AIF1 PB"},
+ /* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+ {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX8 MUX", "AIF2_PB", "AIF2 PB"},
+ /* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+ {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX8 MUX", "AIF3_PB", "AIF3 PB"},
+
+ {"SLIM RX1", NULL, "SLIM RX1 MUX"},
+ {"SLIM RX2", NULL, "SLIM RX2 MUX"},
+ {"SLIM RX3", NULL, "SLIM RX3 MUX"},
+ {"SLIM RX4", NULL, "SLIM RX4 MUX"},
+ {"SLIM RX5", NULL, "SLIM RX5 MUX"},
+ {"SLIM RX6", NULL, "SLIM RX6 MUX"},
+ {"SLIM RX7", NULL, "SLIM RX7 MUX"},
+ {"SLIM RX8", NULL, "SLIM RX8 MUX"},
+
+ {"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX1 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX1 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX1 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX1 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX1 MIX1 INP1", "IIR2", "IIR2"},
+ {"RX1 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX1 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX1 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX1 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX1 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX1 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX1 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX1 MIX1 INP2", "IIR2", "IIR2"},
+ {"RX1 MIX1 INP3", "RX1", "SLIM RX1"},
+ {"RX1 MIX1 INP3", "RX2", "SLIM RX2"},
+ {"RX1 MIX1 INP3", "RX3", "SLIM RX3"},
+ {"RX1 MIX1 INP3", "RX4", "SLIM RX4"},
+ {"RX1 MIX1 INP3", "RX5", "SLIM RX5"},
+ {"RX1 MIX1 INP3", "RX6", "SLIM RX6"},
+ {"RX1 MIX1 INP3", "RX7", "SLIM RX7"},
+ {"RX2 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX2 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX2 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX2 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX2 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX2 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX2 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX2 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX2 MIX1 INP1", "IIR2", "IIR2"},
+ {"RX2 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX2 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX2 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX2 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX2 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX2 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX2 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX2 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX2 MIX1 INP2", "IIR2", "IIR2"},
+ {"RX3 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX3 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX3 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX3 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX3 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX3 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX3 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX3 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX3 MIX1 INP1", "IIR2", "IIR2"},
+ {"RX3 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX3 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX3 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX3 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX3 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX3 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX3 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX3 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX3 MIX1 INP2", "IIR2", "IIR2"},
+ {"RX4 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX4 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX4 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX4 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX4 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX4 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX4 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX4 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX4 MIX1 INP1", "IIR2", "IIR2"},
+ {"RX4 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX4 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX4 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX4 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX4 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX4 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX4 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX4 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX4 MIX1 INP2", "IIR2", "IIR2"},
+ {"RX5 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX5 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX5 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX5 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX5 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX5 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX5 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX5 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX5 MIX1 INP1", "IIR2", "IIR2"},
+ {"RX5 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX5 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX5 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX5 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX5 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX5 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX5 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX5 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX5 MIX1 INP2", "IIR2", "IIR2"},
+ {"RX6 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX6 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX6 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX6 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX6 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX6 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX6 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX6 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX6 MIX1 INP1", "IIR2", "IIR2"},
+ {"RX6 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX6 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX6 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX6 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX6 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX6 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX6 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX6 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX6 MIX1 INP2", "IIR2", "IIR2"},
+ {"RX7 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX7 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX7 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX7 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX7 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX7 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX7 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX7 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX7 MIX1 INP1", "IIR2", "IIR2"},
+ {"RX7 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX7 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX7 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX7 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX7 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX7 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX7 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX7 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX7 MIX1 INP2", "IIR2", "IIR2"},
+ {"RX8 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX8 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX8 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX8 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX8 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX8 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX8 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX8 MIX1 INP1", "RX8", "SLIM RX8"},
+ {"RX8 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX8 MIX1 INP1", "IIR2", "IIR2"},
+ {"RX8 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX8 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX8 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX8 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX8 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX8 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX8 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX8 MIX1 INP2", "RX8", "SLIM RX8"},
+ {"RX8 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX8 MIX1 INP2", "IIR2", "IIR2"},
+
+ /* IIR1, IIR2 inputs to Second RX Mixer on RX1, RX2 and RX7 chains. */
+ {"RX1 MIX2 INP1", "IIR1", "IIR1"},
+ {"RX1 MIX2 INP2", "IIR1", "IIR1"},
+ {"RX2 MIX2 INP1", "IIR1", "IIR1"},
+ {"RX2 MIX2 INP2", "IIR1", "IIR1"},
+ {"RX7 MIX2 INP1", "IIR1", "IIR1"},
+ {"RX7 MIX2 INP2", "IIR1", "IIR1"},
+ {"RX1 MIX2 INP1", "IIR2", "IIR2"},
+ {"RX1 MIX2 INP2", "IIR2", "IIR2"},
+ {"RX2 MIX2 INP1", "IIR2", "IIR2"},
+ {"RX2 MIX2 INP2", "IIR2", "IIR2"},
+ {"RX7 MIX2 INP1", "IIR2", "IIR2"},
+ {"RX7 MIX2 INP2", "IIR2", "IIR2"},
+
+ /* Decimator Inputs */
+ {"DEC1 MUX", "DMIC1", "DMIC1"},
+ {"DEC1 MUX", "ADC6", "ADC6"},
+ {"DEC1 MUX", NULL, "CDC_CONN"},
+ {"DEC2 MUX", "DMIC2", "DMIC2"},
+ {"DEC2 MUX", "ADC5", "ADC5"},
+ {"DEC2 MUX", NULL, "CDC_CONN"},
+ {"DEC3 MUX", "DMIC3", "DMIC3"},
+ {"DEC3 MUX", "ADC4", "ADC4"},
+ {"DEC3 MUX", NULL, "CDC_CONN"},
+ {"DEC4 MUX", "DMIC4", "DMIC4"},
+ {"DEC4 MUX", "ADC3", "ADC3"},
+ {"DEC4 MUX", NULL, "CDC_CONN"},
+ {"DEC5 MUX", "DMIC5", "DMIC5"},
+ {"DEC5 MUX", "ADC2", "ADC2"},
+ {"DEC5 MUX", NULL, "CDC_CONN"},
+ {"DEC6 MUX", "DMIC6", "DMIC6"},
+ {"DEC6 MUX", "ADC1", "ADC1"},
+ {"DEC6 MUX", NULL, "CDC_CONN"},
+ {"DEC7 MUX", "DMIC1", "DMIC1"},
+ {"DEC7 MUX", "DMIC6", "DMIC6"},
+ {"DEC7 MUX", "ADC1", "ADC1"},
+ {"DEC7 MUX", "ADC6", "ADC6"},
+ {"DEC7 MUX", "ANC1_FB", "ANC1 MUX"},
+ {"DEC7 MUX", "ANC2_FB", "ANC2 MUX"},
+ {"DEC7 MUX", NULL, "CDC_CONN"},
+ {"DEC8 MUX", "DMIC2", "DMIC2"},
+ {"DEC8 MUX", "DMIC5", "DMIC5"},
+ {"DEC8 MUX", "ADC2", "ADC2"},
+ {"DEC8 MUX", "ADC5", "ADC5"},
+ {"DEC8 MUX", "ANC1_FB", "ANC1 MUX"},
+ {"DEC8 MUX", "ANC2_FB", "ANC2 MUX"},
+ {"DEC8 MUX", NULL, "CDC_CONN"},
+ {"DEC9 MUX", "DMIC4", "DMIC4"},
+ {"DEC9 MUX", "DMIC5", "DMIC5"},
+ {"DEC9 MUX", "ADC2", "ADC2"},
+ {"DEC9 MUX", "ADC3", "ADC3"},
+ {"DEC9 MUX", "ANC1_FB", "ANC1 MUX"},
+ {"DEC9 MUX", "ANC2_FB", "ANC2 MUX"},
+ {"DEC9 MUX", NULL, "CDC_CONN"},
+ {"DEC10 MUX", "DMIC3", "DMIC3"},
+ {"DEC10 MUX", "DMIC6", "DMIC6"},
+ {"DEC10 MUX", "ADC1", "ADC1"},
+ {"DEC10 MUX", "ADC4", "ADC4"},
+ {"DEC10 MUX", "ANC1_FB", "ANC1 MUX"},
+ {"DEC10 MUX", "ANC2_FB", "ANC2 MUX"},
+ {"DEC10 MUX", NULL, "CDC_CONN"},
+
+ /* ADC Connections */
+ {"ADC1", NULL, "AMIC1"},
+ {"ADC2", NULL, "AMIC2"},
+ {"ADC3", NULL, "AMIC3"},
+ {"ADC4", NULL, "AMIC4"},
+ {"ADC5", NULL, "AMIC5"},
+ {"ADC6", NULL, "AMIC6"},
+
+ /* AUX PGA Connections */
+ {"EAR_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"},
+ {"HPHL_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"},
+ {"HPHR_PA_MIXER", "AUX_PGA_R Switch", "AUX_PGA_Right"},
+ {"LINEOUT1_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"},
+ {"LINEOUT2_PA_MIXER", "AUX_PGA_R Switch", "AUX_PGA_Right"},
+ {"LINEOUT3_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"},
+ {"LINEOUT4_PA_MIXER", "AUX_PGA_R Switch", "AUX_PGA_Right"},
+ {"AUX_PGA_Left", NULL, "AMIC5"},
+ {"AUX_PGA_Right", NULL, "AMIC6"},
+
+ {"IIR1", NULL, "IIR1 INP1 MUX"},
+ {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"},
+ {"IIR1 INP1 MUX", "DEC3", "DEC3 MUX"},
+ {"IIR1 INP1 MUX", "DEC4", "DEC4 MUX"},
+ {"IIR1 INP1 MUX", "DEC5", "DEC5 MUX"},
+ {"IIR1 INP1 MUX", "DEC6", "DEC6 MUX"},
+ {"IIR1 INP1 MUX", "DEC7", "DEC7 MUX"},
+ {"IIR1 INP1 MUX", "DEC8", "DEC8 MUX"},
+ {"IIR1 INP1 MUX", "DEC9", "DEC9 MUX"},
+ {"IIR1 INP1 MUX", "DEC10", "DEC10 MUX"},
+ {"IIR1 INP1 MUX", "RX1", "SLIM RX1"},
+ {"IIR1 INP1 MUX", "RX2", "SLIM RX2"},
+ {"IIR1 INP1 MUX", "RX3", "SLIM RX3"},
+ {"IIR1 INP1 MUX", "RX4", "SLIM RX4"},
+ {"IIR1 INP1 MUX", "RX5", "SLIM RX5"},
+ {"IIR1 INP1 MUX", "RX6", "SLIM RX6"},
+ {"IIR1 INP1 MUX", "RX7", "SLIM RX7"},
+
+ {"IIR2", NULL, "IIR2 INP1 MUX"},
+ {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"},
+ {"IIR2 INP1 MUX", "DEC3", "DEC3 MUX"},
+ {"IIR2 INP1 MUX", "DEC4", "DEC4 MUX"},
+ {"IIR2 INP1 MUX", "DEC5", "DEC5 MUX"},
+ {"IIR2 INP1 MUX", "DEC6", "DEC6 MUX"},
+ {"IIR2 INP1 MUX", "DEC7", "DEC7 MUX"},
+ {"IIR2 INP1 MUX", "DEC8", "DEC8 MUX"},
+ {"IIR2 INP1 MUX", "DEC9", "DEC9 MUX"},
+ {"IIR2 INP1 MUX", "DEC10", "DEC10 MUX"},
+ {"IIR2 INP1 MUX", "RX1", "SLIM RX1"},
+ {"IIR2 INP1 MUX", "RX2", "SLIM RX2"},
+ {"IIR2 INP1 MUX", "RX3", "SLIM RX3"},
+ {"IIR2 INP1 MUX", "RX4", "SLIM RX4"},
+ {"IIR2 INP1 MUX", "RX5", "SLIM RX5"},
+ {"IIR2 INP1 MUX", "RX6", "SLIM RX6"},
+ {"IIR2 INP1 MUX", "RX7", "SLIM RX7"},
+
+ {"IIR1", NULL, "IIR1 INP2 MUX"},
+ {"IIR1 INP2 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR1 INP2 MUX", "DEC2", "DEC2 MUX"},
+ {"IIR1 INP2 MUX", "DEC3", "DEC3 MUX"},
+ {"IIR1 INP2 MUX", "DEC4", "DEC4 MUX"},
+ {"IIR1 INP2 MUX", "DEC5", "DEC5 MUX"},
+ {"IIR1 INP2 MUX", "DEC6", "DEC6 MUX"},
+ {"IIR1 INP2 MUX", "DEC7", "DEC7 MUX"},
+ {"IIR1 INP2 MUX", "DEC8", "DEC8 MUX"},
+ {"IIR1 INP2 MUX", "DEC9", "DEC9 MUX"},
+ {"IIR1 INP2 MUX", "DEC10", "DEC10 MUX"},
+ {"IIR1 INP2 MUX", "RX1", "SLIM RX1"},
+ {"IIR1 INP2 MUX", "RX2", "SLIM RX2"},
+ {"IIR1 INP2 MUX", "RX3", "SLIM RX3"},
+ {"IIR1 INP2 MUX", "RX4", "SLIM RX4"},
+ {"IIR1 INP2 MUX", "RX5", "SLIM RX5"},
+ {"IIR1 INP2 MUX", "RX6", "SLIM RX6"},
+ {"IIR1 INP2 MUX", "RX7", "SLIM RX7"},
+
+ {"IIR2", NULL, "IIR2 INP2 MUX"},
+ {"IIR2 INP2 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR2 INP2 MUX", "DEC2", "DEC2 MUX"},
+ {"IIR2 INP2 MUX", "DEC3", "DEC3 MUX"},
+ {"IIR2 INP2 MUX", "DEC4", "DEC4 MUX"},
+ {"IIR2 INP2 MUX", "DEC5", "DEC5 MUX"},
+ {"IIR2 INP2 MUX", "DEC6", "DEC6 MUX"},
+ {"IIR2 INP2 MUX", "DEC7", "DEC7 MUX"},
+ {"IIR2 INP2 MUX", "DEC8", "DEC8 MUX"},
+ {"IIR2 INP2 MUX", "DEC9", "DEC9 MUX"},
+ {"IIR2 INP2 MUX", "DEC10", "DEC10 MUX"},
+ {"IIR2 INP2 MUX", "RX1", "SLIM RX1"},
+ {"IIR2 INP2 MUX", "RX2", "SLIM RX2"},
+ {"IIR2 INP2 MUX", "RX3", "SLIM RX3"},
+ {"IIR2 INP2 MUX", "RX4", "SLIM RX4"},
+ {"IIR2 INP2 MUX", "RX5", "SLIM RX5"},
+ {"IIR2 INP2 MUX", "RX6", "SLIM RX6"},
+ {"IIR2 INP2 MUX", "RX7", "SLIM RX7"},
+
+ {"IIR1", NULL, "IIR1 INP3 MUX"},
+ {"IIR1 INP3 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR1 INP3 MUX", "DEC2", "DEC2 MUX"},
+ {"IIR1 INP3 MUX", "DEC3", "DEC3 MUX"},
+ {"IIR1 INP3 MUX", "DEC4", "DEC4 MUX"},
+ {"IIR1 INP3 MUX", "DEC5", "DEC5 MUX"},
+ {"IIR1 INP3 MUX", "DEC6", "DEC6 MUX"},
+ {"IIR1 INP3 MUX", "DEC7", "DEC7 MUX"},
+ {"IIR1 INP3 MUX", "DEC8", "DEC8 MUX"},
+ {"IIR1 INP3 MUX", "DEC9", "DEC9 MUX"},
+ {"IIR1 INP3 MUX", "DEC10", "DEC10 MUX"},
+ {"IIR1 INP3 MUX", "RX1", "SLIM RX1"},
+ {"IIR1 INP3 MUX", "RX2", "SLIM RX2"},
+ {"IIR1 INP3 MUX", "RX3", "SLIM RX3"},
+ {"IIR1 INP3 MUX", "RX4", "SLIM RX4"},
+ {"IIR1 INP3 MUX", "RX5", "SLIM RX5"},
+ {"IIR1 INP3 MUX", "RX6", "SLIM RX6"},
+ {"IIR1 INP3 MUX", "RX7", "SLIM RX7"},
+
+ {"IIR2", NULL, "IIR2 INP3 MUX"},
+ {"IIR2 INP3 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR2 INP3 MUX", "DEC2", "DEC2 MUX"},
+ {"IIR2 INP3 MUX", "DEC3", "DEC3 MUX"},
+ {"IIR2 INP3 MUX", "DEC4", "DEC4 MUX"},
+ {"IIR2 INP3 MUX", "DEC5", "DEC5 MUX"},
+ {"IIR2 INP3 MUX", "DEC6", "DEC6 MUX"},
+ {"IIR2 INP3 MUX", "DEC7", "DEC7 MUX"},
+ {"IIR2 INP3 MUX", "DEC8", "DEC8 MUX"},
+ {"IIR2 INP3 MUX", "DEC9", "DEC9 MUX"},
+ {"IIR2 INP3 MUX", "DEC10", "DEC10 MUX"},
+ {"IIR2 INP3 MUX", "RX1", "SLIM RX1"},
+ {"IIR2 INP3 MUX", "RX2", "SLIM RX2"},
+ {"IIR2 INP3 MUX", "RX3", "SLIM RX3"},
+ {"IIR2 INP3 MUX", "RX4", "SLIM RX4"},
+ {"IIR2 INP3 MUX", "RX5", "SLIM RX5"},
+ {"IIR2 INP3 MUX", "RX6", "SLIM RX6"},
+ {"IIR2 INP3 MUX", "RX7", "SLIM RX7"},
+
+ {"IIR1", NULL, "IIR1 INP4 MUX"},
+ {"IIR1 INP4 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR1 INP4 MUX", "DEC2", "DEC2 MUX"},
+ {"IIR1 INP4 MUX", "DEC3", "DEC3 MUX"},
+ {"IIR1 INP4 MUX", "DEC4", "DEC4 MUX"},
+ {"IIR1 INP4 MUX", "DEC5", "DEC5 MUX"},
+ {"IIR1 INP4 MUX", "DEC6", "DEC6 MUX"},
+ {"IIR1 INP4 MUX", "DEC7", "DEC7 MUX"},
+ {"IIR1 INP4 MUX", "DEC8", "DEC8 MUX"},
+ {"IIR1 INP4 MUX", "DEC9", "DEC9 MUX"},
+ {"IIR1 INP4 MUX", "DEC10", "DEC10 MUX"},
+ {"IIR1 INP4 MUX", "RX1", "SLIM RX1"},
+ {"IIR1 INP4 MUX", "RX2", "SLIM RX2"},
+ {"IIR1 INP4 MUX", "RX3", "SLIM RX3"},
+ {"IIR1 INP4 MUX", "RX4", "SLIM RX4"},
+ {"IIR1 INP4 MUX", "RX5", "SLIM RX5"},
+ {"IIR1 INP4 MUX", "RX6", "SLIM RX6"},
+ {"IIR1 INP4 MUX", "RX7", "SLIM RX7"},
+
+ {"IIR2", NULL, "IIR2 INP4 MUX"},
+ {"IIR2 INP4 MUX", "DEC1", "DEC1 MUX"},
+ {"IIR2 INP4 MUX", "DEC2", "DEC2 MUX"},
+ {"IIR2 INP4 MUX", "DEC3", "DEC3 MUX"},
+ {"IIR2 INP4 MUX", "DEC4", "DEC4 MUX"},
+ {"IIR2 INP4 MUX", "DEC5", "DEC5 MUX"},
+ {"IIR2 INP4 MUX", "DEC6", "DEC6 MUX"},
+ {"IIR2 INP4 MUX", "DEC7", "DEC7 MUX"},
+ {"IIR2 INP4 MUX", "DEC8", "DEC8 MUX"},
+ {"IIR2 INP4 MUX", "DEC9", "DEC9 MUX"},
+ {"IIR2 INP4 MUX", "DEC10", "DEC10 MUX"},
+ {"IIR2 INP4 MUX", "RX1", "SLIM RX1"},
+ {"IIR2 INP4 MUX", "RX2", "SLIM RX2"},
+ {"IIR2 INP4 MUX", "RX3", "SLIM RX3"},
+ {"IIR2 INP4 MUX", "RX4", "SLIM RX4"},
+ {"IIR2 INP4 MUX", "RX5", "SLIM RX5"},
+ {"IIR2 INP4 MUX", "RX6", "SLIM RX6"},
+ {"IIR2 INP4 MUX", "RX7", "SLIM RX7"},
+
+ {"MIC BIAS1 Internal1", NULL, "LDO_H"},
+ {"MIC BIAS1 Internal2", NULL, "LDO_H"},
+ {"MIC BIAS1 External", NULL, "LDO_H"},
+ {"MIC BIAS2 Internal1", NULL, "LDO_H"},
+ {"MIC BIAS2 Internal2", NULL, "LDO_H"},
+ {"MIC BIAS2 Internal3", NULL, "LDO_H"},
+ {"MIC BIAS2 External", NULL, "LDO_H"},
+ {"MIC BIAS3 Internal1", NULL, "LDO_H"},
+ {"MIC BIAS3 Internal2", NULL, "LDO_H"},
+ {"MIC BIAS3 External", NULL, "LDO_H"},
+ {"MIC BIAS4 External", NULL, "LDO_H"},
+ {DAPM_MICBIAS2_EXTERNAL_STANDALONE, NULL, "LDO_H Standalone"},
+};
+
+static int tomtom_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s(): substream = %s stream = %d\n" , __func__,
+ substream->name, substream->stream);
+
+ return 0;
+}
+
+static void tomtom_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s(): substream = %s stream = %d\n" , __func__,
+ substream->name, substream->stream);
+}
+
+int tomtom_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, bool dapm)
+{
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: mclk_enable = %u, dapm = %d\n", __func__, mclk_enable,
+ dapm);
+
+ WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+ if (mclk_enable) {
+ wcd9xxx_resmgr_get_bandgap(&tomtom->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr, WCD9XXX_CLK_MCLK);
+ } else {
+ /* Put clock and BG */
+ wcd9xxx_resmgr_put_clk_block(&tomtom->resmgr, WCD9XXX_CLK_MCLK);
+ wcd9xxx_resmgr_put_bandgap(&tomtom->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ }
+ WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+
+ return 0;
+}
+
+static int tomtom_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int tomtom_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ u8 val = 0;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec);
+
+ pr_debug("%s\n", __func__);
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* CPU is master */
+ if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ if (dai->id == AIF1_CAP)
+ snd_soc_update_bits(dai->codec,
+ TOMTOM_A_CDC_CLK_TX_I2S_CTL,
+ TOMTOM_I2S_MASTER_MODE_MASK, 0);
+ else if (dai->id == AIF1_PB)
+ snd_soc_update_bits(dai->codec,
+ TOMTOM_A_CDC_CLK_RX_I2S_CTL,
+ TOMTOM_I2S_MASTER_MODE_MASK, 0);
+ }
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* CPU is slave */
+ if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ val = TOMTOM_I2S_MASTER_MODE_MASK;
+ if (dai->id == AIF1_CAP)
+ snd_soc_update_bits(dai->codec,
+ TOMTOM_A_CDC_CLK_TX_I2S_CTL, val, val);
+ else if (dai->id == AIF1_PB)
+ snd_soc_update_bits(dai->codec,
+ TOMTOM_A_CDC_CLK_RX_I2S_CTL, val, val);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int tomtom_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+
+{
+ struct wcd9xxx_codec_dai_data *dai_data = NULL;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec);
+ struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
+ if (!tx_slot || !rx_slot) {
+ pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n",
+ __func__, tx_slot, rx_slot);
+ return -EINVAL;
+ }
+ pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+ "tomtom->intf_type %d\n",
+ __func__, dai->name, dai->id, tx_num, rx_num,
+ tomtom->intf_type);
+
+ if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ wcd9xxx_init_slimslave(core, core->slim->laddr,
+ tx_num, tx_slot, rx_num, rx_slot);
+ /*Reserve tx11 and tx12 for VI feedback path*/
+ dai_data = &tomtom->dai[AIF4_VIFEED];
+ if (dai_data) {
+ list_add_tail(&core->tx_chs[TOMTOM_TX11].list,
+ &dai_data->wcd9xxx_ch_list);
+ list_add_tail(&core->tx_chs[TOMTOM_TX12].list,
+ &dai_data->wcd9xxx_ch_list);
+ }
+
+ /* Reserve TX13 for MAD data channel */
+ dai_data = &tomtom->dai[AIF4_MAD_TX];
+ if (dai_data)
+ list_add_tail(&core->tx_chs[TOMTOM_TX13].list,
+ &dai_data->wcd9xxx_ch_list);
+ }
+
+ return 0;
+}
+
+static int tomtom_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+
+{
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(dai->codec);
+ u32 i = 0;
+ struct wcd9xxx_ch *ch;
+
+ switch (dai->id) {
+ case AIF1_PB:
+ case AIF2_PB:
+ case AIF3_PB:
+ if (!rx_slot || !rx_num) {
+ pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n",
+ __func__, rx_slot, rx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &tomtom_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: slot_num %u ch->ch_num %d\n",
+ __func__, i, ch->ch_num);
+ rx_slot[i++] = ch->ch_num;
+ }
+ pr_debug("%s: rx_num %d\n", __func__, i);
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ case AIF4_VIFEED:
+ case AIF4_MAD_TX:
+ if (!tx_slot || !tx_num) {
+ pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n",
+ __func__, tx_slot, tx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &tomtom_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: slot_num %u ch->ch_num %d\n",
+ __func__, i, ch->ch_num);
+ tx_slot[i++] = ch->ch_num;
+ }
+ pr_debug("%s: tx_num %d\n", __func__, i);
+ *tx_num = i;
+ break;
+
+ default:
+ pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+ break;
+ }
+
+ return 0;
+}
+
+static int tomtom_set_interpolator_rate(struct snd_soc_dai *dai,
+ u8 rx_fs_rate_reg_val, u32 compander_fs, u32 sample_rate)
+{
+ u32 j;
+ u8 rx_mix1_inp, rx8_mix1_inp;
+ u16 rx_mix_1_reg_1, rx_mix_1_reg_2;
+ u16 rx_fs_reg;
+ u8 rx_mix_1_reg_1_val, rx_mix_1_reg_2_val;
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ int port_rx_8 = TOMTOM_RX_PORT_START_NUMBER + NUM_INTERPOLATORS - 1;
+
+ list_for_each_entry(ch, &tomtom->dai[dai->id].wcd9xxx_ch_list, list) {
+ /* for RX port starting from 16 instead of 10 like tabla */
+ rx_mix1_inp = ch->port + RX_MIX1_INP_SEL_RX1 -
+ TOMTOM_TX_PORT_NUMBER;
+ rx8_mix1_inp = ch->port + RX8_MIX1_INP_SEL_RX1 -
+ TOMTOM_RX_PORT_START_NUMBER;
+ if (((ch->port < port_rx_8) &&
+ ((rx_mix1_inp < RX_MIX1_INP_SEL_RX1) ||
+ (rx_mix1_inp > RX_MIX1_INP_SEL_RX7))) ||
+ ((rx8_mix1_inp < RX8_MIX1_INP_SEL_RX1) ||
+ (rx8_mix1_inp > RX8_MIX1_INP_SEL_RX8))) {
+ pr_err("%s: Invalid TOMTOM_RX%u port. Dai ID is %d\n",
+ __func__, rx8_mix1_inp - 2,
+ dai->id);
+ return -EINVAL;
+ }
+
+ rx_mix_1_reg_1 = TOMTOM_A_CDC_CONN_RX1_B1_CTL;
+
+ for (j = 0; j < NUM_INTERPOLATORS - 1; j++) {
+ rx_mix_1_reg_2 = rx_mix_1_reg_1 + 1;
+
+ rx_mix_1_reg_1_val = snd_soc_read(codec,
+ rx_mix_1_reg_1);
+ rx_mix_1_reg_2_val = snd_soc_read(codec,
+ rx_mix_1_reg_2);
+
+ if (((rx_mix_1_reg_1_val & 0x0F) == rx_mix1_inp) ||
+ (((rx_mix_1_reg_1_val >> 4) & 0x0F)
+ == rx_mix1_inp) ||
+ ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
+
+ rx_fs_reg = TOMTOM_A_CDC_RX1_B5_CTL + 8 * j;
+
+ pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+ __func__, dai->id, j + 1);
+
+ pr_debug("%s: set RX%u sample rate to %u\n",
+ __func__, j + 1, sample_rate);
+
+ snd_soc_update_bits(codec, rx_fs_reg,
+ 0xE0, rx_fs_rate_reg_val);
+
+ if (comp_rx_path[j] < COMPANDER_MAX)
+ tomtom->comp_fs[comp_rx_path[j]]
+ = compander_fs;
+ }
+ if (j < 2)
+ rx_mix_1_reg_1 += 3;
+ else
+ rx_mix_1_reg_1 += 2;
+ }
+
+ /* RX8 interpolator path */
+ rx_mix_1_reg_1_val = snd_soc_read(codec,
+ TOMTOM_A_CDC_CONN_RX8_B1_CTL);
+ if (((rx_mix_1_reg_1_val & 0x0F) == rx8_mix1_inp) ||
+ (((rx_mix_1_reg_1_val >> 4) & 0x0F) == rx8_mix1_inp)) {
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_RX8_B5_CTL,
+ 0xE0, rx_fs_rate_reg_val);
+ pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+ __func__, dai->id, NUM_INTERPOLATORS);
+
+ pr_debug("%s: set RX%u sample rate to %u\n",
+ __func__, NUM_INTERPOLATORS,
+ sample_rate);
+ if (comp_rx_path[NUM_INTERPOLATORS - 1] < COMPANDER_MAX)
+ tomtom->comp_fs[comp_rx_path[j]] =
+ compander_fs;
+ }
+ }
+ return 0;
+}
+
+static int tomtom_set_decimator_rate(struct snd_soc_dai *dai,
+ u8 tx_fs_rate_reg_val, u32 sample_rate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ u32 tx_port;
+ u16 tx_port_reg, tx_fs_reg;
+ u8 tx_port_reg_val;
+ s8 decimator;
+
+ list_for_each_entry(ch, &tomtom->dai[dai->id].wcd9xxx_ch_list, list) {
+
+ tx_port = ch->port + 1;
+ pr_debug("%s: dai->id = %d, tx_port = %d",
+ __func__, dai->id, tx_port);
+
+ if ((tx_port < 1) || (tx_port > NUM_DECIMATORS)) {
+ pr_err("%s: Invalid SLIM TX%u port. DAI ID is %d\n",
+ __func__, tx_port, dai->id);
+ return -EINVAL;
+ }
+
+ tx_port_reg = TOMTOM_A_CDC_CONN_TX_SB_B1_CTL + (tx_port - 1);
+ tx_port_reg_val = snd_soc_read(codec, tx_port_reg);
+
+ decimator = 0;
+
+ if ((tx_port >= 1) && (tx_port <= 6)) {
+
+ tx_port_reg_val = tx_port_reg_val & 0x0F;
+ if (tx_port_reg_val == 0x8)
+ decimator = tx_port;
+
+ } else if ((tx_port >= 7) && (tx_port <= NUM_DECIMATORS)) {
+
+ tx_port_reg_val = tx_port_reg_val & 0x1F;
+
+ if ((tx_port_reg_val >= 0x8) &&
+ (tx_port_reg_val <= 0x11)) {
+
+ decimator = (tx_port_reg_val - 0x8) + 1;
+ }
+ }
+
+ if (decimator) { /* SLIM_TX port has a DEC as input */
+
+ tx_fs_reg = TOMTOM_A_CDC_TX1_CLK_FS_CTL +
+ 8 * (decimator - 1);
+
+ pr_debug("%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
+ __func__, decimator, tx_port, sample_rate);
+
+ snd_soc_update_bits(codec, tx_fs_reg, 0x07,
+ tx_fs_rate_reg_val);
+
+ } else {
+ if ((tx_port_reg_val >= 0x1) &&
+ (tx_port_reg_val <= 0x7)) {
+
+ pr_debug("%s: RMIX%u going to SLIM TX%u\n",
+ __func__, tx_port_reg_val, tx_port);
+
+ } else if ((tx_port_reg_val >= 0x8) &&
+ (tx_port_reg_val <= 0x11)) {
+
+ pr_err("%s: ERROR: Should not be here\n",
+ __func__);
+ pr_err("%s: ERROR: DEC connected to SLIM TX%u\n",
+ __func__, tx_port);
+ return -EINVAL;
+
+ } else if (tx_port_reg_val == 0) {
+ pr_debug("%s: no signal to SLIM TX%u\n",
+ __func__, tx_port);
+ } else {
+ pr_err("%s: ERROR: wrong signal to SLIM TX%u\n",
+ __func__, tx_port);
+ pr_err("%s: ERROR: wrong signal = %u\n",
+ __func__, tx_port_reg_val);
+ return -EINVAL;
+ }
+ }
+ }
+ return 0;
+}
+
+static void tomtom_set_rxsb_port_format(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_codec_dai_data *cdc_dai;
+ struct wcd9xxx_ch *ch;
+ int port;
+ u8 bit_sel;
+ u16 sb_ctl_reg, field_shift;
+
+ switch (params_width(params)) {
+ case 16:
+ bit_sel = 0x2;
+ tomtom_p->dai[dai->id].bit_width = 16;
+ break;
+ case 24:
+ bit_sel = 0x0;
+ tomtom_p->dai[dai->id].bit_width = 24;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid format\n");
+ return;
+ }
+
+ cdc_dai = &tomtom_p->dai[dai->id];
+
+ list_for_each_entry(ch, &cdc_dai->wcd9xxx_ch_list, list) {
+ port = wcd9xxx_get_slave_port(ch->ch_num);
+
+ if (IS_ERR_VALUE(port) ||
+ !TOMTOM_VALIDATE_RX_SBPORT_RANGE(port)) {
+ dev_warn(codec->dev,
+ "%s: invalid port ID %d returned for RX DAI\n",
+ __func__, port);
+ return;
+ }
+
+ port = TOMTOM_CONVERT_RX_SBPORT_ID(port);
+
+ if (port <= 3) {
+ sb_ctl_reg = TOMTOM_A_CDC_CONN_RX_SB_B1_CTL;
+ field_shift = port << 1;
+ } else if (port <= 7) {
+ sb_ctl_reg = TOMTOM_A_CDC_CONN_RX_SB_B2_CTL;
+ field_shift = (port - 4) << 1;
+ } else { /* should not happen */
+ dev_warn(codec->dev,
+ "%s: bad port ID %d\n", __func__, port);
+ return;
+ }
+
+ dev_dbg(codec->dev, "%s: sb_ctl_reg %x field_shift %x\n",
+ __func__, sb_ctl_reg, field_shift);
+ snd_soc_update_bits(codec, sb_ctl_reg, 0x3 << field_shift,
+ bit_sel << field_shift);
+ }
+}
+
+static void tomtom_set_tx_sb_port_format(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_codec_dai_data *cdc_dai;
+ struct wcd9xxx_ch *ch;
+ int port;
+ u8 bit_sel, bit_shift;
+ u16 sb_ctl_reg;
+
+ switch (params_width(params)) {
+ case 16:
+ bit_sel = 0x2;
+ tomtom_p->dai[dai->id].bit_width = 16;
+ break;
+ case 24:
+ bit_sel = 0x0;
+ tomtom_p->dai[dai->id].bit_width = 24;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid format %d\n", __func__,
+ params_width(params));
+ return;
+ }
+
+ cdc_dai = &tomtom_p->dai[dai->id];
+
+ list_for_each_entry(ch, &cdc_dai->wcd9xxx_ch_list, list) {
+ port = wcd9xxx_get_slave_port(ch->ch_num);
+
+ if (IS_ERR_VALUE(port) ||
+ !TOMTOM_VALIDATE_TX_SBPORT_RANGE(port)) {
+ dev_warn(codec->dev,
+ "%s: invalid port ID %d returned for TX DAI\n",
+ __func__, port);
+ return;
+ }
+
+ if (port < 6) /* 6 = SLIMBUS TX7 */
+ bit_shift = TOMTOM_BIT_ADJ_SHIFT_PORT1_6;
+ else if (port < 10)
+ bit_shift = TOMTOM_BIT_ADJ_SHIFT_PORT7_10;
+ else {
+ dev_warn(codec->dev,
+ "%s: port ID %d bitwidth is fixed\n",
+ __func__, port);
+ return;
+ }
+
+ sb_ctl_reg = (TOMTOM_A_CDC_CONN_TX_SB_B1_CTL + port);
+
+ dev_dbg(codec->dev, "%s: reg %x bit_sel %x bit_shift %x\n",
+ __func__, sb_ctl_reg, bit_sel, bit_shift);
+ snd_soc_update_bits(codec, sb_ctl_reg, 0x3 <<
+ bit_shift, bit_sel << bit_shift);
+ }
+}
+
+static int tomtom_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec);
+ u8 tx_fs_rate, rx_fs_rate, i2s_bit_mode;
+ u32 compander_fs;
+ int ret;
+
+ pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
+ dai->name, dai->id, params_rate(params),
+ params_channels(params));
+
+ switch (params_rate(params)) {
+ case 8000:
+ tx_fs_rate = 0x00;
+ rx_fs_rate = 0x00;
+ compander_fs = COMPANDER_FS_8KHZ;
+ break;
+ case 16000:
+ tx_fs_rate = 0x01;
+ rx_fs_rate = 0x20;
+ compander_fs = COMPANDER_FS_16KHZ;
+ break;
+ case 32000:
+ tx_fs_rate = 0x02;
+ rx_fs_rate = 0x40;
+ compander_fs = COMPANDER_FS_32KHZ;
+ break;
+ case 48000:
+ tx_fs_rate = 0x03;
+ rx_fs_rate = 0x60;
+ compander_fs = COMPANDER_FS_48KHZ;
+ break;
+ case 96000:
+ tx_fs_rate = 0x04;
+ rx_fs_rate = 0x80;
+ compander_fs = COMPANDER_FS_96KHZ;
+ break;
+ case 192000:
+ tx_fs_rate = 0x05;
+ rx_fs_rate = 0xA0;
+ compander_fs = COMPANDER_FS_192KHZ;
+ break;
+ default:
+ pr_err("%s: Invalid sampling rate %d\n", __func__,
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_CAPTURE:
+ if (dai->id != AIF4_VIFEED &&
+ dai->id != AIF4_MAD_TX) {
+ ret = tomtom_set_decimator_rate(dai, tx_fs_rate,
+ params_rate(params));
+ if (ret < 0) {
+ pr_err("%s: set decimator rate failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ tomtom->dai[dai->id].rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ i2s_bit_mode = 0x01;
+ tomtom->dai[dai->id].bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ tomtom->dai[dai->id].bit_width = 24;
+ i2s_bit_mode = 0x00;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ tomtom->dai[dai->id].bit_width = 32;
+ i2s_bit_mode = 0x00;
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: Invalid format 0x%x\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_TX_I2S_CTL,
+ 0x20, i2s_bit_mode << 5);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_TX_I2S_CTL,
+ 0x07, tx_fs_rate);
+ } else {
+ /* only generic ports can have sample bit adjustment */
+ if (dai->id != AIF4_VIFEED &&
+ dai->id != AIF4_MAD_TX)
+ tomtom_set_tx_sb_port_format(params, dai);
+ }
+
+ break;
+
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = tomtom_set_interpolator_rate(dai, rx_fs_rate,
+ compander_fs,
+ params_rate(params));
+ if (ret < 0) {
+ pr_err("%s: set decimator rate failed %d\n", __func__,
+ ret);
+ return ret;
+ }
+ if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_CLK_RX_I2S_CTL,
+ 0x20, 0x20);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_CLK_RX_I2S_CTL,
+ 0x20, 0x00);
+ break;
+ default:
+ pr_err("invalid format\n");
+ break;
+ }
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_I2S_CTL,
+ 0x03, (rx_fs_rate >> 0x05));
+ } else {
+ tomtom_set_rxsb_port_format(params, dai);
+ tomtom->dai[dai->id].rate = params_rate(params);
+ }
+ break;
+ default:
+ pr_err("%s: Invalid stream type %d\n", __func__,
+ substream->stream);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tomtom_dai_ops = {
+ .startup = tomtom_startup,
+ .shutdown = tomtom_shutdown,
+ .hw_params = tomtom_hw_params,
+ .set_sysclk = tomtom_set_dai_sysclk,
+ .set_fmt = tomtom_set_dai_fmt,
+ .set_channel_map = tomtom_set_channel_map,
+ .get_channel_map = tomtom_get_channel_map,
+};
+
+static struct snd_soc_dai_driver tomtom_dai[] = {
+ {
+ .name = "tomtom_rx1",
+ .id = AIF1_PB,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .rates = WCD9330_RATES,
+ .formats = TOMTOM_FORMATS_S16_S24_LE,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+ {
+ .name = "tomtom_tx1",
+ .id = AIF1_CAP,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .rates = WCD9330_RATES,
+ .formats = TOMTOM_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+ {
+ .name = "tomtom_rx2",
+ .id = AIF2_PB,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .rates = WCD9330_RATES,
+ .formats = TOMTOM_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+ {
+ .name = "tomtom_tx2",
+ .id = AIF2_CAP,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .rates = WCD9330_RATES,
+ .formats = TOMTOM_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+ {
+ .name = "tomtom_rx3",
+ .id = AIF3_PB,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .rates = WCD9330_RATES,
+ .formats = TOMTOM_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+ {
+ .name = "tomtom_tx3",
+ .id = AIF3_CAP,
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .rates = WCD9330_RATES,
+ .formats = TOMTOM_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+ {
+ .name = "tomtom_vifeedback",
+ .id = AIF4_VIFEED,
+ .capture = {
+ .stream_name = "VIfeed",
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = TOMTOM_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+ {
+ .name = "tomtom_mad1",
+ .id = AIF4_MAD_TX,
+ .capture = {
+ .stream_name = "AIF4 MAD TX",
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = TOMTOM_FORMATS_S16_S24_LE,
+ .rate_min = 16000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+};
+
+static struct snd_soc_dai_driver tomtom_i2s_dai[] = {
+ {
+ .name = "tomtom_i2s_rx1",
+ .id = AIF1_PB,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .rates = WCD9330_RATES,
+ .formats = TOMTOM_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+ {
+ .name = "tomtom_i2s_tx1",
+ .id = AIF1_CAP,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .rates = WCD9330_RATES,
+ .formats = TOMTOM_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+ {
+ .name = "tomtom_i2s_rx2",
+ .id = AIF1_PB,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .rates = WCD9330_RATES,
+ .formats = TOMTOM_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+ {
+ .name = "tomtom_i2s_tx2",
+ .id = AIF1_CAP,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .rates = WCD9330_RATES,
+ .formats = TOMTOM_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tomtom_dai_ops,
+ },
+};
+
+static int tomtom_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai,
+ bool up)
+{
+ int ret = 0;
+ struct wcd9xxx_ch *ch;
+
+ if (up) {
+ list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+ ret = wcd9xxx_get_slave_port(ch->ch_num);
+ if (ret < 0) {
+ pr_err("%s: Invalid slave port ID: %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ } else {
+ set_bit(ret, &dai->ch_mask);
+ }
+ }
+ } else {
+ ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0),
+ msecs_to_jiffies(
+ TOMTOM_SLIM_CLOSE_TIMEOUT));
+ if (!ret) {
+ pr_err("%s: Slim close tx/rx wait timeout\n", __func__);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static void tomtom_codec_enable_int_port(struct wcd9xxx_codec_dai_data *dai,
+ struct snd_soc_codec *codec)
+{
+ struct wcd9xxx_ch *ch;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ int port_num = 0;
+ unsigned short reg = 0;
+ u8 val = 0;
+ if (!dai || !codec) {
+ pr_err("%s: Invalid params\n", __func__);
+ return;
+ }
+ list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+ if (ch->port >= TOMTOM_RX_PORT_START_NUMBER) {
+ port_num = ch->port - TOMTOM_RX_PORT_START_NUMBER;
+ reg = TOMTOM_SLIM_PGD_PORT_INT_EN0 + (port_num / 8);
+ val = wcd9xxx_interface_reg_read(wcd9xxx,
+ reg);
+ if (!(val & (1 << (port_num % 8)))) {
+ val |= (1 << (port_num % 8));
+ wcd9xxx_interface_reg_write(
+ wcd9xxx, reg, val);
+ val = wcd9xxx_interface_reg_read(
+ wcd9xxx, reg);
+ }
+ } else {
+ port_num = ch->port;
+ reg = TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8);
+ val = wcd9xxx_interface_reg_read(wcd9xxx,
+ reg);
+ if (!(val & (1 << (port_num % 8)))) {
+ val |= (1 << (port_num % 8));
+ wcd9xxx_interface_reg_write(wcd9xxx,
+ reg, val);
+ val = wcd9xxx_interface_reg_read(
+ wcd9xxx, reg);
+ }
+ }
+ }
+}
+
+static int tomtom_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct wcd9xxx *core;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ struct wcd9xxx_codec_dai_data *dai;
+
+ core = dev_get_drvdata(codec->dev->parent);
+
+ pr_debug("%s: event called! codec name %s num_dai %d\n"
+ "stream name %s event %d\n",
+ __func__, codec->component.name,
+ codec->component.num_dai, w->sname, event);
+
+ /* Execute the callback only if interface type is slimbus */
+ if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ return 0;
+
+ dai = &tomtom_p->dai[w->shift];
+ pr_debug("%s: w->name %s w->shift %d event %d\n",
+ __func__, w->name, w->shift, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dai->bus_down_in_recovery = false;
+ tomtom_codec_enable_int_port(dai, codec);
+ (void) tomtom_codec_enable_slim_chmask(dai, true);
+ ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ if (!dai->bus_down_in_recovery)
+ ret = tomtom_codec_enable_slim_chmask(dai, false);
+ else
+ pr_debug("%s: bus in recovery skip enable slim_chmask",
+ __func__);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ pr_debug("%s: Disconnect RX port, ret = %d\n",
+ __func__, ret);
+ }
+ break;
+ }
+ return ret;
+}
+
+static int tomtom_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct wcd9xxx *core = NULL;
+ struct snd_soc_codec *codec = NULL;
+ struct tomtom_priv *tomtom_p = NULL;
+ u32 ret = 0;
+ struct wcd9xxx_codec_dai_data *dai = NULL;
+
+ if (!w) {
+ pr_err("%s invalid params\n", __func__);
+ return -EINVAL;
+ }
+ codec = snd_soc_dapm_to_codec(w->dapm);
+ tomtom_p = snd_soc_codec_get_drvdata(codec);
+ core = dev_get_drvdata(codec->dev->parent);
+
+ pr_debug("%s: event called! codec name %s num_dai %d stream name %s\n",
+ __func__, codec->component.name,
+ codec->component.num_dai, w->sname);
+
+ /* Execute the callback only if interface type is slimbus */
+ if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ pr_err("%s Interface is not correct", __func__);
+ return 0;
+ }
+
+ pr_debug("%s(): w->name %s event %d w->shift %d\n",
+ __func__, w->name, event, w->shift);
+ if (w->shift != AIF4_VIFEED) {
+ pr_err("%s Error in enabling the tx path\n", __func__);
+ ret = -EINVAL;
+ goto out_vi;
+ }
+ dai = &tomtom_p->dai[w->shift];
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /*Enable V&I sensing*/
+ snd_soc_update_bits(codec, TOMTOM_A_SPKR1_PROT_EN,
+ 0x88, 0x88);
+ /*Enable spkr VI clocks*/
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL, 0xC, 0xC);
+ dai->bus_down_in_recovery = false;
+ tomtom_codec_enable_int_port(dai, codec);
+ (void) tomtom_codec_enable_slim_chmask(dai, true);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ if (ret)
+ pr_err("%s error in close_slim_sch_tx %d\n",
+ __func__, ret);
+ if (!dai->bus_down_in_recovery)
+ ret = tomtom_codec_enable_slim_chmask(dai, false);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ pr_debug("%s: Disconnect TX port, ret = %d\n",
+ __func__, ret);
+ }
+
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL,
+ 0xC, 0x0);
+ /*Disable V&I sensing*/
+ snd_soc_update_bits(codec, TOMTOM_A_SPKR1_PROT_EN,
+ 0x88, 0x00);
+ break;
+ }
+out_vi:
+ return ret;
+}
+
+/* __tomtom_codec_enable_slimtx: Enable the slimbus slave port
+ * for TX path
+ * @codec: Handle to the codec for which the slave port is to be
+ * enabled.
+ * @dai_data: The dai specific data for dai which is enabled.
+ */
+static int __tomtom_codec_enable_slimtx(struct snd_soc_codec *codec,
+ int event, struct wcd9xxx_codec_dai_data *dai_data)
+{
+ struct wcd9xxx *core;
+ int ret = 0;
+
+ core = dev_get_drvdata(codec->dev->parent);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dai_data->bus_down_in_recovery = false;
+ tomtom_codec_enable_int_port(dai_data, codec);
+ (void) tomtom_codec_enable_slim_chmask(dai_data, true);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai_data->wcd9xxx_ch_list,
+ dai_data->rate,
+ dai_data->bit_width,
+ &dai_data->grph);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = wcd9xxx_close_slim_sch_tx(core,
+ &dai_data->wcd9xxx_ch_list,
+ dai_data->grph);
+ if (!dai_data->bus_down_in_recovery)
+ ret = tomtom_codec_enable_slim_chmask(dai_data, false);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai_data->wcd9xxx_ch_list,
+ dai_data->grph);
+ dev_dbg(codec->dev,
+ "%s: Disconnect TX port, ret = %d\n",
+ __func__, ret);
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * tomtom_codec_enable_slimtx_mad: Callback function that will be invoked
+ * to setup the slave port for MAD.
+ * @codec: Handle to the codec
+ * @event: Indicates whether to enable or disable the slave port
+ */
+static int tomtom_codec_enable_slimtx_mad(struct snd_soc_codec *codec,
+ u8 event)
+{
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_codec_dai_data *dai;
+ int dapm_event = SND_SOC_DAPM_POST_PMU;
+
+ dai = &tomtom_p->dai[AIF4_MAD_TX];
+
+ if (event == 0)
+ dapm_event = SND_SOC_DAPM_POST_PMD;
+
+ dev_dbg(codec->dev,
+ "%s: mad_channel, event = 0x%x\n",
+ __func__, event);
+ return __tomtom_codec_enable_slimtx(codec, dapm_event, dai);
+}
+
+/*
+ * tomtom_codec_enable_slimtx: DAPM widget allback for TX widgets
+ * @w: widget for which this callback is invoked
+ * @kcontrol: kcontrol associated with this widget
+ * @event: DAPM supplied event indicating enable/disable
+ */
+static int tomtom_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_codec_dai_data *dai;
+
+ dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d stream name %s\n",
+ __func__, codec->component.name,
+ codec->component.num_dai, w->sname);
+
+ /* Execute the callback only if interface type is slimbus */
+ if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ return 0;
+
+ dev_dbg(codec->dev,
+ "%s(): w->name %s event %d w->shift %d\n",
+ __func__, w->name, event, w->shift);
+
+ dai = &tomtom_p->dai[w->shift];
+ return __tomtom_codec_enable_slimtx(codec, event, dai);
+}
+
+static int tomtom_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+ WCD9XXX_CLSH_STATE_EAR,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
+
+ usleep_range(5000, 5100);
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+ WCD9XXX_CLSH_STATE_EAR,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_PRE_DAC);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+ WCD9XXX_CLSH_STATE_EAR,
+ WCD9XXX_CLSH_REQ_DISABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
+ usleep_range(5000, 5100);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_codec_set_iir_gain(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU: /* fall through */
+ case SND_SOC_DAPM_PRE_PMD:
+ if (strnstr(w->name, "IIR1", sizeof("IIR1"))) {
+ snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B1_CTL,
+ snd_soc_read(codec,
+ TOMTOM_A_CDC_IIR1_GAIN_B1_CTL));
+ snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B2_CTL,
+ snd_soc_read(codec,
+ TOMTOM_A_CDC_IIR1_GAIN_B2_CTL));
+ snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B3_CTL,
+ snd_soc_read(codec,
+ TOMTOM_A_CDC_IIR1_GAIN_B3_CTL));
+ snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B4_CTL,
+ snd_soc_read(codec,
+ TOMTOM_A_CDC_IIR1_GAIN_B4_CTL));
+ } else {
+ snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B1_CTL,
+ snd_soc_read(codec,
+ TOMTOM_A_CDC_IIR2_GAIN_B1_CTL));
+ snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B2_CTL,
+ snd_soc_read(codec,
+ TOMTOM_A_CDC_IIR2_GAIN_B2_CTL));
+ snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B3_CTL,
+ snd_soc_read(codec,
+ TOMTOM_A_CDC_IIR2_GAIN_B3_CTL));
+ snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B4_CTL,
+ snd_soc_read(codec,
+ TOMTOM_A_CDC_IIR2_GAIN_B4_CTL));
+ }
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_codec_dsm_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u8 reg_val, zoh_mux_val = 0x00;
+
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ reg_val = snd_soc_read(codec, TOMTOM_A_CDC_CONN_CLSH_CTL);
+
+ if ((reg_val & 0x30) == 0x10)
+ zoh_mux_val = 0x04;
+ else if ((reg_val & 0x30) == 0x20)
+ zoh_mux_val = 0x08;
+
+ if (zoh_mux_val != 0x00)
+ snd_soc_update_bits(codec,
+ TOMTOM_A_CDC_CONN_CLSH_CTL,
+ 0x0C, zoh_mux_val);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_CONN_CLSH_CTL,
+ 0x0C, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int tomtom_codec_enable_anc_ear(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = tomtom_codec_enable_anc(w, kcontrol, event);
+ msleep(50);
+ snd_soc_update_bits(codec, TOMTOM_A_RX_EAR_EN, 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ ret = tomtom_codec_enable_ear_pa(w, kcontrol, event);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, TOMTOM_A_RX_EAR_EN, 0x10, 0x00);
+ msleep(40);
+ ret |= tomtom_codec_enable_anc(w, kcontrol, event);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = tomtom_codec_enable_ear_pa(w, kcontrol, event);
+ break;
+ }
+ return ret;
+}
+
+/* Todo: Have seperate dapm widgets for I2S and Slimbus.
+ * Might Need to have callbacks registered only for slimbus
+ */
+static const struct snd_soc_dapm_widget tomtom_dapm_widgets[] = {
+ /*RX stuff */
+ SND_SOC_DAPM_OUTPUT("EAR"),
+
+ SND_SOC_DAPM_PGA_E("EAR PA", TOMTOM_A_RX_EAR_EN, 4, 0, NULL, 0,
+ tomtom_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MIXER_E("DAC1", TOMTOM_A_RX_EAR_EN, 6, 0, dac1_switch,
+ ARRAY_SIZE(dac1_switch), tomtom_codec_ear_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+ AIF1_PB, 0, tomtom_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+ AIF2_PB, 0, tomtom_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+ AIF3_PB, 0, tomtom_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TOMTOM_RX1, 0,
+ &slim_rx_mux[TOMTOM_RX1]),
+ SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TOMTOM_RX2, 0,
+ &slim_rx_mux[TOMTOM_RX2]),
+ SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TOMTOM_RX3, 0,
+ &slim_rx_mux[TOMTOM_RX3]),
+ SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TOMTOM_RX4, 0,
+ &slim_rx_mux[TOMTOM_RX4]),
+ SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TOMTOM_RX5, 0,
+ &slim_rx_mux[TOMTOM_RX5]),
+ SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TOMTOM_RX6, 0,
+ &slim_rx_mux[TOMTOM_RX6]),
+ SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TOMTOM_RX7, 0,
+ &slim_rx_mux[TOMTOM_RX7]),
+ SND_SOC_DAPM_MUX("SLIM RX8 MUX", SND_SOC_NOPM, TOMTOM_RX8, 0,
+ &slim_rx_mux[TOMTOM_RX8]),
+
+ SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX8", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Headphone */
+ SND_SOC_DAPM_OUTPUT("HEADPHONE"),
+ SND_SOC_DAPM_PGA_E("HPHL", TOMTOM_A_RX_HPH_CNP_EN, 5, 0, NULL, 0,
+ tomtom_hph_pa_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("HPHL DAC", TOMTOM_A_RX_HPH_L_DAC_CTL, 7, 0,
+ hphl_switch, ARRAY_SIZE(hphl_switch), tomtom_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("HPHR", TOMTOM_A_RX_HPH_CNP_EN, 4, 0, NULL, 0,
+ tomtom_hph_pa_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("HPHR DAC", NULL, TOMTOM_A_RX_HPH_R_DAC_CTL, 7, 0,
+ tomtom_hphr_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ /* Speaker */
+ SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT3"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT4"),
+ SND_SOC_DAPM_OUTPUT("SPK_OUT"),
+
+ SND_SOC_DAPM_PGA_E("LINEOUT1 PA", TOMTOM_A_RX_LINE_CNP_EN, 0, 0, NULL,
+ 0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT2 PA", TOMTOM_A_RX_LINE_CNP_EN, 1, 0, NULL,
+ 0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT3 PA", TOMTOM_A_RX_LINE_CNP_EN, 2, 0, NULL,
+ 0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT4 PA", TOMTOM_A_RX_LINE_CNP_EN, 3, 0, NULL,
+ 0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0 , NULL,
+ 0, tomtom_codec_enable_spk_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("SPK2 PA", SND_SOC_NOPM, 0, 0 , NULL,
+ 0, tomtom_codec_enable_spk_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("LINEOUT1 DAC", NULL, TOMTOM_A_RX_LINE_1_DAC_CTL, 7,
+ 0, tomtom_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("LINEOUT2 DAC", NULL, TOMTOM_A_RX_LINE_2_DAC_CTL, 7,
+ 0, tomtom_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("LINEOUT3 DAC", NULL, TOMTOM_A_RX_LINE_3_DAC_CTL, 7,
+ 0, tomtom_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("LINEOUT3 DAC GROUND", SND_SOC_NOPM, 0, 0,
+ &lineout3_ground_switch),
+ SND_SOC_DAPM_DAC_E("LINEOUT4 DAC", NULL, TOMTOM_A_RX_LINE_4_DAC_CTL, 7,
+ 0, tomtom_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("LINEOUT4 DAC GROUND", SND_SOC_NOPM, 0, 0,
+ &lineout4_ground_switch),
+
+ SND_SOC_DAPM_DAC_E("SPK DAC", NULL, TOMTOM_A_CDC_BOOST_TRGR_EN, 0, 0,
+ tomtom_spk_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("SPK2 DAC", NULL, TOMTOM_A_CDC_BOOST_TRGR_EN, 1, 0,
+ tomtom_spk_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("VDD_SPKDRV", SND_SOC_NOPM, 0, 0,
+ tomtom_codec_enable_vdd_spkr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("VDD_SPKDRV2", SND_SOC_NOPM, 0, 0,
+ tomtom_codec_enable_vdd_spkr2,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX7 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("RX1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER_E("RX3 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
+ 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER_E("RX4 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 3, 0, NULL,
+ 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER_E("RX5 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 4, 0, NULL,
+ 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER_E("RX6 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 5, 0, NULL,
+ 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER_E("RX7 MIX2", TOMTOM_A_CDC_CLK_RX_B1_CTL, 6, 0, NULL,
+ 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER_E("RX8 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 7, 0, NULL,
+ 0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX_E("RX1 INTERP", TOMTOM_A_CDC_CLK_RX_B1_CTL, 0, 0,
+ &rx1_interp_mux, tomtom_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX2 INTERP", TOMTOM_A_CDC_CLK_RX_B1_CTL, 1, 0,
+ &rx2_interp_mux, tomtom_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+
+ SND_SOC_DAPM_MIXER("RX1 CHAIN", TOMTOM_A_CDC_RX1_B6_CTL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX2 CHAIN", TOMTOM_A_CDC_RX2_B6_CTL, 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+ &rx_mix1_inp3_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx2_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx2_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx3_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx3_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX4 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx4_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX4 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx4_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX5 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx5_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX5 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx5_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX6 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx6_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX6 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx6_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX7 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx7_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX7 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx7_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX8 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx8_mix1_inp1_mux),
+ SND_SOC_DAPM_MUX("RX8 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx8_mix1_inp2_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+ &rx1_mix2_inp1_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX2 INP2", SND_SOC_NOPM, 0, 0,
+ &rx1_mix2_inp2_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+ &rx2_mix2_inp1_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX2 INP2", SND_SOC_NOPM, 0, 0,
+ &rx2_mix2_inp2_mux),
+ SND_SOC_DAPM_MUX("RX7 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+ &rx7_mix2_inp1_mux),
+ SND_SOC_DAPM_MUX("RX7 MIX2 INP2", SND_SOC_NOPM, 0, 0,
+ &rx7_mix2_inp2_mux),
+
+ SND_SOC_DAPM_MUX("RDAC5 MUX", SND_SOC_NOPM, 0, 0,
+ &rx_dac5_mux),
+ SND_SOC_DAPM_MUX("RDAC7 MUX", SND_SOC_NOPM, 0, 0,
+ &rx_dac7_mux),
+
+ SND_SOC_DAPM_MUX("MAD_SEL MUX", SND_SOC_NOPM, 0, 0,
+ &mad_sel_mux),
+
+ SND_SOC_DAPM_MUX_E("CLASS_H_DSM MUX", SND_SOC_NOPM, 0, 0,
+ &class_h_dsm_mux, tomtom_codec_dsm_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0,
+ tomtom_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("CDC_I2S_RX_CONN", WCD9XXX_A_CDC_CLK_OTHR_CTL, 5, 0,
+ NULL, 0),
+
+ /* TX */
+
+ SND_SOC_DAPM_SUPPLY("CDC_CONN", WCD9XXX_A_CDC_CLK_OTHR_CTL, 2, 0, NULL,
+ 0),
+
+ SND_SOC_DAPM_SUPPLY("LDO_H", SND_SOC_NOPM, 7, 0,
+ tomtom_codec_enable_ldo_h,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ /*
+ * DAPM 'LDO_H Standalone' is to be powered by mbhc driver after
+ * acquring codec_resource lock.
+ * So call __tomtom_codec_enable_ldo_h instead and avoid deadlock.
+ */
+ SND_SOC_DAPM_SUPPLY("LDO_H Standalone", SND_SOC_NOPM, 7, 0,
+ __tomtom_codec_enable_ldo_h,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("COMP0_CLK", SND_SOC_NOPM, 0, 0,
+ tomtom_config_compander, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("COMP1_CLK", SND_SOC_NOPM, 1, 0,
+ tomtom_config_compander, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("COMP2_CLK", SND_SOC_NOPM, 2, 0,
+ tomtom_config_compander, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 External", SND_SOC_NOPM, 7, 0,
+ tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 Internal1", SND_SOC_NOPM, 7, 0,
+ tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 Internal2", SND_SOC_NOPM, 7, 0,
+ tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("AMIC3"),
+
+ SND_SOC_DAPM_INPUT("AMIC4"),
+
+ SND_SOC_DAPM_INPUT("AMIC5"),
+
+ SND_SOC_DAPM_INPUT("AMIC6"),
+
+ SND_SOC_DAPM_MUX_E("DEC1 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0,
+ &dec1_mux, tomtom_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC2 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 1, 0,
+ &dec2_mux, tomtom_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC3 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 2, 0,
+ &dec3_mux, tomtom_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC4 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 3, 0,
+ &dec4_mux, tomtom_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC5 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 4, 0,
+ &dec5_mux, tomtom_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC6 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 5, 0,
+ &dec6_mux, tomtom_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC7 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 6, 0,
+ &dec7_mux, tomtom_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC8 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 7, 0,
+ &dec8_mux, tomtom_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC9 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL, 0, 0,
+ &dec9_mux, tomtom_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC10 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL, 1, 0,
+ &dec10_mux, tomtom_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("ANC1 MUX", SND_SOC_NOPM, 0, 0, &anc1_mux),
+ SND_SOC_DAPM_MUX("ANC2 MUX", SND_SOC_NOPM, 0, 0, &anc2_mux),
+
+ SND_SOC_DAPM_OUTPUT("ANC HEADPHONE"),
+ SND_SOC_DAPM_PGA_E("ANC HPHL", SND_SOC_NOPM, 5, 0, NULL, 0,
+ tomtom_codec_enable_anc_hph,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("ANC HPHR", SND_SOC_NOPM, 4, 0, NULL, 0,
+ tomtom_codec_enable_anc_hph, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("ANC EAR"),
+ SND_SOC_DAPM_PGA_E("ANC EAR PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tomtom_codec_enable_anc_ear,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
+
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_EXTERNAL_STANDALONE, SND_SOC_NOPM,
+ 7, 0, tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 External", SND_SOC_NOPM, 7, 0,
+ tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 Internal1", SND_SOC_NOPM, 7, 0,
+ tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 Internal2", SND_SOC_NOPM, 7, 0,
+ tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 Internal3", SND_SOC_NOPM, 7, 0,
+ tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 External", SND_SOC_NOPM, 7, 0,
+ tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 Internal1", SND_SOC_NOPM, 7, 0,
+ tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 Internal2", SND_SOC_NOPM, 7, 0,
+ tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS4 External", SND_SOC_NOPM, 7,
+ 0, tomtom_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+ AIF1_CAP, 0, tomtom_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+ AIF2_CAP, 0, tomtom_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+ AIF3_CAP, 0, tomtom_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM,
+ AIF4_VIFEED, 0, tomtom_codec_enable_slimvi_feedback,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF4 MAD", "AIF4 MAD TX", 0,
+ SND_SOC_NOPM, 0, 0,
+ tomtom_codec_enable_mad,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("MADONOFF", SND_SOC_NOPM, 0, 0,
+ &aif4_mad_switch),
+ SND_SOC_DAPM_INPUT("MADINPUT"),
+ SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"),
+
+ SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+ aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+ aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+ aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)),
+
+ SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TOMTOM_TX1, 0,
+ &sb_tx1_mux),
+ SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TOMTOM_TX2, 0,
+ &sb_tx2_mux),
+ SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TOMTOM_TX3, 0,
+ &sb_tx3_mux),
+ SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TOMTOM_TX4, 0,
+ &sb_tx4_mux),
+ SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TOMTOM_TX5, 0,
+ &sb_tx5_mux),
+ SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TOMTOM_TX6, 0,
+ &sb_tx6_mux),
+ SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TOMTOM_TX7, 0,
+ &sb_tx7_mux),
+ SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TOMTOM_TX8, 0,
+ &sb_tx8_mux),
+ SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TOMTOM_TX9, 0,
+ &sb_tx9_mux),
+ SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TOMTOM_TX10, 0,
+ &sb_tx10_mux),
+
+ /* Digital Mic Inputs */
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+ tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+ tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0,
+ tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0,
+ tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0,
+ tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 0, 0,
+ tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* Sidetone */
+ SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+
+ SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux),
+
+ SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux),
+
+ SND_SOC_DAPM_MUX("IIR1 INP4 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp4_mux),
+
+ SND_SOC_DAPM_MIXER_E("IIR1", TOMTOM_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0,
+ tomtom_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux),
+
+ SND_SOC_DAPM_MUX("IIR2 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp2_mux),
+
+ SND_SOC_DAPM_MUX("IIR2 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp3_mux),
+
+ SND_SOC_DAPM_MUX("IIR2 INP4 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp4_mux),
+
+ SND_SOC_DAPM_MIXER_E("IIR2", TOMTOM_A_CDC_CLK_SD_CTL, 1, 0, NULL, 0,
+ tomtom_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ /* AUX PGA */
+ SND_SOC_DAPM_ADC_E("AUX_PGA_Left", NULL, TOMTOM_A_RX_AUX_SW_CTL, 7, 0,
+ tomtom_codec_enable_aux_pga, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("AUX_PGA_Right", NULL, TOMTOM_A_RX_AUX_SW_CTL, 6, 0,
+ tomtom_codec_enable_aux_pga, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ /* Lineout, ear and HPH PA Mixers */
+
+ SND_SOC_DAPM_MIXER("EAR_PA_MIXER", SND_SOC_NOPM, 0, 0,
+ ear_pa_mix, ARRAY_SIZE(ear_pa_mix)),
+
+ SND_SOC_DAPM_MIXER("HPHL_PA_MIXER", SND_SOC_NOPM, 0, 0,
+ hphl_pa_mix, ARRAY_SIZE(hphl_pa_mix)),
+
+ SND_SOC_DAPM_MIXER("HPHR_PA_MIXER", SND_SOC_NOPM, 0, 0,
+ hphr_pa_mix, ARRAY_SIZE(hphr_pa_mix)),
+
+ SND_SOC_DAPM_MIXER("LINEOUT1_PA_MIXER", SND_SOC_NOPM, 0, 0,
+ lineout1_pa_mix, ARRAY_SIZE(lineout1_pa_mix)),
+
+ SND_SOC_DAPM_MIXER("LINEOUT2_PA_MIXER", SND_SOC_NOPM, 0, 0,
+ lineout2_pa_mix, ARRAY_SIZE(lineout2_pa_mix)),
+
+ SND_SOC_DAPM_MIXER("LINEOUT3_PA_MIXER", SND_SOC_NOPM, 0, 0,
+ lineout3_pa_mix, ARRAY_SIZE(lineout3_pa_mix)),
+
+ SND_SOC_DAPM_MIXER("LINEOUT4_PA_MIXER", SND_SOC_NOPM, 0, 0,
+ lineout4_pa_mix, ARRAY_SIZE(lineout4_pa_mix)),
+
+ SND_SOC_DAPM_SWITCH("VIONOFF", SND_SOC_NOPM, 0, 0,
+ &aif4_vi_switch),
+
+ SND_SOC_DAPM_INPUT("VIINPUT"),
+};
+
+static irqreturn_t tomtom_slimbus_irq(int irq, void *data)
+{
+ struct tomtom_priv *priv = data;
+ struct snd_soc_codec *codec = priv->codec;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ unsigned long status = 0;
+ int i, j, port_id, k;
+ u32 bit;
+ u8 val, int_val = 0;
+ bool tx, cleared;
+ unsigned short reg = 0;
+
+ for (i = TOMTOM_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0;
+ i <= TOMTOM_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) {
+ val = wcd9xxx_interface_reg_read(wcd9xxx, i);
+ status |= ((u32)val << (8 * j));
+ }
+
+ for_each_set_bit(j, &status, 32) {
+ tx = (j >= 16 ? true : false);
+ port_id = (tx ? j - 16 : j);
+ val = wcd9xxx_interface_reg_read(wcd9xxx,
+ TOMTOM_SLIM_PGD_PORT_INT_RX_SOURCE0 + j);
+ if (val) {
+ if (!tx)
+ reg = TOMTOM_SLIM_PGD_PORT_INT_EN0 +
+ (port_id / 8);
+ else
+ reg = TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 +
+ (port_id / 8);
+ int_val = wcd9xxx_interface_reg_read(
+ wcd9xxx, reg);
+ /*
+ * Ignore interrupts for ports for which the
+ * interrupts are not specifically enabled.
+ */
+ if (!(int_val & (1 << (port_id % 8))))
+ continue;
+ }
+ if (val & TOMTOM_SLIM_IRQ_OVERFLOW)
+ pr_err_ratelimited(
+ "%s: overflow error on %s port %d, value %x\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val);
+ if (val & TOMTOM_SLIM_IRQ_UNDERFLOW)
+ pr_err_ratelimited(
+ "%s: underflow error on %s port %d, value %x\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val);
+ if ((val & TOMTOM_SLIM_IRQ_OVERFLOW) ||
+ (val & TOMTOM_SLIM_IRQ_UNDERFLOW)) {
+ if (!tx)
+ reg = TOMTOM_SLIM_PGD_PORT_INT_EN0 +
+ (port_id / 8);
+ else
+ reg = TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 +
+ (port_id / 8);
+ int_val = wcd9xxx_interface_reg_read(wcd9xxx, reg);
+ if (int_val & (1 << (port_id % 8))) {
+ int_val = int_val ^ (1 << (port_id % 8));
+ wcd9xxx_interface_reg_write(wcd9xxx, reg,
+ int_val);
+ }
+ }
+ if (val & TOMTOM_SLIM_IRQ_PORT_CLOSED) {
+ /*
+ * INT SOURCE register starts from RX to TX
+ * but port number in the ch_mask is in opposite way
+ */
+ bit = (tx ? j - 16 : j + 16);
+ pr_debug("%s: %s port %d closed value %x, bit %u\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val,
+ bit);
+ for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) {
+ pr_debug("%s: priv->dai[%d].ch_mask = 0x%lx\n",
+ __func__, k, priv->dai[k].ch_mask);
+ if (test_and_clear_bit(bit,
+ &priv->dai[k].ch_mask)) {
+ cleared = true;
+ if (!priv->dai[k].ch_mask)
+ wake_up(&priv->dai[k].dai_wait);
+ /*
+ * There are cases when multiple DAIs
+ * might be using the same slimbus
+ * channel. Hence don't break here.
+ */
+ }
+ }
+ WARN(!cleared,
+ "Couldn't find slimbus %s port %d for closing\n",
+ (tx ? "TX" : "RX"), port_id);
+ }
+ wcd9xxx_interface_reg_write(wcd9xxx,
+ TOMTOM_SLIM_PGD_PORT_INT_CLR_RX_0 +
+ (j / 8),
+ 1 << (j % 8));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int tomtom_handle_pdata(struct tomtom_priv *tomtom)
+{
+ struct snd_soc_codec *codec = tomtom->codec;
+ struct wcd9xxx_pdata *pdata = tomtom->resmgr.pdata;
+ int k1, k2, k3, dec, rc = 0;
+ u8 leg_mode, txfe_bypass, txfe_buff, flag;
+ u8 i = 0, j = 0;
+ u8 val_txfe = 0, value = 0;
+ u8 dmic_ctl_val, mad_dmic_ctl_val;
+ u8 anc_ctl_value = 0;
+ u32 def_dmic_rate;
+ u16 tx_dmic_ctl_reg;
+
+ if (!pdata) {
+ pr_err("%s: NULL pdata\n", __func__);
+ rc = -ENODEV;
+ goto done;
+ }
+
+ leg_mode = pdata->amic_settings.legacy_mode;
+ txfe_bypass = pdata->amic_settings.txfe_enable;
+ txfe_buff = pdata->amic_settings.txfe_buff;
+ flag = pdata->amic_settings.use_pdata;
+
+ /* Make sure settings are correct */
+ if ((pdata->micbias.ldoh_v > WCD9XXX_LDOH_3P0_V) ||
+ (pdata->micbias.bias1_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias2_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias3_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias4_cfilt_sel > WCD9XXX_CFILT3_SEL)) {
+ rc = -EINVAL;
+ goto done;
+ }
+ /* figure out k value */
+ k1 = wcd9xxx_resmgr_get_k_val(&tomtom->resmgr,
+ pdata->micbias.cfilt1_mv);
+ k2 = wcd9xxx_resmgr_get_k_val(&tomtom->resmgr,
+ pdata->micbias.cfilt2_mv);
+ k3 = wcd9xxx_resmgr_get_k_val(&tomtom->resmgr,
+ pdata->micbias.cfilt3_mv);
+
+ if (IS_ERR_VALUE(k1) || IS_ERR_VALUE(k2) || IS_ERR_VALUE(k3)) {
+ rc = -EINVAL;
+ goto done;
+ }
+ /* Set voltage level and always use LDO */
+ snd_soc_update_bits(codec, TOMTOM_A_LDO_H_MODE_1, 0x0C,
+ (pdata->micbias.ldoh_v << 2));
+
+ snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_1_VAL, 0xFC, (k1 << 2));
+ snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_2_VAL, 0xFC, (k2 << 2));
+ snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_3_VAL, 0xFC, (k3 << 2));
+
+ snd_soc_update_bits(codec, TOMTOM_A_MICB_1_CTL, 0x60,
+ (pdata->micbias.bias1_cfilt_sel << 5));
+ snd_soc_update_bits(codec, TOMTOM_A_MICB_2_CTL, 0x60,
+ (pdata->micbias.bias2_cfilt_sel << 5));
+ snd_soc_update_bits(codec, TOMTOM_A_MICB_3_CTL, 0x60,
+ (pdata->micbias.bias3_cfilt_sel << 5));
+ snd_soc_update_bits(codec, tomtom->resmgr.reg_addr->micb_4_ctl, 0x60,
+ (pdata->micbias.bias4_cfilt_sel << 5));
+
+ for (i = 0; i < 6; j++, i += 2) {
+ if (flag & (0x01 << i)) {
+ val_txfe = (txfe_bypass & (0x01 << i)) ? 0x20 : 0x00;
+ val_txfe = val_txfe |
+ ((txfe_buff & (0x01 << i)) ? 0x10 : 0x00);
+ snd_soc_update_bits(codec,
+ TOMTOM_A_TX_1_2_TEST_EN + j * 10,
+ 0x30, val_txfe);
+ }
+ if (flag & (0x01 << (i + 1))) {
+ val_txfe = (txfe_bypass &
+ (0x01 << (i + 1))) ? 0x02 : 0x00;
+ val_txfe |= (txfe_buff &
+ (0x01 << (i + 1))) ? 0x01 : 0x00;
+ snd_soc_update_bits(codec,
+ TOMTOM_A_TX_1_2_TEST_EN + j * 10,
+ 0x03, val_txfe);
+ }
+ }
+ if (flag & 0x40) {
+ value = (leg_mode & 0x40) ? 0x10 : 0x00;
+ value = value | ((txfe_bypass & 0x40) ? 0x02 : 0x00);
+ value = value | ((txfe_buff & 0x40) ? 0x01 : 0x00);
+ snd_soc_update_bits(codec, TOMTOM_A_TX_7_MBHC_EN,
+ 0x13, value);
+ }
+
+ if (pdata->ocp.use_pdata) {
+ /* not defined in CODEC specification */
+ if (pdata->ocp.hph_ocp_limit == 1 ||
+ pdata->ocp.hph_ocp_limit == 5) {
+ rc = -EINVAL;
+ goto done;
+ }
+ snd_soc_update_bits(codec, TOMTOM_A_RX_COM_OCP_CTL,
+ 0x0F, pdata->ocp.num_attempts);
+ snd_soc_write(codec, TOMTOM_A_RX_COM_OCP_COUNT,
+ ((pdata->ocp.run_time << 4) | pdata->ocp.wait_time));
+ snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_OCP_CTL,
+ 0xE0, (pdata->ocp.hph_ocp_limit << 5));
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+ if (pdata->regulator[i].name &&
+ !strcmp(pdata->regulator[i].name, "CDC_VDDA_RX")) {
+ if (pdata->regulator[i].min_uV == 1800000 &&
+ pdata->regulator[i].max_uV == 1800000) {
+ snd_soc_write(codec, TOMTOM_A_BIAS_REF_CTL,
+ 0x1C);
+ } else if (pdata->regulator[i].min_uV == 2200000 &&
+ pdata->regulator[i].max_uV == 2200000) {
+ snd_soc_write(codec, TOMTOM_A_BIAS_REF_CTL,
+ 0x1E);
+ } else {
+ pr_err("%s: unsupported CDC_VDDA_RX voltage\n"
+ "min %d, max %d\n", __func__,
+ pdata->regulator[i].min_uV,
+ pdata->regulator[i].max_uV);
+ rc = -EINVAL;
+ }
+ break;
+ }
+ }
+
+ /* Set micbias capless mode with tail current */
+ value = (pdata->micbias.bias1_cap_mode == MICBIAS_EXT_BYP_CAP ?
+ 0x00 : 0x16);
+ snd_soc_update_bits(codec, TOMTOM_A_MICB_1_CTL, 0x1E, value);
+ value = (pdata->micbias.bias2_cap_mode == MICBIAS_EXT_BYP_CAP ?
+ 0x00 : 0x16);
+ snd_soc_update_bits(codec, TOMTOM_A_MICB_2_CTL, 0x1E, value);
+ value = (pdata->micbias.bias3_cap_mode == MICBIAS_EXT_BYP_CAP ?
+ 0x00 : 0x16);
+ snd_soc_update_bits(codec, TOMTOM_A_MICB_3_CTL, 0x1E, value);
+ value = (pdata->micbias.bias4_cap_mode == MICBIAS_EXT_BYP_CAP ?
+ 0x00 : 0x16);
+ snd_soc_update_bits(codec, TOMTOM_A_MICB_4_CTL, 0x1E, value);
+
+ /* Set the DMIC sample rate */
+ switch (pdata->mclk_rate) {
+ case TOMTOM_MCLK_CLK_9P6MHZ:
+ def_dmic_rate =
+ WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+ break;
+ case TOMTOM_MCLK_CLK_12P288MHZ:
+ def_dmic_rate =
+ WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ;
+ break;
+ default:
+ /* should never happen */
+ pr_err("%s: Invalid mclk_rate %d\n",
+ __func__, pdata->mclk_rate);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (pdata->dmic_sample_rate ==
+ WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+ pr_info("%s: dmic_rate invalid default = %d\n",
+ __func__, def_dmic_rate);
+ pdata->dmic_sample_rate = def_dmic_rate;
+ }
+
+ if (pdata->mad_dmic_sample_rate ==
+ WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+ pr_info("%s: mad_dmic_rate invalid default = %d\n",
+ __func__, def_dmic_rate);
+ /*
+ * use dmic_sample_rate as the default for MAD
+ * if mad dmic sample rate is undefined
+ */
+ pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate;
+ }
+
+ /*
+ * Default the DMIC clk rates to mad_dmic_sample_rate,
+ * whereas, the anc/txfe dmic rates to dmic_sample_rate
+ * since the anc/txfe are independent of mad block.
+ */
+ mad_dmic_ctl_val = tomtom_get_dmic_clk_val(tomtom->codec,
+ pdata->mclk_rate,
+ pdata->mad_dmic_sample_rate);
+ snd_soc_update_bits(codec, TOMTOM_A_DMIC_B1_CTL,
+ 0xE0, mad_dmic_ctl_val << 5);
+ snd_soc_update_bits(codec, TOMTOM_A_DMIC_B2_CTL,
+ 0x70, mad_dmic_ctl_val << 4);
+ snd_soc_update_bits(codec, TOMTOM_A_DMIC_B2_CTL,
+ 0x0E, mad_dmic_ctl_val << 1);
+
+ dmic_ctl_val = tomtom_get_dmic_clk_val(tomtom->codec,
+ pdata->mclk_rate,
+ pdata->dmic_sample_rate);
+
+ if (dmic_ctl_val == WCD9330_DMIC_CLK_DIV_2)
+ anc_ctl_value = WCD9XXX_ANC_DMIC_X2_ON;
+ else
+ anc_ctl_value = WCD9XXX_ANC_DMIC_X2_OFF;
+
+ for (dec = 0; dec < NUM_DECIMATORS; dec++) {
+ tx_dmic_ctl_reg =
+ TOMTOM_A_CDC_TX1_DMIC_CTL + (8 * dec);
+ snd_soc_update_bits(codec, tx_dmic_ctl_reg,
+ 0x07, dmic_ctl_val);
+ }
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC1_B2_CTL,
+ 0x1, anc_ctl_value);
+ snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC2_B2_CTL,
+ 0x1, anc_ctl_value);
+done:
+ return rc;
+}
+
+static const struct wcd9xxx_reg_mask_val tomtom_reg_defaults[] = {
+
+ /* set MCLk to 9.6 */
+ TOMTOM_REG_VAL(TOMTOM_A_CHIP_CTL, 0x02),
+
+ /* EAR PA deafults */
+ TOMTOM_REG_VAL(TOMTOM_A_RX_EAR_CMBUFF, 0x05),
+
+ /* RX deafults */
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX1_B5_CTL, 0x79),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX2_B5_CTL, 0x79),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX3_B5_CTL, 0x79),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX4_B5_CTL, 0x79),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX5_B5_CTL, 0x79),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX6_B5_CTL, 0x79),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX7_B5_CTL, 0x79),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX8_B5_CTL, 0x79),
+
+ /* RX1 and RX2 defaults */
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX1_B6_CTL, 0xA0),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX2_B6_CTL, 0xA0),
+
+ /* RX3 to RX7 defaults */
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX3_B6_CTL, 0x80),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX4_B6_CTL, 0x80),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX5_B6_CTL, 0x80),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX6_B6_CTL, 0x80),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX7_B6_CTL, 0x80),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX8_B6_CTL, 0x80),
+
+ /* MAD registers */
+ TOMTOM_REG_VAL(TOMTOM_A_MAD_ANA_CTRL, 0xF1),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_MAIN_CTL_1, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_MAIN_CTL_2, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_1, 0x00),
+ /* Set SAMPLE_TX_EN bit */
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_2, 0x03),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_3, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_4, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_5, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_6, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_7, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_8, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL, 0x40),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_DEBUG_B7_CTL, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_CLK_OTHR_CTL, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_INP_SEL, 0x01),
+
+ /* Set HPH Path to low power mode */
+ TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_BIAS_PA, 0x57),
+
+ /* BUCK default */
+ TOMTOM_REG_VAL(TOMTOM_A_BUCK_CTRL_CCL_4, 0x51),
+ TOMTOM_REG_VAL(TOMTOM_A_BUCK_CTRL_CCL_1, 0x5B),
+};
+
+/*
+ * Don't update TOMTOM_A_CHIP_CTL, TOMTOM_A_BUCK_CTRL_CCL_1 and
+ * TOMTOM_A_RX_EAR_CMBUFF as those are updated in tomtom_reg_defaults
+ */
+static const struct wcd9xxx_reg_mask_val tomtom_1_0_reg_defaults[] = {
+ TOMTOM_REG_VAL(TOMTOM_A_TX_1_GAIN, 0x2),
+ TOMTOM_REG_VAL(TOMTOM_A_TX_2_GAIN, 0x2),
+ TOMTOM_REG_VAL(TOMTOM_A_TX_1_2_ADC_IB, 0x44),
+ TOMTOM_REG_VAL(TOMTOM_A_TX_3_GAIN, 0x2),
+ TOMTOM_REG_VAL(TOMTOM_A_TX_4_GAIN, 0x2),
+ TOMTOM_REG_VAL(TOMTOM_A_TX_3_4_ADC_IB, 0x44),
+ TOMTOM_REG_VAL(TOMTOM_A_TX_5_GAIN, 0x2),
+ TOMTOM_REG_VAL(TOMTOM_A_TX_6_GAIN, 0x2),
+ TOMTOM_REG_VAL(TOMTOM_A_TX_5_6_ADC_IB, 0x44),
+ TOMTOM_REG_VAL(WCD9XXX_A_BUCK_MODE_3, 0xCE),
+ TOMTOM_REG_VAL(WCD9XXX_A_BUCK_CTRL_VCL_1, 0x8),
+ TOMTOM_REG_VAL(TOMTOM_A_BUCK_CTRL_CCL_4, 0x51),
+ TOMTOM_REG_VAL(TOMTOM_A_NCP_DTEST, 0x10),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_CHOP_CTL, 0xA4),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_OCP_CTL, 0x69),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xDA),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_CNP_WG_TIME, 0x15),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_EAR_BIAS_PA, 0x76),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_EAR_CNP, 0xC0),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_BIAS_PA, 0x78),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_1_TEST, 0x2),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_2_TEST, 0x2),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_3_TEST, 0x2),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_4_TEST, 0x2),
+ TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV1_OCP_CTL, 0x97),
+ TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV1_CLIP_DET, 0x1),
+ TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV1_IEC, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV2_OCP_CTL, 0x97),
+ TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV2_CLIP_DET, 0x1),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX1_MUX_CTL, 0x4A),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX2_MUX_CTL, 0x4A),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX3_MUX_CTL, 0x4A),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX4_MUX_CTL, 0x4A),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX5_MUX_CTL, 0x4A),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX6_MUX_CTL, 0x4A),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX7_MUX_CTL, 0x4A),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX8_MUX_CTL, 0x4A),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX9_MUX_CTL, 0x4A),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX10_MUX_CTL, 0x4A),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX1_B4_CTL, 0xB),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX2_B4_CTL, 0xB),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX3_B4_CTL, 0xB),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX4_B4_CTL, 0xB),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX5_B4_CTL, 0xB),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX6_B4_CTL, 0xB),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX7_B4_CTL, 0xB),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX8_B4_CTL, 0xB),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_VBAT_GAIN_UPD_MON, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B1_CTL, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B2_CTL, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B3_CTL, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B4_CTL, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_COMP0_B4_CTL, 0x37),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_COMP0_B5_CTL, 0x7f),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_COMP0_B5_CTL, 0x7f),
+};
+
+static const struct wcd9xxx_reg_mask_val tomtom_2_0_reg_defaults[] = {
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_MAIN_CTL_2, 0x32),
+ TOMTOM_REG_VAL(TOMTOM_A_RCO_CTRL, 0x10),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_L_TEST, 0x0A),
+ TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_R_TEST, 0x0A),
+ TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE0, 0xC3),
+ TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_DATA0, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_SCK_MODE, 0x04),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_WS_MODE, 0x04),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_SCK_MODE, 0x04),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_WS_MODE, 0x04),
+ TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE1, 0xE0),
+ TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE2, 0x03),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_JTCK_MODE, 0x04),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_JTDI_MODE, 0x04),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_JTMS_MODE, 0x04),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_JTDO_MODE, 0x04),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_JTRST_MODE, 0x04),
+};
+
+static const struct wcd9xxx_reg_mask_val tomtom_2_0_reg_i2c_defaults[] = {
+ TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE0, 0x00),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_SCK_MODE, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_WS_MODE, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_SCK_MODE, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_WS_MODE, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE1, 0x0),
+ TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE2, 0x0),
+};
+
+static void tomtom_update_reg_defaults(struct snd_soc_codec *codec)
+{
+ u32 i;
+ struct wcd9xxx *tomtom_core = dev_get_drvdata(codec->dev->parent);
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ for (i = 0; i < ARRAY_SIZE(tomtom_reg_defaults); i++)
+ snd_soc_write(codec, tomtom_reg_defaults[i].reg,
+ tomtom_reg_defaults[i].val);
+
+ for (i = 0; i < ARRAY_SIZE(tomtom_1_0_reg_defaults); i++)
+ snd_soc_write(codec, tomtom_1_0_reg_defaults[i].reg,
+ tomtom_1_0_reg_defaults[i].val);
+
+ if (!TOMTOM_IS_1_0(tomtom_core->version)) {
+ for (i = 0; i < ARRAY_SIZE(tomtom_2_0_reg_defaults); i++)
+ snd_soc_write(codec, tomtom_2_0_reg_defaults[i].reg,
+ tomtom_2_0_reg_defaults[i].val);
+
+ if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ for (i = 0; i < ARRAY_SIZE(tomtom_2_0_reg_i2c_defaults);
+ i++)
+ snd_soc_write(codec,
+ tomtom_2_0_reg_i2c_defaults[i].reg,
+ tomtom_2_0_reg_i2c_defaults[i].val);
+ }
+ }
+}
+
+static const struct wcd9xxx_reg_mask_val tomtom_codec_reg_init_val[] = {
+ /* Initialize current threshold to 350MA
+ * number of wait and run cycles to 4096
+ */
+ {TOMTOM_A_RX_HPH_OCP_CTL, 0xE1, 0x61},
+ {TOMTOM_A_RX_COM_OCP_COUNT, 0xFF, 0xFF},
+ {TOMTOM_A_RX_HPH_L_TEST, 0x01, 0x01},
+ {TOMTOM_A_RX_HPH_R_TEST, 0x01, 0x01},
+
+ /* Initialize gain registers to use register gain */
+ {TOMTOM_A_RX_HPH_L_GAIN, 0x20, 0x20},
+ {TOMTOM_A_RX_HPH_R_GAIN, 0x20, 0x20},
+ {TOMTOM_A_RX_LINE_1_GAIN, 0x20, 0x20},
+ {TOMTOM_A_RX_LINE_2_GAIN, 0x20, 0x20},
+ {TOMTOM_A_RX_LINE_3_GAIN, 0x20, 0x20},
+ {TOMTOM_A_RX_LINE_4_GAIN, 0x20, 0x20},
+ {TOMTOM_A_SPKR_DRV1_GAIN, 0x04, 0x04},
+ {TOMTOM_A_SPKR_DRV2_GAIN, 0x04, 0x04},
+
+ /* Use 16 bit sample size for TX1 to TX6 */
+ {TOMTOM_A_CDC_CONN_TX_SB_B1_CTL, 0x30, 0x20},
+ {TOMTOM_A_CDC_CONN_TX_SB_B2_CTL, 0x30, 0x20},
+ {TOMTOM_A_CDC_CONN_TX_SB_B3_CTL, 0x30, 0x20},
+ {TOMTOM_A_CDC_CONN_TX_SB_B4_CTL, 0x30, 0x20},
+ {TOMTOM_A_CDC_CONN_TX_SB_B5_CTL, 0x30, 0x20},
+ {TOMTOM_A_CDC_CONN_TX_SB_B6_CTL, 0x30, 0x20},
+
+ /* Use 16 bit sample size for TX7 to TX10 */
+ {TOMTOM_A_CDC_CONN_TX_SB_B7_CTL, 0x60, 0x40},
+ {TOMTOM_A_CDC_CONN_TX_SB_B8_CTL, 0x60, 0x40},
+ {TOMTOM_A_CDC_CONN_TX_SB_B9_CTL, 0x60, 0x40},
+ {TOMTOM_A_CDC_CONN_TX_SB_B10_CTL, 0x60, 0x40},
+
+ /*enable HPF filter for TX paths */
+ {TOMTOM_A_CDC_TX1_MUX_CTL, 0x8, 0x0},
+ {TOMTOM_A_CDC_TX2_MUX_CTL, 0x8, 0x0},
+ {TOMTOM_A_CDC_TX3_MUX_CTL, 0x8, 0x0},
+ {TOMTOM_A_CDC_TX4_MUX_CTL, 0x8, 0x0},
+ {TOMTOM_A_CDC_TX5_MUX_CTL, 0x8, 0x0},
+ {TOMTOM_A_CDC_TX6_MUX_CTL, 0x8, 0x0},
+ {TOMTOM_A_CDC_TX7_MUX_CTL, 0x8, 0x0},
+ {TOMTOM_A_CDC_TX8_MUX_CTL, 0x8, 0x0},
+ {TOMTOM_A_CDC_TX9_MUX_CTL, 0x8, 0x0},
+ {TOMTOM_A_CDC_TX10_MUX_CTL, 0x8, 0x0},
+
+ /* Compander zone selection */
+ {TOMTOM_A_CDC_COMP0_B4_CTL, 0x3F, 0x37},
+ {TOMTOM_A_CDC_COMP1_B4_CTL, 0x3F, 0x37},
+ {TOMTOM_A_CDC_COMP2_B4_CTL, 0x3F, 0x37},
+ {TOMTOM_A_CDC_COMP0_B5_CTL, 0x7F, 0x7F},
+ {TOMTOM_A_CDC_COMP1_B5_CTL, 0x7F, 0x7F},
+ {TOMTOM_A_CDC_COMP2_B5_CTL, 0x7F, 0x7F},
+
+ /*
+ * Setup wavegen timer to 20msec and disable chopper
+ * as default. This corresponds to Compander OFF
+ */
+ {TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xFF, 0xDB},
+ {TOMTOM_A_RX_HPH_CNP_WG_TIME, 0xFF, 0x58},
+ {TOMTOM_A_RX_HPH_BIAS_WG_OCP, 0xFF, 0x1A},
+ {TOMTOM_A_RX_HPH_CHOP_CTL, 0xFF, 0x24},
+
+ /* Choose max non-overlap time for NCP */
+ {TOMTOM_A_NCP_CLK, 0xFF, 0xFC},
+
+ /* Program the 0.85 volt VBG_REFERENCE */
+ {TOMTOM_A_BIAS_CURR_CTL_2, 0xFF, 0x04},
+
+ /* set MAD input MIC to DMIC1 */
+ {TOMTOM_A_CDC_MAD_INP_SEL, 0x0F, 0x08},
+
+ {TOMTOM_A_INTR_MODE, 0x04, 0x04},
+};
+
+static const struct wcd9xxx_reg_mask_val tomtom_codec_2_0_reg_init_val[] = {
+ {TOMTOM_A_RX_HPH_L_TEST, 0x08, 0x00},
+ {TOMTOM_A_RX_HPH_R_TEST, 0x08, 0x00},
+ {TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD, 0xFF, 0x00},
+ {TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD, 0xFF, 0x00},
+ {TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING, 0x01, 0x01},
+ {TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING, 0x01, 0x01},
+ {TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL, 0x01, 0x00},
+ {TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL, 0x01, 0x00},
+};
+
+static void tomtom_codec_init_reg(struct snd_soc_codec *codec)
+{
+ u32 i;
+ struct wcd9xxx *tomtom_core = dev_get_drvdata(codec->dev->parent);
+
+ for (i = 0; i < ARRAY_SIZE(tomtom_codec_reg_init_val); i++)
+ snd_soc_update_bits(codec, tomtom_codec_reg_init_val[i].reg,
+ tomtom_codec_reg_init_val[i].mask,
+ tomtom_codec_reg_init_val[i].val);
+
+ if (!TOMTOM_IS_1_0(tomtom_core->version)) {
+ for (i = 0; i < ARRAY_SIZE(tomtom_codec_2_0_reg_init_val); i++)
+ snd_soc_update_bits(codec,
+ tomtom_codec_2_0_reg_init_val[i].reg,
+ tomtom_codec_2_0_reg_init_val[i].mask,
+ tomtom_codec_2_0_reg_init_val[i].val);
+ }
+
+}
+
+static void tomtom_slim_interface_init_reg(struct snd_soc_codec *codec)
+{
+ int i;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
+ wcd9xxx_interface_reg_write(wcd9xxx,
+ TOMTOM_SLIM_PGD_PORT_INT_EN0 + i,
+ 0xFF);
+}
+
+static int tomtom_setup_irqs(struct tomtom_priv *tomtom)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = tomtom->codec;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+
+ ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS,
+ tomtom_slimbus_irq, "SLIMBUS Slave", tomtom);
+ if (ret)
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_SLIMBUS);
+ else
+ tomtom_slim_interface_init_reg(codec);
+
+ return ret;
+}
+
+static void tomtom_cleanup_irqs(struct tomtom_priv *tomtom)
+{
+ struct snd_soc_codec *codec = tomtom->codec;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+
+ wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tomtom);
+}
+
+static
+struct firmware_cal *tomtom_get_hwdep_fw_cal(struct snd_soc_codec *codec,
+ enum wcd_cal_type type)
+{
+ struct tomtom_priv *tomtom;
+ struct firmware_cal *hwdep_cal;
+ if (!codec) {
+ pr_err("%s: NULL codec pointer\n", __func__);
+ return NULL;
+ }
+ tomtom = snd_soc_codec_get_drvdata(codec);
+ hwdep_cal = wcdcal_get_fw_cal(tomtom->fw_data, type);
+ if (!hwdep_cal) {
+ dev_err(codec->dev, "%s: cal not sent by %d\n",
+ __func__, type);
+ return NULL;
+ } else {
+ return hwdep_cal;
+ }
+}
+
+int tomtom_hs_detect(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+ int rc;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+ if (mbhc_cfg->insert_detect) {
+ rc = wcd9xxx_mbhc_start(&tomtom->mbhc, mbhc_cfg);
+ if (!rc)
+ tomtom->mbhc_started = true;
+ } else {
+ /* MBHC is disabled, so disable Auto pulldown */
+ snd_soc_update_bits(codec, TOMTOM_A_MBHC_INSERT_DETECT2, 0xC0,
+ 0x00);
+ snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_2_CTL, 0x01,
+ 0x00);
+ tomtom->mbhc.mbhc_cfg = NULL;
+ rc = 0;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(tomtom_hs_detect);
+
+void tomtom_hs_detect_exit(struct snd_soc_codec *codec)
+{
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ wcd9xxx_mbhc_stop(&tomtom->mbhc);
+ tomtom->mbhc_started = false;
+}
+EXPORT_SYMBOL(tomtom_hs_detect_exit);
+
+void tomtom_event_register(
+ int (*machine_event_cb)(struct snd_soc_codec *codec,
+ enum wcd9xxx_codec_event),
+ struct snd_soc_codec *codec)
+{
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ tomtom->machine_codec_event_cb = machine_event_cb;
+}
+EXPORT_SYMBOL(tomtom_event_register);
+
+void tomtom_register_ext_clk_cb(
+ int (*codec_ext_clk_en)(struct snd_soc_codec *codec,
+ int enable, bool dapm),
+ int (*get_ext_clk_cnt) (void),
+ struct snd_soc_codec *codec)
+{
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ tomtom->codec_ext_clk_en_cb = codec_ext_clk_en;
+ tomtom->codec_get_ext_clk_cnt = get_ext_clk_cnt;
+}
+EXPORT_SYMBOL(tomtom_register_ext_clk_cb);
+
+static void tomtom_init_slim_slave_cfg(struct snd_soc_codec *codec)
+{
+ struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct afe_param_cdc_slimbus_slave_cfg *cfg;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ uint64_t eaddr = 0;
+
+ cfg = &priv->slimbus_slave_cfg;
+ cfg->minor_version = 1;
+ cfg->tx_slave_port_offset = 0;
+ cfg->rx_slave_port_offset = 16;
+
+ memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr));
+ WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6);
+ cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF;
+ cfg->device_enum_addr_msw = eaddr >> 32;
+
+ pr_debug("%s: slimbus logical address 0x%llx\n", __func__, eaddr);
+}
+
+static int tomtom_device_down(struct wcd9xxx *wcd9xxx)
+{
+ int count;
+ struct snd_soc_codec *codec;
+ struct tomtom_priv *priv;
+
+ codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+ priv = snd_soc_codec_get_drvdata(codec);
+ wcd_cpe_ssr_event(priv->cpe_core, WCD_CPE_BUS_DOWN_EVENT);
+ snd_soc_card_change_online_state(codec->component.card, 0);
+ set_bit(BUS_DOWN, &priv->status_mask);
+
+ for (count = 0; count < NUM_CODEC_DAIS; count++)
+ priv->dai[count].bus_down_in_recovery = true;
+ return 0;
+}
+
+static int wcd9xxx_prepare_static_pa(struct wcd9xxx_mbhc *mbhc,
+ struct list_head *lh)
+{
+ int i;
+ struct snd_soc_codec *codec = mbhc->codec;
+ u32 delay;
+
+ const struct wcd9xxx_reg_mask_val reg_set_paon[] = {
+ {TOMTOM_A_TX_COM_BIAS, 0xff, 0xF0},
+ {WCD9XXX_A_CDC_RX1_B6_CTL, 0xff, 0x81},
+ {WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x01, 0x01},
+ {WCD9XXX_A_BUCK_MODE_2, 0xff, 0xEF},
+ {WCD9XXX_A_BUCK_MODE_2, 0xff, 0xEE},
+ {TOMTOM_A_NCP_DTEST, 0xff, 0x20},
+ {WCD9XXX_A_CDC_CLK_OTHR_CTL, 0xff, 0x21},
+ {WCD9XXX_A_CDC_RX2_B6_CTL, 0xff, 0x81},
+ {WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x02, 0x02},
+
+ {WCD9XXX_A_BUCK_MODE_2, 0xff, 0xAE},
+ {WCD9XXX_A_BUCK_MODE_2, 0xff, 0xAA},
+ {WCD9XXX_A_NCP_CLK, 0xff, 0x9C},
+ {WCD9XXX_A_NCP_CLK, 0xff, 0xFC},
+ {WCD9XXX_A_RX_COM_BIAS, 0xff, 0xA0},
+ {WCD9XXX_A_BUCK_MODE_3, 0xff, 0xC6},
+ {WCD9XXX_A_BUCK_MODE_4, 0xff, 0xE6},
+ {WCD9XXX_A_BUCK_MODE_5, 0xff, 0x02},
+ {WCD9XXX_A_BUCK_MODE_1, 0xff, 0xA1},
+ /* Add a delay of 1ms after this reg write */
+
+ {WCD9XXX_A_NCP_STATIC, 0xff, 0x28},
+ {WCD9XXX_A_NCP_EN, 0xff, 0xFF},
+ /* Add a delay of 1ms after this reg write */
+
+ /* set HPHL */
+ {WCD9XXX_A_RX_HPH_L_TEST, 0xff, 0x00},
+ {TOMTOM_A_RX_HPH_L_PA_CTL, 0xff, 0x42},
+ {TOMTOM_A_RX_HPH_BIAS_LDO, 0xff, 0x8C},
+ {TOMTOM_A_RX_HPH_CHOP_CTL, 0xff, 0xA4},
+ {WCD9XXX_A_RX_HPH_L_GAIN, 0xff, 0xE0},
+ {WCD9XXX_A_RX_HPH_L_GAIN, 0xff, 0xEC},
+
+ /* set HPHR */
+ {WCD9XXX_A_RX_HPH_R_TEST, 0xff, 0x00},
+ {TOMTOM_A_RX_HPH_R_PA_CTL, 0xff, 0x42},
+ {WCD9XXX_A_RX_HPH_R_GAIN, 0xff, 0x20},
+ {WCD9XXX_A_RX_HPH_R_GAIN, 0xff, 0x2C},
+
+ /* set HPH PAs */
+ {WCD9XXX_A_RX_HPH_BIAS_WG_OCP, 0xff, 0x2A},
+ {WCD9XXX_A_RX_HPH_CNP_WG_CTL, 0xff, 0xDA},
+ {WCD9XXX_A_RX_HPH_CNP_WG_TIME, 0xff, 0x15},
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, 0xff, 0xE6},
+ {WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xff, 0x40},
+ {WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xff, 0xC0},
+ {WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xff, 0x40},
+ {WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xff, 0xC0},
+
+ {TOMTOM_A_RX_HPH_L_ATEST, 0xff, 0x00},
+ {TOMTOM_A_RX_HPH_R_ATEST, 0xff, 0x00},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set_paon); i++) {
+ /*
+ * Some of the codec registers like BUCK_MODE_1
+ * and NCP_EN requires 1ms wait time for them
+ * to take effect. Other register writes for
+ * PA configuration do not require any wait time.
+ */
+ if (reg_set_paon[i].reg == WCD9XXX_A_BUCK_MODE_1 ||
+ reg_set_paon[i].reg == WCD9XXX_A_NCP_EN)
+ delay = 1000;
+ else
+ delay = 0;
+ wcd9xxx_soc_update_bits_push(codec, lh,
+ reg_set_paon[i].reg,
+ reg_set_paon[i].mask,
+ reg_set_paon[i].val, delay);
+ }
+ pr_debug("%s: PAs are prepared\n", __func__);
+
+ return 0;
+}
+
+static int wcd9xxx_enable_static_pa(struct wcd9xxx_mbhc *mbhc, bool enable,
+ u8 hph_pa)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const int wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) *
+ TOMTOM_WG_TIME_FACTOR_US;
+ u8 mask = (hph_pa << 4);
+ u8 pa_en = enable ? mask : ~mask;
+
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, mask, pa_en);
+ /* Wait for wave gen time to avoid pop noise */
+ usleep_range(wg_time, wg_time + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ pr_debug("%s: PAs are %s as static mode (wg_time %d)\n", __func__,
+ enable ? "enabled" : "disabled", wg_time);
+ return 0;
+}
+
+static int tomtom_setup_zdet(struct wcd9xxx_mbhc *mbhc,
+ enum mbhc_impedance_detect_stages stage)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+#define __wr(reg, mask, value) \
+ do { \
+ ret = wcd9xxx_soc_update_bits_push(codec, \
+ &tomtom->reg_save_restore, \
+ reg, mask, value, 0); \
+ if (ret < 0) \
+ return ret; \
+ } while (0)
+
+ switch (stage) {
+
+ case MBHC_ZDET_PRE_MEASURE:
+ INIT_LIST_HEAD(&tomtom->reg_save_restore);
+ wcd9xxx_prepare_static_pa(mbhc, &tomtom->reg_save_restore);
+ /* Set HPH_MBHC for zdet */
+ __wr(WCD9XXX_A_MBHC_HPH, 0xff, 0xC4);
+ usleep_range(10, 10 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ wcd9xxx_enable_static_pa(mbhc, HPH_PA_ENABLE, HPH_PA_L_R);
+
+ /* save old value of registers and write the new value */
+ __wr(WCD9XXX_A_RX_HPH_OCP_CTL, 0xff, 0x69);
+ __wr(WCD9XXX_A_CDC_RX1_B6_CTL, 0xff, 0x80);
+ __wr(WCD9XXX_A_CDC_RX2_B6_CTL, 0xff, 0x80);
+ /* Enable MBHC MUX, Set MUX current to 37.5uA and ADC7 */
+ __wr(WCD9XXX_A_MBHC_SCALING_MUX_1, 0xff, 0xC0);
+ __wr(WCD9XXX_A_MBHC_SCALING_MUX_2, 0xff, 0xF0);
+ __wr(TOMTOM_A_TX_7_TXFE_CLKDIV, 0xff, 0x8B);
+ __wr(WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0xff, 0x78);
+ __wr(WCD9XXX_A_TX_7_MBHC_EN, 0xff, 0x8C);
+ __wr(WCD9XXX_A_CDC_MBHC_B1_CTL, 0xff, 0xDC);
+ /* Reset MBHC and set it up for STA */
+ __wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xff, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x00);
+ __wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xff, 0x02);
+ __wr(WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL, 0xff, 0x80);
+ __wr(WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0xff, 0x25);
+ /* Wait for ~50us to let MBHC hardware settle down */
+ usleep_range(50, 50 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ break;
+ case MBHC_ZDET_POST_MEASURE:
+ /* 0x69 for 105 number of samples for PA RAMP */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69);
+ /* Program the PA Ramp to FS_16K, L shift 1 */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL,
+ 0x1 << 4 | 0x6);
+ /* Reset the PA Ramp */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1C);
+ /*
+ * Connect the PA Ramp to PA chain and release reset with
+ * keep it connected.
+ */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
+
+ /* Start the PA ramp on HPH L and R */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x05);
+ /* Ramp generator takes ~30ms */
+ usleep_range(TOMTOM_HPH_PA_RAMP_DELAY,
+ TOMTOM_HPH_PA_RAMP_DELAY +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+ /*
+ * Set the multiplication factor for zdet calculation
+ * based on the Ramp voltage and Gain used
+ */
+ tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_1X;
+ break;
+ case MBHC_ZDET_GAIN_0:
+ /* Set Gain at 1x */
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_L_ATEST, 0x00);
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_R_ATEST, 0x00);
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_L_PA_CTL, 0x42);
+ /* Allow 100us for gain registers to settle */
+ usleep_range(100,
+ 100 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ break;
+ case MBHC_ZDET_GAIN_UPDATE_1X:
+ /*
+ * Set the multiplication factor for zdet calculation
+ * based on the Gain value used
+ */
+ tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_1X;
+ break;
+ case MBHC_ZDET_GAIN_1:
+ /* Set Gain at 10x */
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_L_ATEST, 0x10);
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_R_ATEST, 0x00);
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_L_PA_CTL, 0x42);
+ /* Allow 100us for gain registers to settle */
+ usleep_range(100,
+ 100 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+ /*
+ * Set the multiplication factor for zdet calculation
+ * based on the Gain value used
+ */
+ tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_10X;
+ break;
+ case MBHC_ZDET_GAIN_2:
+ /* Set Gain at 100x */
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_L_ATEST, 0x00);
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_R_ATEST, 0x10);
+ snd_soc_write(codec, TOMTOM_A_RX_HPH_L_PA_CTL, 0x43);
+ /* Allow 100us for gain registers to settle */
+ usleep_range(100,
+ 100 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+ /*
+ * Set the multiplication factor for zdet calculation
+ * based on the Gain value used
+ */
+ tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_100X;
+ break;
+ case MBHC_ZDET_RAMP_DISABLE:
+ /* Ramp HPH L & R back to Zero */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+ /* 0x69 for 105 number of samples for PA RAMP */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69);
+ /* Program the PA Ramp to FS_16K, L shift 1 */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL,
+ 0x1 << 4 | 0x6);
+ /* Reset the PA Ramp */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x17);
+ /*
+ * Connect the PA Ramp to PA chain and release reset with
+ * keep it connected.
+ */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
+ /* Start the PA ramp on HPH L and R */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x0A);
+ /* Ramp generator takes ~30ms to settle down */
+ usleep_range(TOMTOM_HPH_PA_RAMP_DELAY,
+ TOMTOM_HPH_PA_RAMP_DELAY +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ break;
+ case MBHC_ZDET_HPHR_RAMP_DISABLE:
+ /* Ramp HPHR back to Zero */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL,
+ 0x1 << 4 | 0x6);
+ /* Reset the PA Ramp */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x17);
+ /*
+ * Connect the PA Ramp to PA chain and release reset with
+ * keep it connected.
+ */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x08);
+ /* Ramp generator takes ~30ms to settle down */
+ usleep_range(TOMTOM_HPH_PA_RAMP_DELAY,
+ TOMTOM_HPH_PA_RAMP_DELAY +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ break;
+ case MBHC_ZDET_HPHL_RAMP_DISABLE:
+ /* Ramp back to Zero */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL,
+ 0x1 << 4 | 0x6);
+ /* Reset the PA Ramp */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x17);
+ /*
+ * Connect the PA Ramp to PA chain and release reset with
+ * keep it connected.
+ */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x02);
+ /* Ramp generator takes ~30ms to settle down */
+ usleep_range(TOMTOM_HPH_PA_RAMP_DELAY,
+ TOMTOM_HPH_PA_RAMP_DELAY +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ break;
+ case MBHC_ZDET_HPHR_PA_DISABLE:
+ /* Disable PA */
+ wcd9xxx_enable_static_pa(mbhc, HPH_PA_DISABLE, HPH_PA_R);
+ break;
+ case MBHC_ZDET_PA_DISABLE:
+ /* Disable PA */
+ if (!mbhc->hph_pa_dac_state &&
+ (!(test_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state) ||
+ test_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state))))
+ wcd9xxx_enable_static_pa(mbhc, HPH_PA_DISABLE,
+ HPH_PA_L_R);
+ else if (!(snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN) & 0x10))
+ wcd9xxx_enable_static_pa(mbhc, HPH_PA_ENABLE, HPH_PA_R);
+
+ /* Turn off PA ramp generator */
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x00);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL, 0x00);
+ snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x00);
+
+ /* Restore registers */
+ wcd9xxx_restore_registers(codec, &tomtom->reg_save_restore);
+ break;
+ }
+#undef __wr
+
+ return ret;
+}
+
+/* Calculate final impedance values for HPH left and right based on formulae */
+static void tomtom_compute_impedance(struct wcd9xxx_mbhc *mbhc, s16 *l, s16 *r,
+ uint32_t *zl, uint32_t *zr)
+{
+ s64 zln, zrn;
+ int zld, zrd;
+ s64 rl = 0, rr = 0;
+ struct snd_soc_codec *codec;
+ struct tomtom_priv *tomtom;
+
+ if (!mbhc) {
+ pr_err("%s: Invalid parameters mbhc = %pK\n",
+ __func__, mbhc);
+ return;
+ }
+ codec = mbhc->codec;
+ tomtom = snd_soc_codec_get_drvdata(codec);
+
+ if (l && zl) {
+ zln = (s64) (l[1] - l[0]) * tomtom->zdet_gain_mul_fact;
+ zld = (l[2] - l[0]);
+ if (zld)
+ rl = div_s64(zln, zld);
+ else
+ /* If L0 and L2 are same, Z has to be on Zone 3.
+ * Assign a default value so that atleast the value
+ * is read again with Ramp-up
+ */
+ rl = TOMTOM_ZDET_ZONE_3_DEFAULT_VAL;
+
+ /* 32-bit LSBs are enough to hold Impedance values */
+ *zl = (u32) rl;
+ }
+ if (r && zr) {
+ zrn = (s64) (r[1] - r[0]) * tomtom->zdet_gain_mul_fact;
+ zrd = (r[2] - r[0]);
+ if (zrd)
+ rr = div_s64(zrn, zrd);
+ else
+ /* If R0 and R2 are same, Z has to be on Zone 3.
+ * Assign a default value so that atleast the value
+ * is read again with Ramp-up
+ */
+ rr = TOMTOM_ZDET_ZONE_3_DEFAULT_VAL;
+
+ /* 32-bit LSBs are enough to hold Impedance values */
+ *zr = (u32) rr;
+ }
+}
+
+/*
+ * Calculate error approximation of impedance values for HPH left
+ * and HPH right based on QFuse values
+ */
+static void tomtom_zdet_error_approx(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr)
+{
+ struct snd_soc_codec *codec;
+ struct tomtom_priv *tomtom;
+ s8 q1_t, q2_t;
+ s8 q1_m, q2_m;
+ s8 q1, q2;
+ u8 div_shift;
+ int rl_alpha = 0, rr_alpha = 0;
+ int rl_beta = 0, rr_beta = 0;
+ u64 rl = 0, rr = 0;
+ const int mult_factor = TOMTOM_ZDET_ERROR_APPROX_MUL_FACTOR;
+ const int shift = TOMTOM_ZDET_ERROR_APPROX_SHIFT;
+
+ if (!zl || !zr || !mbhc) {
+ pr_err("%s: Invalid parameters zl = %pK zr = %pK, mbhc = %pK\n",
+ __func__, zl, zr, mbhc);
+ return;
+ }
+ codec = mbhc->codec;
+ tomtom = snd_soc_codec_get_drvdata(codec);
+
+ if ((tomtom->zdet_gain_mul_fact == TOMTOM_ZDET_MUL_FACTOR_1X) ||
+ (tomtom->zdet_gain_mul_fact == TOMTOM_ZDET_MUL_FACTOR_10X)) {
+ q1_t = ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT0) &
+ 0x3) << 0x5);
+ q1_t |= ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT1) &
+ 0xF8) >> 0x3);
+ q2_t = ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT1) &
+ 0x7) << 0x4);
+ q2_t |= ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT2) &
+ 0xF0) >> 0x4);
+ /* Take out the numeric part of the Qfuse value */
+ q1_m = q1_t & 0x3F;
+ q2_m = q2_t & 0x3F;
+ /* Check the sign part of the Qfuse and adjust value */
+ q1 = (q1_t & 0x40) ? -q1_m : q1_m;
+ q2 = (q2_t & 0x40) ? -q2_m : q2_m;
+ div_shift = 1;
+ } else {
+ q1_t = ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT2) &
+ 0xF) << 0x2);
+ q1_t |= ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT3) &
+ 0xC0) >> 0x6);
+ q2_t = (snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT3) & 0x3F);
+ /* Take out the numeric part of the Qfuse value */
+ q1_m = q1_t & 0x1F;
+ q2_m = q2_t & 0x1F;
+ /* Check the sign part of the Qfuse and adjust value */
+ q1 = (q1_t & 0x20) ? -q1_m : q1_m;
+ q2 = (q2_t & 0x20) ? -q2_m : q2_m;
+ div_shift = 0;
+ }
+
+ dev_dbg(codec->dev, "%s: qfuse1 = %d, qfuse2 = %d\n",
+ __func__, q1, q2);
+ if (!q1 && !q2) {
+ dev_dbg(codec->dev, "%s: qfuse1 and qfuse2 are 0. Exiting\n",
+ __func__);
+ return;
+ }
+
+ /*
+ * Use multiplication and shift to avoid floating point math
+ * The Z value is calculated with the below formulae using
+ * the Qfuse value-
+ * zl = zl * [1 - {(Q1 / div) / 100}] (Include sign for Q1)
+ * zr = zr * [1 - {(Q2 / div) / 100}] (Include sign for Q2)
+ * We multiply by 65536 and shift 16 times to get the approx result
+ * div = 4 for 1x gain, div = 2 for 10x/100x gain
+ */
+ /* Q1/4 */
+ rl_alpha = q1 >> div_shift;
+ rl_alpha = 100 - rl_alpha;
+ /* {rl_alpha/100} * 65536 */
+ rl_beta = rl_alpha * mult_factor;
+ rl = (u64) *zl * rl_beta;
+ /* rl/65536 */
+ rl = (u64) rl >> shift;
+
+ rr_alpha = q2 >> div_shift;
+ rr_alpha = 100 - rr_alpha;
+ rr_beta = rr_alpha * mult_factor;
+ rr = (u64) *zr * rr_beta;
+ rr = (u64) rr >> shift;
+
+ dev_dbg(codec->dev, "%s: rl = 0x%llx (%lld) \t rr = 0x%llx (%lld)\n",
+ __func__, rl, rl, rr, rr);
+
+ *zl = (u32) rl;
+ *zr = (u32) rr;
+}
+
+static enum wcd9xxx_cdc_type tomtom_get_cdc_type(void)
+{
+ return WCD9XXX_CDC_TYPE_TOMTOM;
+}
+
+static bool tomtom_mbhc_ins_rem_status(struct snd_soc_codec *codec)
+{
+ return !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
+ (1 << 4));
+}
+
+static void tomtom_mbhc_micb_pulldown_ctrl(struct wcd9xxx_mbhc *mbhc,
+ bool enable)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ if (!enable) {
+ /* Remove automatic pulldown on micbias */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x01, 0x00);
+ } else {
+ /* Enable automatic pulldown on micbias */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x01, 0x01);
+ }
+}
+
+static void tomtom_codec_hph_auto_pull_down(struct snd_soc_codec *codec,
+ bool enable)
+{
+ struct wcd9xxx *tomtom_core = dev_get_drvdata(codec->dev->parent);
+
+ if (TOMTOM_IS_1_0(tomtom_core->version))
+ return;
+
+ dev_dbg(codec->dev, "%s: %s auto pull down\n", __func__,
+ enable ? "enable" : "disable");
+ if (enable) {
+ snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_L_TEST, 0x08, 0x08);
+ snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_R_TEST, 0x08, 0x08);
+ } else {
+ snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_L_TEST, 0x08, 0x00);
+ snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_R_TEST, 0x08, 0x00);
+ }
+}
+
+static const struct wcd9xxx_mbhc_cb mbhc_cb = {
+ .get_cdc_type = tomtom_get_cdc_type,
+ .setup_zdet = tomtom_setup_zdet,
+ .compute_impedance = tomtom_compute_impedance,
+ .zdet_error_approx = tomtom_zdet_error_approx,
+ .insert_rem_status = tomtom_mbhc_ins_rem_status,
+ .micbias_pulldown_ctrl = tomtom_mbhc_micb_pulldown_ctrl,
+ .codec_rco_ctrl = tomtom_codec_internal_rco_ctrl,
+ .hph_auto_pulldown_ctrl = tomtom_codec_hph_auto_pull_down,
+ .get_hwdep_fw_cal = tomtom_get_hwdep_fw_cal,
+};
+
+static const struct wcd9xxx_mbhc_intr cdc_intr_ids = {
+ .poll_plug_rem = WCD9XXX_IRQ_MBHC_REMOVAL,
+ .shortavg_complete = WCD9XXX_IRQ_MBHC_SHORT_TERM,
+ .potential_button_press = WCD9XXX_IRQ_MBHC_PRESS,
+ .button_release = WCD9XXX_IRQ_MBHC_RELEASE,
+ .dce_est_complete = WCD9XXX_IRQ_MBHC_POTENTIAL,
+ .insertion = WCD9XXX_IRQ_MBHC_INSERTION,
+ .hph_left_ocp = WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ .hph_right_ocp = WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ .hs_jack_switch = WCD9330_IRQ_MBHC_JACK_SWITCH,
+};
+
+static int tomtom_post_reset_cb(struct wcd9xxx *wcd9xxx)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec;
+ struct tomtom_priv *tomtom;
+ int rco_clk_rate;
+
+ codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+ tomtom = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_card_change_online_state(codec->component.card, 1);
+ clear_bit(BUS_DOWN, &tomtom->status_mask);
+
+ mutex_lock(&tomtom->codec_mutex);
+
+ tomtom_update_reg_defaults(codec);
+ if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ)
+ snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x0);
+ else if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_9P6MHZ)
+ snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x2);
+ tomtom_codec_init_reg(codec);
+
+ snd_soc_cache_sync(codec);
+
+ ret = tomtom_handle_pdata(tomtom);
+ if (IS_ERR_VALUE(ret))
+ pr_err("%s: bad pdata\n", __func__);
+
+ tomtom_init_slim_slave_cfg(codec);
+ tomtom_slim_interface_init_reg(codec);
+ wcd_cpe_ssr_event(tomtom->cpe_core, WCD_CPE_BUS_UP_EVENT);
+ wcd9xxx_resmgr_post_ssr(&tomtom->resmgr);
+
+ if (tomtom->mbhc_started) {
+ wcd9xxx_mbhc_deinit(&tomtom->mbhc);
+ tomtom->mbhc_started = false;
+
+ if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ)
+ rco_clk_rate = TOMTOM_MCLK_CLK_12P288MHZ;
+ else
+ rco_clk_rate = TOMTOM_MCLK_CLK_9P6MHZ;
+
+ ret = wcd9xxx_mbhc_init(&tomtom->mbhc, &tomtom->resmgr, codec,
+ tomtom_enable_mbhc_micbias,
+ &mbhc_cb, &cdc_intr_ids,
+ rco_clk_rate, TOMTOM_ZDET_SUPPORTED);
+ if (ret)
+ pr_err("%s: mbhc init failed %d\n", __func__, ret);
+ else
+ tomtom_hs_detect(codec, tomtom->mbhc.mbhc_cfg);
+ }
+
+ if (tomtom->machine_codec_event_cb)
+ tomtom->machine_codec_event_cb(codec,
+ WCD9XXX_CODEC_EVENT_CODEC_UP);
+
+ tomtom_cleanup_irqs(tomtom);
+ ret = tomtom_setup_irqs(tomtom);
+ if (ret)
+ pr_err("%s: Failed to setup irq: %d\n", __func__, ret);
+
+ /*
+ * After SSR, the qfuse sensing is lost.
+ * Perform qfuse sensing again after SSR
+ * handling is finished.
+ */
+ tomtom_enable_qfuse_sensing(codec);
+ mutex_unlock(&tomtom->codec_mutex);
+ return ret;
+}
+
+void *tomtom_get_afe_config(struct snd_soc_codec *codec,
+ enum afe_config_type config_type)
+{
+ struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (config_type) {
+ case AFE_SLIMBUS_SLAVE_CONFIG:
+ return &priv->slimbus_slave_cfg;
+ case AFE_CDC_REGISTERS_CONFIG:
+ return &tomtom_audio_reg_cfg;
+ case AFE_SLIMBUS_SLAVE_PORT_CONFIG:
+ return &tomtom_slimbus_slave_port_cfg;
+ case AFE_AANC_VERSION:
+ return &tomtom_cdc_aanc_version;
+ case AFE_CLIP_BANK_SEL:
+ return &clip_bank_sel;
+ case AFE_CDC_CLIP_REGISTERS_CONFIG:
+ return &tomtom_clip_reg_cfg;
+ default:
+ pr_err("%s: Unknown config_type 0x%x\n", __func__, config_type);
+ return NULL;
+ }
+}
+
+static struct wcd9xxx_reg_address tomtom_reg_address = {
+ .micb_4_mbhc = TOMTOM_A_MICB_4_MBHC,
+ .micb_4_int_rbias = TOMTOM_A_MICB_4_INT_RBIAS,
+ .micb_4_ctl = TOMTOM_A_MICB_4_CTL,
+};
+
+static int wcd9xxx_ssr_register(struct wcd9xxx *control,
+ int (*device_down_cb)(struct wcd9xxx *wcd9xxx),
+ int (*device_up_cb)(struct wcd9xxx *wcd9xxx),
+ void *priv)
+{
+ control->dev_down = device_down_cb;
+ control->post_reset = device_up_cb;
+ control->ssr_priv = priv;
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tomtom_1_dapm_widgets[] = {
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, TOMTOM_A_TX_1_GAIN, 7, 0,
+ tomtom_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, TOMTOM_A_TX_2_GAIN, 7, 0,
+ tomtom_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC3", NULL, TOMTOM_A_TX_3_GAIN, 7, 0,
+ tomtom_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC4", NULL, TOMTOM_A_TX_4_GAIN, 7, 0,
+ tomtom_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC5", NULL, TOMTOM_A_TX_5_GAIN, 7, 0,
+ tomtom_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC6", NULL, TOMTOM_A_TX_6_GAIN, 7, 0,
+ tomtom_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static struct regulator *tomtom_codec_find_regulator(struct snd_soc_codec *cdc,
+ const char *name)
+{
+ int i;
+ struct wcd9xxx *core = dev_get_drvdata(cdc->dev->parent);
+
+ for (i = 0; i < core->num_of_supplies; i++) {
+ if (core->supplies[i].supply &&
+ !strcmp(core->supplies[i].supply, name))
+ return core->supplies[i].consumer;
+ }
+
+ return NULL;
+}
+
+static struct wcd_cpe_core *tomtom_codec_get_cpe_core(
+ struct snd_soc_codec *codec)
+{
+ struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+ return priv->cpe_core;
+}
+
+static int tomtom_codec_fll_enable(struct snd_soc_codec *codec,
+ bool enable)
+{
+ struct wcd9xxx *wcd9xxx;
+
+ if (!codec || !codec->control_data) {
+ pr_err("%s: Invalid codec handle, %pK\n",
+ __func__, codec);
+ return -EINVAL;
+ }
+
+ wcd9xxx = codec->control_data;
+
+ dev_dbg(codec->dev, "%s: %s, mclk_rate = %d\n",
+ __func__, (enable ? "enable" : "disable"),
+ wcd9xxx->mclk_rate);
+
+ switch (wcd9xxx->mclk_rate) {
+ case TOMTOM_MCLK_CLK_9P6MHZ:
+ snd_soc_update_bits(codec, TOMTOM_A_FLL_NREF,
+ 0x1F, 0x15);
+ snd_soc_update_bits(codec, TOMTOM_A_FLL_KDCO_TUNE,
+ 0x07, 0x06);
+ snd_soc_write(codec, TOMTOM_A_FLL_LOCK_THRESH, 0xD1);
+ snd_soc_write(codec, TOMTOM_A_FLL_LOCK_DET_COUNT,
+ 0x40);
+ break;
+ case TOMTOM_MCLK_CLK_12P288MHZ:
+ snd_soc_update_bits(codec, TOMTOM_A_FLL_NREF,
+ 0x1F, 0x11);
+ snd_soc_update_bits(codec, TOMTOM_A_FLL_KDCO_TUNE,
+ 0x07, 0x05);
+ snd_soc_write(codec, TOMTOM_A_FLL_LOCK_THRESH, 0xB1);
+ snd_soc_write(codec, TOMTOM_A_FLL_LOCK_DET_COUNT,
+ 0x40);
+ break;
+ }
+
+ return 0;
+}
+
+static int tomtom_codec_slim_reserve_bw(struct snd_soc_codec *codec,
+ u32 bw_ops, bool commit)
+{
+ struct wcd9xxx *wcd9xxx;
+ if (!codec) {
+ pr_err("%s: Invalid handle to codec\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ if (!wcd9xxx) {
+ dev_err(codec->dev, "%s: Invalid parent drv_data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return wcd9xxx_slim_reserve_bw(wcd9xxx, bw_ops, commit);
+}
+
+static int tomtom_codec_vote_max_bw(struct snd_soc_codec *codec,
+ bool vote)
+{
+ u32 bw_ops;
+
+ if (vote)
+ bw_ops = SLIM_BW_CLK_GEAR_9;
+ else
+ bw_ops = SLIM_BW_UNVOTE;
+
+ return tomtom_codec_slim_reserve_bw(codec,
+ bw_ops, true);
+}
+
+static const struct wcd9xxx_resmgr_cb resmgr_cb = {
+ .cdc_rco_ctrl = tomtom_codec_internal_rco_ctrl,
+};
+
+static int tomtom_cpe_err_irq_control(struct snd_soc_codec *codec,
+ enum cpe_err_irq_cntl_type cntl_type, u8 *status)
+{
+ switch (cntl_type) {
+ case CPE_ERR_IRQ_MASK:
+ snd_soc_update_bits(codec,
+ TOMTOM_A_SVASS_INT_MASK,
+ 0x3F, 0x3F);
+ break;
+ case CPE_ERR_IRQ_UNMASK:
+ snd_soc_update_bits(codec,
+ TOMTOM_A_SVASS_INT_MASK,
+ 0x3F, 0x0C);
+ break;
+ case CPE_ERR_IRQ_CLEAR:
+ snd_soc_update_bits(codec,
+ TOMTOM_A_SVASS_INT_CLR,
+ 0x3F, 0x3F);
+ break;
+ case CPE_ERR_IRQ_STATUS:
+ if (!status)
+ return -EINVAL;
+ *status = snd_soc_read(codec,
+ TOMTOM_A_SVASS_INT_STATUS);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct wcd_cpe_cdc_cb cpe_cb = {
+ .cdc_clk_en = tomtom_codec_internal_rco_ctrl,
+ .cpe_clk_en = tomtom_codec_fll_enable,
+ .lab_cdc_ch_ctl = tomtom_codec_enable_slimtx_mad,
+ .cdc_ext_clk = tomtom_codec_ext_clk_en,
+ .bus_vote_bw = tomtom_codec_vote_max_bw,
+ .cpe_err_irq_control = tomtom_cpe_err_irq_control,
+};
+
+static struct cpe_svc_init_param cpe_svc_params = {
+ .version = 0,
+ .query_freq_plans_cb = NULL,
+ .change_freq_plan_cb = NULL,
+};
+
+static int tomtom_cpe_initialize(struct snd_soc_codec *codec)
+{
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ struct wcd_cpe_params cpe_params;
+
+ memset(&cpe_params, 0,
+ sizeof(struct wcd_cpe_params));
+ cpe_params.codec = codec;
+ cpe_params.get_cpe_core = tomtom_codec_get_cpe_core;
+ cpe_params.cdc_cb = &cpe_cb;
+ cpe_params.dbg_mode = cpe_debug_mode;
+ cpe_params.cdc_major_ver = CPE_SVC_CODEC_TOMTOM;
+ cpe_params.cdc_minor_ver = CPE_SVC_CODEC_V1P0;
+ cpe_params.cdc_id = CPE_SVC_CODEC_TOMTOM;
+
+ cpe_params.cdc_irq_info.cpe_engine_irq =
+ WCD9330_IRQ_SVASS_ENGINE;
+ cpe_params.cdc_irq_info.cpe_err_irq =
+ WCD9330_IRQ_SVASS_ERR_EXCEPTION;
+ cpe_params.cdc_irq_info.cpe_fatal_irqs =
+ TOMTOM_CPE_FATAL_IRQS;
+
+ cpe_svc_params.context = codec;
+ cpe_params.cpe_svc_params = &cpe_svc_params;
+
+ tomtom->cpe_core = wcd_cpe_init("cpe", codec,
+ &cpe_params);
+ if (IS_ERR_OR_NULL(tomtom->cpe_core)) {
+ dev_err(codec->dev,
+ "%s: Failed to enable CPE\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tomtom_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wcd9xxx *control;
+ struct tomtom_priv *tomtom;
+ struct wcd9xxx_pdata *pdata;
+ struct wcd9xxx *wcd9xxx;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ int ret = 0;
+ int i, rco_clk_rate;
+ void *ptr = NULL;
+ struct wcd9xxx_core_resource *core_res;
+ struct clk *wcd_ext_clk = NULL;
+
+ dev_info(codec->dev, "%s()\n", __func__);
+
+ control = dev_get_drvdata(codec->dev->parent);
+
+ tomtom = snd_soc_codec_get_drvdata(codec);
+
+ wcd9xxx_ssr_register(control, tomtom_device_down,
+ tomtom_post_reset_cb, (void *)codec);
+
+ for (i = 0; i < NUM_DECIMATORS; i++) {
+ tx_hpf_work[i].tomtom = tomtom;
+ tx_hpf_work[i].decimator = i + 1;
+ tx_hpf_work[i].tx_hpf_bypass = false;
+ INIT_DELAYED_WORK(&tx_hpf_work[i].dwork,
+ tx_hpf_corner_freq_callback);
+ }
+
+ wcd9xxx = control;
+ if (!of_find_property(wcd9xxx->dev->of_node, "clock-names", NULL)) {
+ dev_dbg(wcd9xxx->dev, "%s: codec not using audio-ext-clk driver\n",
+ __func__);
+ } else {
+ wcd_ext_clk = clk_get(wcd9xxx->dev, "wcd_clk");
+ if (IS_ERR(wcd_ext_clk)) {
+ dev_err(codec->dev, "%s: clk get %s failed\n",
+ __func__, "wcd_ext_clk");
+ goto err_nomem_slimch;
+ }
+ }
+ tomtom->wcd_ext_clk = wcd_ext_clk;
+ core_res = &wcd9xxx->core_res;
+ pdata = dev_get_platdata(codec->dev->parent);
+ /* codec resmgr module init */
+ ret = wcd9xxx_resmgr_init(&tomtom->resmgr, codec, core_res, pdata,
+ &pdata->micbias, &tomtom_reg_address,
+ &resmgr_cb, WCD9XXX_CDC_TYPE_TOMTOM);
+ if (ret) {
+ pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
+ goto err_nomem_slimch;
+ }
+
+ tomtom->clsh_d.buck_mv = tomtom_codec_get_buck_mv(codec);
+ /* TomTom does not support dynamic switching of vdd_cp */
+ tomtom->clsh_d.is_dynamic_vdd_cp = false;
+ wcd9xxx_clsh_init(&tomtom->clsh_d, &tomtom->resmgr);
+
+ if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ)
+ rco_clk_rate = TOMTOM_MCLK_CLK_12P288MHZ;
+ else
+ rco_clk_rate = TOMTOM_MCLK_CLK_9P6MHZ;
+
+ tomtom->fw_data = kzalloc(sizeof(*(tomtom->fw_data)), GFP_KERNEL);
+ if (!tomtom->fw_data) {
+ dev_err(codec->dev, "Failed to allocate fw_data\n");
+ goto err_nomem_slimch;
+ }
+ set_bit(WCD9XXX_ANC_CAL, tomtom->fw_data->cal_bit);
+ set_bit(WCD9XXX_MAD_CAL, tomtom->fw_data->cal_bit);
+ set_bit(WCD9XXX_MBHC_CAL, tomtom->fw_data->cal_bit);
+ ret = wcd_cal_create_hwdep(tomtom->fw_data,
+ WCD9XXX_CODEC_HWDEP_NODE, codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
+ goto err_hwdep;
+ }
+
+ /* init and start mbhc */
+ ret = wcd9xxx_mbhc_init(&tomtom->mbhc, &tomtom->resmgr, codec,
+ tomtom_enable_mbhc_micbias,
+ &mbhc_cb, &cdc_intr_ids,
+ rco_clk_rate, TOMTOM_ZDET_SUPPORTED);
+ if (ret) {
+ pr_err("%s: mbhc init failed %d\n", __func__, ret);
+ goto err_hwdep;
+ }
+
+ tomtom->codec = codec;
+ for (i = 0; i < COMPANDER_MAX; i++) {
+ tomtom->comp_enabled[i] = 0;
+ tomtom->comp_fs[i] = COMPANDER_FS_48KHZ;
+ }
+ tomtom->intf_type = wcd9xxx_get_intf_type();
+ tomtom->aux_pga_cnt = 0;
+ tomtom->aux_l_gain = 0x1F;
+ tomtom->aux_r_gain = 0x1F;
+ tomtom->ldo_h_users = 0;
+ tomtom->micb_2_users = 0;
+ tomtom_update_reg_defaults(codec);
+ pr_debug("%s: MCLK Rate = %x\n", __func__, wcd9xxx->mclk_rate);
+ if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ)
+ snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x0);
+ else if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_9P6MHZ)
+ snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x2);
+ tomtom_codec_init_reg(codec);
+
+ ret = tomtom_handle_pdata(tomtom);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: bad pdata\n", __func__);
+ goto err_hwdep;
+ }
+
+ tomtom->spkdrv_reg = tomtom_codec_find_regulator(codec,
+ WCD9XXX_VDD_SPKDRV_NAME);
+ tomtom->spkdrv2_reg = tomtom_codec_find_regulator(codec,
+ WCD9XXX_VDD_SPKDRV2_NAME);
+
+ ptr = kmalloc((sizeof(tomtom_rx_chs) +
+ sizeof(tomtom_tx_chs)), GFP_KERNEL);
+ if (!ptr) {
+ pr_err("%s: no mem for slim chan ctl data\n", __func__);
+ ret = -ENOMEM;
+ goto err_hwdep;
+ }
+
+ if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ snd_soc_dapm_new_controls(dapm, tomtom_dapm_i2s_widgets,
+ ARRAY_SIZE(tomtom_dapm_i2s_widgets));
+ snd_soc_dapm_add_routes(dapm, audio_i2s_map,
+ ARRAY_SIZE(audio_i2s_map));
+ for (i = 0; i < ARRAY_SIZE(tomtom_i2s_dai); i++)
+ INIT_LIST_HEAD(&tomtom->dai[i].wcd9xxx_ch_list);
+ } else if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ for (i = 0; i < NUM_CODEC_DAIS; i++) {
+ INIT_LIST_HEAD(&tomtom->dai[i].wcd9xxx_ch_list);
+ init_waitqueue_head(&tomtom->dai[i].dai_wait);
+ }
+ tomtom_slimbus_slave_port_cfg.slave_dev_intfdev_la =
+ control->slim_slave->laddr;
+ tomtom_slimbus_slave_port_cfg.slave_dev_pgd_la =
+ control->slim->laddr;
+ tomtom_slimbus_slave_port_cfg.slave_port_mapping[0] =
+ TOMTOM_MAD_SLIMBUS_TX_PORT;
+
+ tomtom_init_slim_slave_cfg(codec);
+ }
+
+ snd_soc_dapm_new_controls(dapm, tomtom_1_dapm_widgets,
+ ARRAY_SIZE(tomtom_1_dapm_widgets));
+ snd_soc_add_codec_controls(codec,
+ tomtom_1_x_analog_gain_controls,
+ ARRAY_SIZE(tomtom_1_x_analog_gain_controls));
+
+ snd_soc_add_codec_controls(codec, impedance_detect_controls,
+ ARRAY_SIZE(impedance_detect_controls));
+ snd_soc_add_codec_controls(codec, hph_type_detect_controls,
+ ARRAY_SIZE(hph_type_detect_controls));
+
+ control->num_rx_port = TOMTOM_RX_MAX;
+ control->rx_chs = ptr;
+ memcpy(control->rx_chs, tomtom_rx_chs, sizeof(tomtom_rx_chs));
+ control->num_tx_port = TOMTOM_TX_MAX;
+ control->tx_chs = ptr + sizeof(tomtom_rx_chs);
+ memcpy(control->tx_chs, tomtom_tx_chs, sizeof(tomtom_tx_chs));
+
+ snd_soc_dapm_sync(dapm);
+
+ ret = tomtom_setup_irqs(tomtom);
+ if (ret) {
+ pr_err("%s: tomtom irq setup failed %d\n", __func__, ret);
+ goto err_pdata;
+ }
+
+ atomic_set(&kp_tomtom_priv, (unsigned long)tomtom);
+ mutex_lock(&tomtom->codec_mutex);
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_disable_pin(dapm, "ANC HEADPHONE");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+ mutex_unlock(&tomtom->codec_mutex);
+ snd_soc_dapm_sync(dapm);
+
+ codec->component.ignore_pmdown_time = 1;
+ ret = tomtom_cpe_initialize(codec);
+ if (ret) {
+ dev_info(codec->dev,
+ "%s: cpe initialization failed, ret = %d\n",
+ __func__, ret);
+ /* Do not fail probe if CPE failed */
+ ret = 0;
+ }
+ return ret;
+
+err_pdata:
+ kfree(ptr);
+ control->rx_chs = NULL;
+ control->tx_chs = NULL;
+err_hwdep:
+ kfree(tomtom->fw_data);
+ tomtom->fw_data = NULL;
+err_nomem_slimch:
+ devm_kfree(codec->dev, tomtom);
+ return ret;
+}
+static int tomtom_codec_remove(struct snd_soc_codec *codec)
+{
+ struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *control;
+
+ WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+ atomic_set(&kp_tomtom_priv, 0);
+
+ WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+
+ control = dev_get_drvdata(codec->dev->parent);
+ control->rx_chs = NULL;
+ control->tx_chs = NULL;
+
+ if (tomtom->wcd_ext_clk)
+ clk_put(tomtom->wcd_ext_clk);
+ tomtom_cleanup_irqs(tomtom);
+
+ /* cleanup MBHC */
+ wcd9xxx_mbhc_deinit(&tomtom->mbhc);
+ /* cleanup resmgr */
+ wcd9xxx_resmgr_deinit(&tomtom->resmgr);
+
+ tomtom->spkdrv_reg = NULL;
+ tomtom->spkdrv2_reg = NULL;
+
+ devm_kfree(codec->dev, tomtom);
+ return 0;
+}
+
+static struct regmap *tomtom_get_regmap(struct device *dev)
+{
+ struct wcd9xxx *control = dev_get_drvdata(dev->parent);
+
+ return control->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_tomtom = {
+ .probe = tomtom_codec_probe,
+ .remove = tomtom_codec_remove,
+ .controls = tomtom_snd_controls,
+ .num_controls = ARRAY_SIZE(tomtom_snd_controls),
+ .dapm_widgets = tomtom_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tomtom_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .get_regmap = tomtom_get_regmap,
+};
+
+#ifdef CONFIG_PM
+static int tomtom_suspend(struct device *dev)
+{
+ dev_dbg(dev, "%s: system suspend\n", __func__);
+ return 0;
+}
+
+static int tomtom_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tomtom_priv *tomtom = platform_get_drvdata(pdev);
+
+ if (!tomtom) {
+ dev_err(dev, "%s: tomtom private data is NULL\n", __func__);
+ return -EINVAL;
+ }
+ dev_dbg(dev, "%s: system resume\n", __func__);
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(&tomtom->resmgr,
+ WCD9XXX_EVENT_POST_RESUME);
+ return 0;
+}
+
+static const struct dev_pm_ops tomtom_pm_ops = {
+ .suspend = tomtom_suspend,
+ .resume = tomtom_resume,
+};
+#endif
+
+static int tomtom_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct tomtom_priv *tomtom;
+
+ tomtom = devm_kzalloc(&pdev->dev, sizeof(struct tomtom_priv),
+ GFP_KERNEL);
+ if (!tomtom) {
+ dev_err(&pdev->dev, "%s: cannot create memory for wcd9330\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, tomtom);
+
+ if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tomtom,
+ tomtom_dai, ARRAY_SIZE(tomtom_dai));
+ else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tomtom,
+ tomtom_i2s_dai, ARRAY_SIZE(tomtom_i2s_dai));
+ mutex_init(&tomtom->codec_mutex);
+ return ret;
+}
+static int tomtom_remove(struct platform_device *pdev)
+{
+ struct tomtom_priv *tomtom = platform_get_drvdata(pdev);
+
+ mutex_destroy(&tomtom->codec_mutex);
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+static struct platform_driver tomtom_codec_driver = {
+ .probe = tomtom_probe,
+ .remove = tomtom_remove,
+ .driver = {
+ .name = "tomtom_codec",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &tomtom_pm_ops,
+#endif
+ },
+};
+
+static int __init tomtom_codec_init(void)
+{
+ return platform_driver_register(&tomtom_codec_driver);
+}
+
+static void __exit tomtom_codec_exit(void)
+{
+ platform_driver_unregister(&tomtom_codec_driver);
+}
+
+module_init(tomtom_codec_init);
+module_exit(tomtom_codec_exit);
+
+MODULE_DESCRIPTION("TomTom codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9330.h b/sound/soc/codecs/wcd9330.h
new file mode 100644
index 000000000000..d8acbeab9443
--- /dev/null
+++ b/sound/soc/codecs/wcd9330.h
@@ -0,0 +1,128 @@
+/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef WCD9330_H
+#define WCD9330_H
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/apr_audio-v2.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
+#include "wcd9xxx-common.h"
+
+#define TOMTOM_NUM_REGISTERS 0x400
+#define TOMTOM_MAX_REGISTER (TOMTOM_NUM_REGISTERS-1)
+#define TOMTOM_CACHE_SIZE TOMTOM_NUM_REGISTERS
+
+#define TOMTOM_REG_VAL(reg, val) {reg, 0, val}
+#define TOMTOM_MCLK_ID 0
+
+#define TOMTOM_REGISTER_START_OFFSET 0x800
+#define TOMTOM_SB_PGD_PORT_RX_BASE 0x40
+#define TOMTOM_SB_PGD_PORT_TX_BASE 0x50
+
+#define WCD9330_DMIC_CLK_DIV_2 0x00
+#define WCD9330_DMIC_CLK_DIV_3 0x01
+#define WCD9330_DMIC_CLK_DIV_4 0x02
+#define WCD9330_DMIC_CLK_DIV_6 0x03
+#define WCD9330_DMIC_CLK_DIV_16 0x04
+
+#define TOMTOM_ZDET_SUPPORTED true
+
+extern const u8 tomtom_reset_reg_defaults[TOMTOM_CACHE_SIZE];
+struct tomtom_codec_dai_data {
+ u32 rate;
+ u32 *ch_num;
+ u32 ch_act;
+ u32 ch_tot;
+};
+
+enum tomtom_pid_current {
+ TOMTOM_PID_MIC_2P5_UA,
+ TOMTOM_PID_MIC_5_UA,
+ TOMTOM_PID_MIC_10_UA,
+ TOMTOM_PID_MIC_20_UA,
+};
+
+enum tomtom_mbhc_analog_pwr_cfg {
+ TOMTOM_ANALOG_PWR_COLLAPSED = 0,
+ TOMTOM_ANALOG_PWR_ON,
+ TOMTOM_NUM_ANALOG_PWR_CONFIGS,
+};
+
+enum {
+ HPH_PA_NONE = 0,
+ HPH_PA_R,
+ HPH_PA_L,
+ HPH_PA_L_R,
+};
+
+/* Number of input and output Slimbus port */
+enum {
+ TOMTOM_RX1 = 0,
+ TOMTOM_RX2,
+ TOMTOM_RX3,
+ TOMTOM_RX4,
+ TOMTOM_RX5,
+ TOMTOM_RX6,
+ TOMTOM_RX7,
+ TOMTOM_RX8,
+ TOMTOM_RX9,
+ TOMTOM_RX10,
+ TOMTOM_RX11,
+ TOMTOM_RX12,
+ TOMTOM_RX13,
+ TOMTOM_RX_MAX,
+};
+
+enum {
+ TOMTOM_TX1 = 0,
+ TOMTOM_TX2,
+ TOMTOM_TX3,
+ TOMTOM_TX4,
+ TOMTOM_TX5,
+ TOMTOM_TX6,
+ TOMTOM_TX7,
+ TOMTOM_TX8,
+ TOMTOM_TX9,
+ TOMTOM_TX10,
+ TOMTOM_TX11,
+ TOMTOM_TX12,
+ TOMTOM_TX13,
+ TOMTOM_TX14,
+ TOMTOM_TX15,
+ TOMTOM_TX16,
+ TOMTOM_TX_MAX,
+};
+
+extern int tomtom_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
+ bool dapm);
+extern int tomtom_codec_mclk_enable(struct snd_soc_codec *codec,
+ int mclk_enable, bool dapm);
+extern int tomtom_hs_detect(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc_config *mbhc_cfg);
+extern void tomtom_hs_detect_exit(struct snd_soc_codec *codec);
+extern void *tomtom_get_afe_config(struct snd_soc_codec *codec,
+ enum afe_config_type config_type);
+
+extern void tomtom_event_register(
+ int (*machine_event_cb)(struct snd_soc_codec *codec,
+ enum wcd9xxx_codec_event),
+ struct snd_soc_codec *codec);
+extern void tomtom_register_ext_clk_cb(
+ int (*codec_ext_clk_en)(struct snd_soc_codec *codec,
+ int enable, bool dapm),
+ int (*get_ext_clk_cnt) (void),
+ struct snd_soc_codec *codec);
+extern int tomtom_enable_qfuse_sensing(struct snd_soc_codec *codec);
+#endif
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
new file mode 100644
index 000000000000..bb99e98f34e2
--- /dev/null
+++ b/sound/soc/codecs/wcd9335.c
@@ -0,0 +1,14441 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9335/registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/soundwire/swr-wcd.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/info.h>
+#include "wcd9335.h"
+#include "wcd-mbhc-v2.h"
+#include "wcd9xxx-common-v2.h"
+#include "wcd9xxx-resmgr-v2.h"
+#include "wcd_cpe_core.h"
+#include "wcdcal-hwdep.h"
+
+#define TASHA_RX_PORT_START_NUMBER 16
+
+#define WCD9335_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+/* Fractional Rates */
+#define WCD9335_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100)
+
+#define WCD9335_MIX_RATES_MASK (SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define TASHA_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define TASHA_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TASHA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+/*
+ * Timeout in milli seconds and it is the wait time for
+ * slim channel removal interrupt to receive.
+ */
+#define TASHA_SLIM_CLOSE_TIMEOUT 1000
+#define TASHA_SLIM_IRQ_OVERFLOW (1 << 0)
+#define TASHA_SLIM_IRQ_UNDERFLOW (1 << 1)
+#define TASHA_SLIM_IRQ_PORT_CLOSED (1 << 2)
+#define TASHA_MCLK_CLK_12P288MHZ 12288000
+#define TASHA_MCLK_CLK_9P6MHZ 9600000
+
+#define TASHA_SLIM_PGD_PORT_INT_TX_EN0 (TASHA_SLIM_PGD_PORT_INT_EN0 + 2)
+
+#define TASHA_NUM_INTERPOLATORS 9
+#define TASHA_NUM_DECIMATORS 9
+
+#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE))
+#define TASHA_MAD_AUDIO_FIRMWARE_PATH "wcd9335/wcd9335_mad_audio.bin"
+#define TASHA_CPE_SS_ERR_STATUS_MEM_ACCESS (1 << 0)
+#define TASHA_CPE_SS_ERR_STATUS_WDOG_BITE (1 << 1)
+
+#define TASHA_CPE_FATAL_IRQS \
+ (TASHA_CPE_SS_ERR_STATUS_WDOG_BITE | \
+ TASHA_CPE_SS_ERR_STATUS_MEM_ACCESS)
+
+#define SLIM_BW_CLK_GEAR_9 6200000
+#define SLIM_BW_UNVOTE 0
+
+#define CPE_FLL_CLK_75MHZ 75000000
+#define CPE_FLL_CLK_150MHZ 150000000
+#define WCD9335_REG_BITS 8
+
+#define WCD9335_MAX_VALID_ADC_MUX 13
+#define WCD9335_INVALID_ADC_MUX 9
+
+#define TASHA_DIG_CORE_REG_MIN WCD9335_CDC_ANC0_CLK_RESET_CTL
+#define TASHA_DIG_CORE_REG_MAX 0xDFF
+
+/* Convert from vout ctl to micbias voltage in mV */
+#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50)
+
+#define TASHA_ZDET_NUM_MEASUREMENTS 150
+#define TASHA_MBHC_GET_C1(c) ((c & 0xC000) >> 14)
+#define TASHA_MBHC_GET_X1(x) (x & 0x3FFF)
+/* z value compared in milliOhm */
+#define TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000))
+#define TASHA_MBHC_ZDET_CONST (86 * 16384)
+#define TASHA_MBHC_MOISTURE_VREF V_45_MV
+#define TASHA_MBHC_MOISTURE_IREF I_3P0_UA
+
+#define TASHA_VERSION_ENTRY_SIZE 17
+
+#define WCD9335_AMIC_PWR_LEVEL_LP 0
+#define WCD9335_AMIC_PWR_LEVEL_DEFAULT 1
+#define WCD9335_AMIC_PWR_LEVEL_HP 2
+#define WCD9335_AMIC_PWR_LVL_MASK 0x60
+#define WCD9335_AMIC_PWR_LVL_SHIFT 0x5
+
+#define WCD9335_DEC_PWR_LVL_MASK 0x06
+#define WCD9335_DEC_PWR_LVL_LP 0x02
+#define WCD9335_DEC_PWR_LVL_HP 0x04
+#define WCD9335_DEC_PWR_LVL_DF 0x00
+#define WCD9335_STRING_LEN 100
+
+#define CALCULATE_VOUT_D(req_mv) (((req_mv - 650) * 10) / 25)
+
+static int cpe_debug_mode;
+
+#define TASHA_MAX_MICBIAS 4
+#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone"
+#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone"
+#define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone"
+#define DAPM_MICBIAS4_STANDALONE "MIC BIAS4 Standalone"
+
+#define DAPM_LDO_H_STANDALONE "LDO_H"
+module_param(cpe_debug_mode, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(cpe_debug_mode, "boot cpe in debug mode");
+
+#define TASHA_DIG_CORE_COLLAPSE_TIMER_MS (5 * 1000)
+
+#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64
+
+static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = {
+ "cdc-vdd-mic-bias",
+};
+
+enum {
+ POWER_COLLAPSE,
+ POWER_RESUME,
+};
+
+enum tasha_sido_voltage {
+ SIDO_VOLTAGE_SVS_MV = 950,
+ SIDO_VOLTAGE_NOMINAL_MV = 1100,
+};
+
+static enum codec_variant codec_ver;
+
+static int dig_core_collapse_enable = 1;
+module_param(dig_core_collapse_enable, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating");
+
+/* dig_core_collapse timer in seconds */
+static int dig_core_collapse_timer = (TASHA_DIG_CORE_COLLAPSE_TIMER_MS/1000);
+module_param(dig_core_collapse_timer, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating");
+
+/* SVS Scaling enable/disable */
+static int svs_scaling_enabled = 1;
+module_param(svs_scaling_enabled, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(svs_scaling_enabled, "enable/disable svs scaling");
+
+/* SVS buck setting */
+static int sido_buck_svs_voltage = SIDO_VOLTAGE_SVS_MV;
+module_param(sido_buck_svs_voltage, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(sido_buck_svs_voltage,
+ "setting for SVS voltage for SIDO BUCK");
+
+#define TASHA_TX_UNMUTE_DELAY_MS 40
+
+static int tx_unmute_delay = TASHA_TX_UNMUTE_DELAY_MS;
+module_param(tx_unmute_delay, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path");
+
+static struct afe_param_slimbus_slave_port_cfg tasha_slimbus_slave_port_cfg = {
+ .minor_version = 1,
+ .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1,
+ .slave_dev_pgd_la = 0,
+ .slave_dev_intfdev_la = 0,
+ .bit_width = 16,
+ .data_format = 0,
+ .num_channels = 1
+};
+
+struct tasha_mbhc_zdet_param {
+ u16 ldo_ctl;
+ u16 noff;
+ u16 nshift;
+ u16 btn5;
+ u16 btn6;
+ u16 btn7;
+};
+
+static struct afe_param_cdc_reg_page_cfg tasha_cdc_reg_page_cfg = {
+ .minor_version = AFE_API_VERSION_CDC_REG_PAGE_CFG,
+ .enable = 1,
+ .proc_id = AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1,
+};
+
+static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = {
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_MAIN_CTL_1),
+ HW_MAD_AUDIO_ENABLE, 0x1, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_AUDIO_CTL_3),
+ HW_MAD_AUDIO_SLEEP_TIME, 0xF, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_AUDIO_CTL_4),
+ HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG),
+ MAD_AUDIO_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3),
+ MAD_AUDIO_INT_MASK_REG, 0x1, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3),
+ MAD_AUDIO_INT_STATUS_REG, 0x1, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3),
+ MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG),
+ VBAT_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3),
+ VBAT_INT_MASK_REG, 0x08, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3),
+ VBAT_INT_STATUS_REG, 0x08, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3),
+ VBAT_INT_CLEAR_REG, 0x08, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG),
+ VBAT_RELEASE_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3),
+ VBAT_RELEASE_INT_MASK_REG, 0x10, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3),
+ VBAT_RELEASE_INT_STATUS_REG, 0x10, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3),
+ VBAT_RELEASE_INT_CLEAR_REG, 0x10, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_TX_BASE),
+ SB_PGD_PORT_TX_WATERMARK_N, 0x1E, WCD9335_REG_BITS, 0x1
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_TX_BASE),
+ SB_PGD_PORT_TX_ENABLE_N, 0x1, WCD9335_REG_BITS, 0x1
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_RX_BASE),
+ SB_PGD_PORT_RX_WATERMARK_N, 0x1E, WCD9335_REG_BITS, 0x1
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_RX_BASE),
+ SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD9335_REG_BITS, 0x1
+ },
+ { 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_IIR_ADAPT_CTL),
+ AANC_FF_GAIN_ADAPTIVE, 0x4, WCD9335_REG_BITS, 0
+ },
+ { 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_IIR_ADAPT_CTL),
+ AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD9335_REG_BITS, 0
+ },
+ {
+ 1,
+ (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_FF_A_GAIN_CTL),
+ AANC_GAIN_CONTROL, 0xFF, WCD9335_REG_BITS, 0
+ },
+};
+
+static struct afe_param_cdc_reg_cfg_data tasha_audio_reg_cfg = {
+ .num_registers = ARRAY_SIZE(audio_reg_cfg),
+ .reg_data = audio_reg_cfg,
+};
+
+static struct afe_param_id_cdc_aanc_version tasha_cdc_aanc_version = {
+ .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION,
+ .aanc_hw_version = AANC_HW_BLOCK_VERSION_2,
+};
+
+enum {
+ VI_SENSE_1,
+ VI_SENSE_2,
+ AIF4_SWITCH_VALUE,
+ AUDIO_NOMINAL,
+ CPE_NOMINAL,
+ HPH_PA_DELAY,
+ ANC_MIC_AMIC1,
+ ANC_MIC_AMIC2,
+ ANC_MIC_AMIC3,
+ ANC_MIC_AMIC4,
+ ANC_MIC_AMIC5,
+ ANC_MIC_AMIC6,
+ CLASSH_CONFIG,
+};
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ AIF2_PB,
+ AIF2_CAP,
+ AIF3_PB,
+ AIF3_CAP,
+ AIF4_PB,
+ AIF_MIX1_PB,
+ AIF4_MAD_TX,
+ AIF4_VIFEED,
+ AIF5_CPE_TX,
+ NUM_CODEC_DAIS,
+};
+
+enum {
+ INTn_1_MIX_INP_SEL_ZERO = 0,
+ INTn_1_MIX_INP_SEL_DEC0,
+ INTn_1_MIX_INP_SEL_DEC1,
+ INTn_1_MIX_INP_SEL_IIR0,
+ INTn_1_MIX_INP_SEL_IIR1,
+ INTn_1_MIX_INP_SEL_RX0,
+ INTn_1_MIX_INP_SEL_RX1,
+ INTn_1_MIX_INP_SEL_RX2,
+ INTn_1_MIX_INP_SEL_RX3,
+ INTn_1_MIX_INP_SEL_RX4,
+ INTn_1_MIX_INP_SEL_RX5,
+ INTn_1_MIX_INP_SEL_RX6,
+ INTn_1_MIX_INP_SEL_RX7,
+
+};
+
+#define IS_VALID_NATIVE_FIFO_PORT(inp) \
+ ((inp >= INTn_1_MIX_INP_SEL_RX0) && \
+ (inp <= INTn_1_MIX_INP_SEL_RX3))
+
+enum {
+ INTn_2_INP_SEL_ZERO = 0,
+ INTn_2_INP_SEL_RX0,
+ INTn_2_INP_SEL_RX1,
+ INTn_2_INP_SEL_RX2,
+ INTn_2_INP_SEL_RX3,
+ INTn_2_INP_SEL_RX4,
+ INTn_2_INP_SEL_RX5,
+ INTn_2_INP_SEL_RX6,
+ INTn_2_INP_SEL_RX7,
+ INTn_2_INP_SEL_PROXIMITY,
+};
+
+enum {
+ INTERP_EAR = 0,
+ INTERP_HPHL,
+ INTERP_HPHR,
+ INTERP_LO1,
+ INTERP_LO2,
+ INTERP_LO3,
+ INTERP_LO4,
+ INTERP_SPKR1,
+ INTERP_SPKR2,
+};
+
+struct interp_sample_rate {
+ int sample_rate;
+ int rate_val;
+};
+
+static struct interp_sample_rate int_prim_sample_rate_val[] = {
+ {8000, 0x0}, /* 8K */
+ {16000, 0x1}, /* 16K */
+ {24000, -EINVAL},/* 24K */
+ {32000, 0x3}, /* 32K */
+ {48000, 0x4}, /* 48K */
+ {96000, 0x5}, /* 96K */
+ {192000, 0x6}, /* 192K */
+ {384000, 0x7}, /* 384K */
+ {44100, 0x8}, /* 44.1K */
+};
+
+static struct interp_sample_rate int_mix_sample_rate_val[] = {
+ {48000, 0x4}, /* 48K */
+ {96000, 0x5}, /* 96K */
+ {192000, 0x6}, /* 192K */
+};
+
+static const struct wcd9xxx_ch tasha_rx_chs[TASHA_RX_MAX] = {
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER, 0),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 1, 1),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 2, 2),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 3, 3),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 4, 4),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 5, 5),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 6, 6),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 7, 7),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 8, 8),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 9, 9),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 10, 10),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 11, 11),
+ WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 12, 12),
+};
+
+static const struct wcd9xxx_ch tasha_tx_chs[TASHA_TX_MAX] = {
+ WCD9XXX_CH(0, 0),
+ WCD9XXX_CH(1, 1),
+ WCD9XXX_CH(2, 2),
+ WCD9XXX_CH(3, 3),
+ WCD9XXX_CH(4, 4),
+ WCD9XXX_CH(5, 5),
+ WCD9XXX_CH(6, 6),
+ WCD9XXX_CH(7, 7),
+ WCD9XXX_CH(8, 8),
+ WCD9XXX_CH(9, 9),
+ WCD9XXX_CH(10, 10),
+ WCD9XXX_CH(11, 11),
+ WCD9XXX_CH(12, 12),
+ WCD9XXX_CH(13, 13),
+ WCD9XXX_CH(14, 14),
+ WCD9XXX_CH(15, 15),
+};
+
+static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = {
+ /* Needs to define in the same order of DAI enum definitions */
+ 0,
+ BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+ 0,
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF5_CPE_TX),
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX),
+};
+
+static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = {
+ 0, /* AIF1_PB */
+ BIT(AIF2_CAP), /* AIF1_CAP */
+ 0, /* AIF2_PB */
+ BIT(AIF1_CAP), /* AIF2_CAP */
+};
+
+/* Codec supports 2 IIR filters */
+enum {
+ IIR0 = 0,
+ IIR1,
+ IIR_MAX,
+};
+
+/* Each IIR has 5 Filter Stages */
+enum {
+ BAND1 = 0,
+ BAND2,
+ BAND3,
+ BAND4,
+ BAND5,
+ BAND_MAX,
+};
+
+enum {
+ COMPANDER_1, /* HPH_L */
+ COMPANDER_2, /* HPH_R */
+ COMPANDER_3, /* LO1_DIFF */
+ COMPANDER_4, /* LO2_DIFF */
+ COMPANDER_5, /* LO3_SE */
+ COMPANDER_6, /* LO4_SE */
+ COMPANDER_7, /* SWR SPK CH1 */
+ COMPANDER_8, /* SWR SPK CH2 */
+ COMPANDER_MAX,
+};
+
+enum {
+ SRC_IN_HPHL,
+ SRC_IN_LO1,
+ SRC_IN_HPHR,
+ SRC_IN_LO2,
+ SRC_IN_SPKRL,
+ SRC_IN_LO3,
+ SRC_IN_SPKRR,
+ SRC_IN_LO4,
+};
+
+enum {
+ SPLINE_SRC0,
+ SPLINE_SRC1,
+ SPLINE_SRC2,
+ SPLINE_SRC3,
+ SPLINE_SRC_MAX,
+};
+
+/* wcd9335 interrupt table */
+static const struct intr_data wcd9335_intr_table[] = {
+ {WCD9XXX_IRQ_SLIMBUS, false},
+ {WCD9335_IRQ_MBHC_SW_DET, true},
+ {WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, true},
+ {WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, true},
+ {WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, true},
+ {WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true},
+ {WCD9335_IRQ_FLL_LOCK_LOSS, false},
+ {WCD9335_IRQ_HPH_PA_CNPL_COMPLETE, false},
+ {WCD9335_IRQ_HPH_PA_CNPR_COMPLETE, false},
+ {WCD9335_IRQ_EAR_PA_CNP_COMPLETE, false},
+ {WCD9335_IRQ_LINE_PA1_CNP_COMPLETE, false},
+ {WCD9335_IRQ_LINE_PA2_CNP_COMPLETE, false},
+ {WCD9335_IRQ_LINE_PA3_CNP_COMPLETE, false},
+ {WCD9335_IRQ_LINE_PA4_CNP_COMPLETE, false},
+ {WCD9335_IRQ_HPH_PA_OCPL_FAULT, false},
+ {WCD9335_IRQ_HPH_PA_OCPR_FAULT, false},
+ {WCD9335_IRQ_EAR_PA_OCP_FAULT, false},
+ {WCD9335_IRQ_SOUNDWIRE, false},
+ {WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE, false},
+ {WCD9335_IRQ_RCO_ERROR, false},
+ {WCD9335_IRQ_SVA_ERROR, false},
+ {WCD9335_IRQ_MAD_AUDIO, false},
+ {WCD9335_IRQ_MAD_BEACON, false},
+ {WCD9335_IRQ_SVA_OUTBOX1, true},
+ {WCD9335_IRQ_SVA_OUTBOX2, true},
+ {WCD9335_IRQ_MAD_ULTRASOUND, false},
+ {WCD9335_IRQ_VBAT_ATTACK, false},
+ {WCD9335_IRQ_VBAT_RESTORE, false},
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+static struct snd_soc_dai_driver tasha_dai[];
+static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv);
+
+static int tasha_config_compander(struct snd_soc_codec *, int, int);
+static void tasha_codec_set_tx_hold(struct snd_soc_codec *, u16, bool);
+static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+ bool enable);
+
+/* Hold instance to soundwire platform device */
+struct tasha_swr_ctrl_data {
+ struct platform_device *swr_pdev;
+ struct ida swr_ida;
+};
+
+struct wcd_swr_ctrl_platform_data {
+ void *handle; /* holds codec private data */
+ int (*read)(void *handle, int reg);
+ int (*write)(void *handle, int reg, int val);
+ int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
+ int (*clk)(void *handle, bool enable);
+ int (*handle_irq)(void *handle,
+ irqreturn_t (*swrm_irq_handler)(int irq,
+ void *data),
+ void *swrm_handle,
+ int action);
+};
+
+static struct wcd_mbhc_register
+ wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN",
+ WCD9335_ANA_MBHC_MECH, 0x80, 7, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN",
+ WCD9335_ANA_MBHC_MECH, 0x40, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE",
+ WCD9335_ANA_MBHC_MECH, 0x20, 5, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL",
+ WCD9335_MBHC_PLUG_DETECT_CTL, 0x30, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE",
+ WCD9335_ANA_MBHC_ELECT, 0x08, 3, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL",
+ WCD9335_MBHC_PLUG_DETECT_CTL, 0xC0, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL",
+ WCD9335_ANA_MBHC_MECH, 0x04, 2, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE",
+ WCD9335_ANA_MBHC_MECH, 0x10, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE",
+ WCD9335_ANA_MBHC_MECH, 0x08, 3, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND",
+ WCD9335_ANA_MBHC_MECH, 0x01, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC",
+ WCD9335_ANA_MBHC_ELECT, 0x06, 1, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN",
+ WCD9335_ANA_MBHC_ELECT, 0x80, 7, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC",
+ WCD9335_MBHC_PLUG_DETECT_CTL, 0x0F, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC",
+ WCD9335_MBHC_CTL_1, 0x03, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF",
+ WCD9335_MBHC_CTL_2, 0x03, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT",
+ WCD9335_ANA_MBHC_RESULT_3, 0x08, 3, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT",
+ WCD9335_ANA_MBHC_RESULT_3, 0x20, 5, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT",
+ WCD9335_ANA_MBHC_RESULT_3, 0x80, 7, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT",
+ WCD9335_ANA_MBHC_RESULT_3, 0x40, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN",
+ WCD9335_HPH_OCP_CTL, 0x10, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT",
+ WCD9335_ANA_MBHC_RESULT_3, 0x07, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL",
+ WCD9335_ANA_MBHC_ELECT, 0x70, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT",
+ WCD9335_ANA_MBHC_RESULT_3, 0xFF, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL",
+ WCD9335_ANA_MICB2, 0xC0, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME",
+ WCD9335_HPH_CNP_WG_TIME, 0xFF, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN",
+ WCD9335_ANA_HPH, 0x40, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN",
+ WCD9335_ANA_HPH, 0x80, 7, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN",
+ WCD9335_ANA_HPH, 0xC0, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE",
+ WCD9335_ANA_MBHC_RESULT_3, 0x10, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL",
+ 0, 0, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN",
+ WCD9335_ANA_MBHC_ZDET, 0x01, 0, 0),
+ /*
+ * MBHC FSM status register is only available in Tasha 2.0.
+ * So, init with 0 later once the version is known, then values
+ * will be updated.
+ */
+ WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS",
+ 0, 0, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL",
+ WCD9335_MBHC_CTL_2, 0x70, 4, 0),
+};
+
+static const struct wcd_mbhc_intr intr_ids = {
+ .mbhc_sw_intr = WCD9335_IRQ_MBHC_SW_DET,
+ .mbhc_btn_press_intr = WCD9335_IRQ_MBHC_BUTTON_PRESS_DET,
+ .mbhc_btn_release_intr = WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET,
+ .mbhc_hs_ins_intr = WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+ .mbhc_hs_rem_intr = WCD9335_IRQ_MBHC_ELECT_INS_REM_DET,
+ .hph_left_ocp = WCD9335_IRQ_HPH_PA_OCPL_FAULT,
+ .hph_right_ocp = WCD9335_IRQ_HPH_PA_OCPR_FAULT,
+};
+
+struct wcd_vbat {
+ bool is_enabled;
+ bool adc_config;
+ /* Variables to cache Vbat ADC output values */
+ u16 dcp1;
+ u16 dcp2;
+};
+
+struct hpf_work {
+ struct tasha_priv *tasha;
+ u8 decimator;
+ u8 hpf_cut_off_freq;
+ struct delayed_work dwork;
+};
+
+#define WCD9335_SPK_ANC_EN_DELAY_MS 350
+static int spk_anc_en_delay = WCD9335_SPK_ANC_EN_DELAY_MS;
+module_param(spk_anc_en_delay, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path");
+
+struct spk_anc_work {
+ struct tasha_priv *tasha;
+ struct delayed_work dwork;
+};
+
+struct tx_mute_work {
+ struct tasha_priv *tasha;
+ u8 decimator;
+ struct delayed_work dwork;
+};
+
+struct tasha_priv {
+ struct device *dev;
+ struct wcd9xxx *wcd9xxx;
+
+ struct snd_soc_codec *codec;
+ u32 adc_count;
+ u32 rx_bias_count;
+ s32 dmic_0_1_clk_cnt;
+ s32 dmic_2_3_clk_cnt;
+ s32 dmic_4_5_clk_cnt;
+ s32 ldo_h_users;
+ s32 micb_ref[TASHA_MAX_MICBIAS];
+ s32 pullup_ref[TASHA_MAX_MICBIAS];
+
+ u32 anc_slot;
+ bool anc_func;
+
+ /* Vbat module */
+ struct wcd_vbat vbat;
+
+ /* cal info for codec */
+ struct fw_info *fw_data;
+
+ /*track tasha interface type*/
+ u8 intf_type;
+
+ /* num of slim ports required */
+ struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
+
+ /* SoundWire data structure */
+ struct tasha_swr_ctrl_data *swr_ctrl_data;
+ int nr;
+
+ /*compander*/
+ int comp_enabled[COMPANDER_MAX];
+
+ /* Maintain the status of AUX PGA */
+ int aux_pga_cnt;
+ u8 aux_l_gain;
+ u8 aux_r_gain;
+
+ bool spkr_pa_widget_on;
+ struct regulator *spkdrv_reg;
+ struct regulator *spkdrv2_reg;
+
+ bool mbhc_started;
+ /* class h specific data */
+ struct wcd_clsh_cdc_data clsh_d;
+
+ struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg;
+
+ /*
+ * list used to save/restore registers at start and
+ * end of impedance measurement
+ */
+ struct list_head reg_save_restore;
+
+ /* handle to cpe core */
+ struct wcd_cpe_core *cpe_core;
+ u32 current_cpe_clk_freq;
+ enum tasha_sido_voltage sido_voltage;
+ int sido_ccl_cnt;
+
+ u32 ana_rx_supplies;
+ /* Multiplication factor used for impedance detection */
+ int zdet_gain_mul_fact;
+
+ /* to track the status */
+ unsigned long status_mask;
+
+ struct work_struct tasha_add_child_devices_work;
+ struct wcd_swr_ctrl_platform_data swr_plat_data;
+
+ /* Port values for Rx and Tx codec_dai */
+ unsigned int rx_port_value[TASHA_RX_MAX];
+ unsigned int tx_port_value;
+
+ unsigned int vi_feed_value;
+ /* Tasha Interpolator Mode Select for EAR, HPH_L and HPH_R */
+ u32 hph_mode;
+
+ u16 prim_int_users[TASHA_NUM_INTERPOLATORS];
+ int spl_src_users[SPLINE_SRC_MAX];
+
+ struct wcd9xxx_resmgr_v2 *resmgr;
+ struct delayed_work power_gate_work;
+ struct mutex power_lock;
+ struct mutex sido_lock;
+
+ /* mbhc module */
+ struct wcd_mbhc mbhc;
+ struct blocking_notifier_head notifier;
+ struct mutex micb_lock;
+
+ struct clk *wcd_ext_clk;
+ struct clk *wcd_native_clk;
+ struct mutex swr_read_lock;
+ struct mutex swr_write_lock;
+ struct mutex swr_clk_lock;
+ int swr_clk_users;
+ int native_clk_users;
+ int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high);
+
+ struct snd_info_entry *entry;
+ struct snd_info_entry *version_entry;
+ int power_active_ref;
+
+ struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX];
+
+ int (*machine_codec_event_cb)(struct snd_soc_codec *codec,
+ enum wcd9335_codec_event);
+ int spkr_gain_offset;
+ int spkr_mode;
+ int ear_spkr_gain;
+ struct hpf_work tx_hpf_work[TASHA_NUM_DECIMATORS];
+ struct tx_mute_work tx_mute_dwork[TASHA_NUM_DECIMATORS];
+ struct spk_anc_work spk_anc_dwork;
+ struct mutex codec_mutex;
+ int hph_l_gain;
+ int hph_r_gain;
+ int rx_7_count;
+ int rx_8_count;
+ bool clk_mode;
+ bool clk_internal;
+ /* Lock to prevent multiple functions voting at same time */
+ struct mutex sb_clk_gear_lock;
+ /* Count for functions voting or un-voting */
+ u32 ref_count;
+ /* Lock to protect mclk enablement */
+ struct mutex mclk_lock;
+};
+
+static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec,
+ bool vote);
+
+static const struct tasha_reg_mask_val tasha_spkr_default[] = {
+ {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80},
+ {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80},
+ {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01},
+ {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01},
+ {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50},
+ {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50},
+};
+
+static const struct tasha_reg_mask_val tasha_spkr_mode1[] = {
+ {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x00},
+ {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x00},
+ {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x00},
+ {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x00},
+ {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44},
+ {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44},
+};
+
+/*
+ * wcd9335_get_codec_info: Get codec specific information
+ *
+ * @wcd9xxx: pointer to wcd9xxx structure
+ * @wcd_type: pointer to wcd9xxx_codec_type structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9335_get_codec_info(struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_codec_type *wcd_type)
+{
+ u16 id_minor, id_major;
+ struct regmap *wcd_regmap;
+ int rc, val, version = 0;
+
+ if (!wcd9xxx || !wcd_type)
+ return -EINVAL;
+
+ if (!wcd9xxx->regmap) {
+ dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n",
+ __func__);
+ return -EINVAL;
+ }
+ wcd_regmap = wcd9xxx->regmap;
+
+ rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0,
+ (u8 *)&id_minor, sizeof(u16));
+ if (rc)
+ return -EINVAL;
+
+ rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2,
+ (u8 *)&id_major, sizeof(u16));
+ if (rc)
+ return -EINVAL;
+
+ dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n",
+ __func__, id_major, id_minor);
+
+ /* Version detection */
+ if (id_major == TASHA_MAJOR) {
+ regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0,
+ &val);
+ version = ((u8)val & 0x80) >> 7;
+ } else if (id_major == TASHA2P0_MAJOR)
+ version = 2;
+ else
+ dev_err(wcd9xxx->dev, "%s: wcd9335 version unknown (major 0x%x, minor 0x%x)\n",
+ __func__, id_major, id_minor);
+
+ /* Fill codec type info */
+ wcd_type->id_major = id_major;
+ wcd_type->id_minor = id_minor;
+ wcd_type->num_irqs = WCD9335_NUM_IRQS;
+ wcd_type->version = version;
+ wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1;
+ wcd_type->i2c_chip_status = 0x01;
+ wcd_type->intr_tbl = wcd9335_intr_table;
+ wcd_type->intr_tbl_size = ARRAY_SIZE(wcd9335_intr_table);
+
+ wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] =
+ WCD9335_INTR_PIN1_STATUS0;
+ wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] =
+ WCD9335_INTR_PIN1_CLEAR0;
+ wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] =
+ WCD9335_INTR_PIN1_MASK0;
+ wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] =
+ WCD9335_INTR_LEVEL0;
+ wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] =
+ WCD9335_INTR_CLR_COMMIT;
+
+ return rc;
+}
+EXPORT_SYMBOL(wcd9335_get_codec_info);
+
+/*
+ * wcd9335_bringdown: Bringdown WCD Codec
+ *
+ * @wcd9xxx: Pointer to wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9335_bringdown(struct wcd9xxx *wcd9xxx)
+{
+ if (!wcd9xxx || !wcd9xxx->regmap)
+ return -EINVAL;
+
+ regmap_write(wcd9xxx->regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x04);
+
+ return 0;
+}
+EXPORT_SYMBOL(wcd9335_bringdown);
+
+/*
+ * wcd9335_bringup: Bringup WCD Codec
+ *
+ * @wcd9xxx: Pointer to the wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9335_bringup(struct wcd9xxx *wcd9xxx)
+{
+ int ret = 0;
+ int val, byte0;
+ struct regmap *wcd_regmap;
+
+ if (!wcd9xxx)
+ return -EINVAL;
+
+ if (!wcd9xxx->regmap) {
+ dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n",
+ __func__);
+ return -EINVAL;
+ }
+ wcd_regmap = wcd9xxx->regmap;
+
+ regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, &val);
+ regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, &byte0);
+
+ if ((val < 0) || (byte0 < 0)) {
+ dev_err(wcd9xxx->dev, "%s: tasha codec version detection fail!\n",
+ __func__);
+ return -EINVAL;
+ }
+ if ((val & 0x80) && (byte0 == 0x0)) {
+ dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.1\n",
+ __func__);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01);
+ regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC);
+ regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x5);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x7);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x3);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3);
+ } else if (byte0 == 0x1) {
+ dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v2.0\n",
+ __func__);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01);
+ regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_TEST_2, 0x00);
+ regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_8, 0x6F);
+ regmap_write(wcd_regmap, WCD9335_BIAS_VBG_FINE_ADJ, 0x65);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x5);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x7);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x3);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3);
+ } else if ((byte0 == 0) && (!(val & 0x80))) {
+ dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.0\n",
+ __func__);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01);
+ regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC);
+ regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x3);
+ regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3);
+ } else {
+ dev_err(wcd9xxx->dev, "%s: tasha codec version unknown\n",
+ __func__);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(wcd9335_bringup);
+
+/**
+ * tasha_set_spkr_gain_offset - offset the speaker path
+ * gain with the given offset value.
+ *
+ * @codec: codec instance
+ * @offset: Indicates speaker path gain offset value.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int tasha_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset)
+{
+ struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (!priv)
+ return -EINVAL;
+
+ priv->spkr_gain_offset = offset;
+ return 0;
+}
+EXPORT_SYMBOL(tasha_set_spkr_gain_offset);
+
+/**
+ * tasha_set_spkr_mode - Configures speaker compander and smartboost
+ * settings based on speaker mode.
+ *
+ * @codec: codec instance
+ * @mode: Indicates speaker configuration mode.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int tasha_set_spkr_mode(struct snd_soc_codec *codec, int mode)
+{
+ struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int i;
+ const struct tasha_reg_mask_val *regs;
+ int size;
+
+ if (!priv)
+ return -EINVAL;
+
+ switch (mode) {
+ case SPKR_MODE_1:
+ regs = tasha_spkr_mode1;
+ size = ARRAY_SIZE(tasha_spkr_mode1);
+ break;
+ default:
+ regs = tasha_spkr_default;
+ size = ARRAY_SIZE(tasha_spkr_default);
+ break;
+ }
+
+ priv->spkr_mode = mode;
+ for (i = 0; i < size; i++)
+ snd_soc_update_bits(codec, regs[i].reg,
+ regs[i].mask, regs[i].val);
+ return 0;
+}
+EXPORT_SYMBOL(tasha_set_spkr_mode);
+
+static void tasha_enable_sido_buck(struct snd_soc_codec *codec)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_update_bits(codec, WCD9335_ANA_RCO, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x02, 0x02);
+ /* 100us sleep needed after IREF settings */
+ usleep_range(100, 110);
+ snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x04, 0x04);
+ /* 100us sleep needed after VREF settings */
+ usleep_range(100, 110);
+ tasha->resmgr->sido_input_src = SIDO_SOURCE_RCO_BG;
+}
+
+static void tasha_cdc_sido_ccl_enable(struct tasha_priv *tasha, bool ccl_flag)
+{
+ struct snd_soc_codec *codec = tasha->codec;
+
+ if (!codec)
+ return;
+
+ if (!TASHA_IS_2_0(tasha->wcd9xxx)) {
+ dev_dbg(codec->dev, "%s: tasha version < 2p0, return\n",
+ __func__);
+ return;
+ }
+ dev_dbg(codec->dev, "%s: sido_ccl_cnt=%d, ccl_flag:%d\n",
+ __func__, tasha->sido_ccl_cnt, ccl_flag);
+ if (ccl_flag) {
+ if (++tasha->sido_ccl_cnt == 1)
+ snd_soc_update_bits(codec,
+ WCD9335_SIDO_SIDO_CCL_10, 0xFF, 0x6E);
+ } else {
+ if (tasha->sido_ccl_cnt == 0) {
+ dev_dbg(codec->dev, "%s: sido_ccl already disabled\n",
+ __func__);
+ return;
+ }
+ if (--tasha->sido_ccl_cnt == 0)
+ snd_soc_update_bits(codec,
+ WCD9335_SIDO_SIDO_CCL_10, 0xFF, 0x02);
+ }
+}
+
+static bool tasha_cdc_is_svs_enabled(struct tasha_priv *tasha)
+{
+ if (TASHA_IS_2_0(tasha->wcd9xxx) &&
+ svs_scaling_enabled)
+ return true;
+
+ return false;
+}
+
+static int tasha_cdc_req_mclk_enable(struct tasha_priv *tasha,
+ bool enable)
+{
+ int ret = 0;
+
+ mutex_lock(&tasha->mclk_lock);
+ if (enable) {
+ tasha_cdc_sido_ccl_enable(tasha, true);
+ ret = clk_prepare_enable(tasha->wcd_ext_clk);
+ if (ret) {
+ dev_err(tasha->dev, "%s: ext clk enable failed\n",
+ __func__);
+ goto unlock_mutex;
+ }
+ /* get BG */
+ wcd_resmgr_enable_master_bias(tasha->resmgr);
+ /* get MCLK */
+ wcd_resmgr_enable_clk_block(tasha->resmgr, WCD_CLK_MCLK);
+ } else {
+ /* put MCLK */
+ wcd_resmgr_disable_clk_block(tasha->resmgr, WCD_CLK_MCLK);
+ /* put BG */
+ wcd_resmgr_disable_master_bias(tasha->resmgr);
+ clk_disable_unprepare(tasha->wcd_ext_clk);
+ tasha_cdc_sido_ccl_enable(tasha, false);
+ }
+unlock_mutex:
+ mutex_unlock(&tasha->mclk_lock);
+ return ret;
+}
+
+static int tasha_cdc_check_sido_value(enum tasha_sido_voltage req_mv)
+{
+ if ((req_mv != SIDO_VOLTAGE_SVS_MV) &&
+ (req_mv != SIDO_VOLTAGE_NOMINAL_MV))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void tasha_codec_apply_sido_voltage(
+ struct tasha_priv *tasha,
+ enum tasha_sido_voltage req_mv)
+{
+ u32 vout_d_val;
+ struct snd_soc_codec *codec = tasha->codec;
+ int ret;
+
+ if (!codec)
+ return;
+
+ if (!tasha_cdc_is_svs_enabled(tasha))
+ return;
+
+ if ((sido_buck_svs_voltage != SIDO_VOLTAGE_SVS_MV) &&
+ (sido_buck_svs_voltage != SIDO_VOLTAGE_NOMINAL_MV))
+ sido_buck_svs_voltage = SIDO_VOLTAGE_SVS_MV;
+
+ ret = tasha_cdc_check_sido_value(req_mv);
+ if (ret < 0) {
+ dev_dbg(codec->dev, "%s: requested mv=%d not in range\n",
+ __func__, req_mv);
+ return;
+ }
+ if (req_mv == tasha->sido_voltage) {
+ dev_dbg(codec->dev, "%s: Already at requested mv=%d\n",
+ __func__, req_mv);
+ return;
+ }
+ if (req_mv == sido_buck_svs_voltage) {
+ if (test_bit(AUDIO_NOMINAL, &tasha->status_mask) ||
+ test_bit(CPE_NOMINAL, &tasha->status_mask)) {
+ dev_dbg(codec->dev,
+ "%s: nominal client running, status_mask=%lu\n",
+ __func__, tasha->status_mask);
+ return;
+ }
+ }
+ /* compute the vout_d step value */
+ vout_d_val = CALCULATE_VOUT_D(req_mv);
+ snd_soc_write(codec, WCD9335_ANA_BUCK_VOUT_D, vout_d_val & 0xFF);
+ snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x80, 0x80);
+
+ /* 1 msec sleep required after SIDO Vout_D voltage change */
+ usleep_range(1000, 1100);
+ tasha->sido_voltage = req_mv;
+ dev_dbg(codec->dev,
+ "%s: updated SIDO buck Vout_D to %d, vout_d step = %u\n",
+ __func__, tasha->sido_voltage, vout_d_val);
+
+ snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL,
+ 0x80, 0x00);
+}
+
+static int tasha_codec_update_sido_voltage(
+ struct tasha_priv *tasha,
+ enum tasha_sido_voltage req_mv)
+{
+ int ret = 0;
+
+ if (!tasha_cdc_is_svs_enabled(tasha))
+ return ret;
+
+ mutex_lock(&tasha->sido_lock);
+ /* enable mclk before setting SIDO voltage */
+ ret = tasha_cdc_req_mclk_enable(tasha, true);
+ if (ret) {
+ dev_err(tasha->dev, "%s: ext clk enable failed\n",
+ __func__);
+ goto err;
+ }
+ tasha_codec_apply_sido_voltage(tasha, req_mv);
+ tasha_cdc_req_mclk_enable(tasha, false);
+
+err:
+ mutex_unlock(&tasha->sido_lock);
+ return ret;
+}
+
+int tasha_enable_efuse_sensing(struct snd_soc_codec *codec)
+{
+ struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ tasha_cdc_mclk_enable(codec, true, false);
+
+ if (!TASHA_IS_2_0(priv->wcd9xxx))
+ snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL,
+ 0x1E, 0x02);
+ snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL,
+ 0x01, 0x01);
+ /*
+ * 5ms sleep required after enabling efuse control
+ * before checking the status.
+ */
+ usleep_range(5000, 5500);
+ if (!(snd_soc_read(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS) & 0x01))
+ WARN(1, "%s: Efuse sense is not complete\n", __func__);
+
+ if (TASHA_IS_2_0(priv->wcd9xxx)) {
+ if (!(snd_soc_read(codec,
+ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0) & 0x40))
+ snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST,
+ 0x04, 0x00);
+ tasha_enable_sido_buck(codec);
+ }
+
+ tasha_cdc_mclk_enable(codec, false, false);
+
+ return 0;
+}
+EXPORT_SYMBOL(tasha_enable_efuse_sensing);
+
+void *tasha_get_afe_config(struct snd_soc_codec *codec,
+ enum afe_config_type config_type)
+{
+ struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (config_type) {
+ case AFE_SLIMBUS_SLAVE_CONFIG:
+ return &priv->slimbus_slave_cfg;
+ case AFE_CDC_REGISTERS_CONFIG:
+ return &tasha_audio_reg_cfg;
+ case AFE_SLIMBUS_SLAVE_PORT_CONFIG:
+ return &tasha_slimbus_slave_port_cfg;
+ case AFE_AANC_VERSION:
+ return &tasha_cdc_aanc_version;
+ case AFE_CLIP_BANK_SEL:
+ return NULL;
+ case AFE_CDC_CLIP_REGISTERS_CONFIG:
+ return NULL;
+ case AFE_CDC_REGISTER_PAGE_CONFIG:
+ return &tasha_cdc_reg_page_cfg;
+ default:
+ dev_err(codec->dev, "%s: Unknown config_type 0x%x\n",
+ __func__, config_type);
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(tasha_get_afe_config);
+
+/*
+ * tasha_event_register: Registers a machine driver callback
+ * function with codec private data for post ADSP sub-system
+ * restart (SSR). This callback function will be called from
+ * codec driver once codec comes out of reset after ADSP SSR.
+ *
+ * @machine_event_cb: callback function from machine driver
+ * @codec: Codec instance
+ *
+ * Return: none
+ */
+void tasha_event_register(
+ int (*machine_event_cb)(struct snd_soc_codec *codec,
+ enum wcd9335_codec_event),
+ struct snd_soc_codec *codec)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ if (tasha)
+ tasha->machine_codec_event_cb = machine_event_cb;
+ else
+ dev_dbg(codec->dev, "%s: Invalid tasha_priv data\n", __func__);
+}
+EXPORT_SYMBOL(tasha_event_register);
+
+static int tasha_mbhc_request_irq(struct snd_soc_codec *codec,
+ int irq, irq_handler_t handler,
+ const char *name, void *data)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+
+ return wcd9xxx_request_irq(core_res, irq, handler, name, data);
+}
+
+static void tasha_mbhc_irq_control(struct snd_soc_codec *codec,
+ int irq, bool enable)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+ if (enable)
+ wcd9xxx_enable_irq(core_res, irq);
+ else
+ wcd9xxx_disable_irq(core_res, irq);
+}
+
+static int tasha_mbhc_free_irq(struct snd_soc_codec *codec,
+ int irq, void *data)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+
+ wcd9xxx_free_irq(core_res, irq, data);
+ return 0;
+}
+
+static void tasha_mbhc_clk_setup(struct snd_soc_codec *codec,
+ bool enable)
+{
+ if (enable)
+ snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1,
+ 0x80, 0x80);
+ else
+ snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1,
+ 0x80, 0x00);
+}
+
+static int tasha_mbhc_btn_to_num(struct snd_soc_codec *codec)
+{
+ return snd_soc_read(codec, WCD9335_ANA_MBHC_RESULT_3) & 0x7;
+}
+
+static void tasha_mbhc_mbhc_bias_control(struct snd_soc_codec *codec,
+ bool enable)
+{
+ if (enable)
+ snd_soc_update_bits(codec, WCD9335_ANA_MBHC_ELECT,
+ 0x01, 0x01);
+ else
+ snd_soc_update_bits(codec, WCD9335_ANA_MBHC_ELECT,
+ 0x01, 0x00);
+}
+
+static void tasha_mbhc_program_btn_thr(struct snd_soc_codec *codec,
+ s16 *btn_low, s16 *btn_high,
+ int num_btn, bool is_micbias)
+{
+ int i;
+ int vth;
+
+ if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+ dev_err(codec->dev, "%s: invalid number of buttons: %d\n",
+ __func__, num_btn);
+ return;
+ }
+ /*
+ * Tasha just needs one set of thresholds for button detection
+ * due to micbias voltage ramp to pullup upon button press. So
+ * btn_low and is_micbias are ignored and always program button
+ * thresholds using btn_high.
+ */
+ for (i = 0; i < num_btn; i++) {
+ vth = ((btn_high[i] * 2) / 25) & 0x3F;
+ snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN0 + i,
+ 0xFC, vth << 2);
+ dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n",
+ __func__, i, btn_high[i], vth);
+ }
+}
+
+static bool tasha_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+ if (lock)
+ return wcd9xxx_lock_sleep(core_res);
+ else {
+ wcd9xxx_unlock_sleep(core_res);
+ return 0;
+ }
+}
+
+static int tasha_mbhc_register_notifier(struct wcd_mbhc *mbhc,
+ struct notifier_block *nblock,
+ bool enable)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ if (enable)
+ return blocking_notifier_chain_register(&tasha->notifier,
+ nblock);
+ else
+ return blocking_notifier_chain_unregister(&tasha->notifier,
+ nblock);
+}
+
+static bool tasha_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num)
+{
+ u8 val;
+ if (micb_num == MIC_BIAS_2) {
+ val = (snd_soc_read(mbhc->codec, WCD9335_ANA_MICB2) >> 6);
+ if (val == 0x01)
+ return true;
+ }
+ return false;
+}
+
+static bool tasha_mbhc_hph_pa_on_status(struct snd_soc_codec *codec)
+{
+ return (snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) ? true : false;
+}
+
+static void tasha_mbhc_hph_l_pull_up_control(struct snd_soc_codec *codec,
+ enum mbhc_hs_pullup_iref pull_up_cur)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ if (!tasha)
+ return;
+
+ /* Default pull up current to 2uA */
+ if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA ||
+ pull_up_cur == I_DEFAULT)
+ pull_up_cur = I_2P0_UA;
+
+ dev_dbg(codec->dev, "%s: HS pull up current:%d\n",
+ __func__, pull_up_cur);
+
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
+ snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL,
+ 0xC0, pull_up_cur << 6);
+ else
+ snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL,
+ 0xC0, 0x40);
+}
+
+static int tasha_enable_ext_mb_source(struct wcd_mbhc *mbhc,
+ bool turn_on)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ struct on_demand_supply *supply;
+
+ if (!tasha)
+ return -EINVAL;
+
+ supply = &tasha->on_demand_list[ON_DEMAND_MICBIAS];
+ if (!supply->supply) {
+ dev_dbg(codec->dev, "%s: warning supply not present ond for %s\n",
+ __func__, "onDemand Micbias");
+ return ret;
+ }
+
+ dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on,
+ supply->ondemand_supply_count);
+
+ if (turn_on) {
+ if (!(supply->ondemand_supply_count)) {
+ ret = snd_soc_dapm_force_enable_pin(
+ snd_soc_codec_get_dapm(codec),
+ "MICBIAS_REGULATOR");
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+ }
+ supply->ondemand_supply_count++;
+ } else {
+ if (supply->ondemand_supply_count > 0)
+ supply->ondemand_supply_count--;
+ if (!(supply->ondemand_supply_count)) {
+ ret = snd_soc_dapm_disable_pin(
+ snd_soc_codec_get_dapm(codec),
+ "MICBIAS_REGULATOR");
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+ }
+ }
+
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to %s external micbias source\n",
+ __func__, turn_on ? "enable" : "disabled");
+ else
+ dev_dbg(codec->dev, "%s: %s external micbias source\n",
+ __func__, turn_on ? "Enabled" : "Disabled");
+
+ return ret;
+}
+
+static int tasha_micbias_control(struct snd_soc_codec *codec,
+ int micb_num,
+ int req, bool is_dapm)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+ int pre_off_event = 0, post_off_event = 0;
+ int post_on_event = 0, post_dapm_off = 0;
+ int post_dapm_on = 0;
+
+ if ((micb_index < 0) || (micb_index > TASHA_MAX_MICBIAS - 1)) {
+ dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+ __func__, micb_index);
+ return -EINVAL;
+ }
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD9335_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD9335_ANA_MICB2;
+ pre_off_event = WCD_EVENT_PRE_MICBIAS_2_OFF;
+ post_off_event = WCD_EVENT_POST_MICBIAS_2_OFF;
+ post_on_event = WCD_EVENT_POST_MICBIAS_2_ON;
+ post_dapm_on = WCD_EVENT_POST_DAPM_MICBIAS_2_ON;
+ post_dapm_off = WCD_EVENT_POST_DAPM_MICBIAS_2_OFF;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD9335_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD9335_ANA_MICB4;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid micbias number: %d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ }
+ mutex_lock(&tasha->micb_lock);
+
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ tasha->pullup_ref[micb_index]++;
+ if ((tasha->pullup_ref[micb_index] == 1) &&
+ (tasha->micb_ref[micb_index] == 0))
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80);
+ break;
+ case MICB_PULLUP_DISABLE:
+ if (tasha->pullup_ref[micb_index] > 0)
+ tasha->pullup_ref[micb_index]--;
+ if ((tasha->pullup_ref[micb_index] == 0) &&
+ (tasha->micb_ref[micb_index] == 0))
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00);
+ break;
+ case MICB_ENABLE:
+ tasha->micb_ref[micb_index]++;
+ if (tasha->micb_ref[micb_index] == 1) {
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40);
+ if (post_on_event)
+ blocking_notifier_call_chain(&tasha->notifier,
+ post_on_event, &tasha->mbhc);
+ }
+ if (is_dapm && post_dapm_on)
+ blocking_notifier_call_chain(&tasha->notifier,
+ post_dapm_on, &tasha->mbhc);
+ break;
+ case MICB_DISABLE:
+ if (tasha->micb_ref[micb_index] > 0)
+ tasha->micb_ref[micb_index]--;
+ if ((tasha->micb_ref[micb_index] == 0) &&
+ (tasha->pullup_ref[micb_index] > 0))
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80);
+ else if ((tasha->micb_ref[micb_index] == 0) &&
+ (tasha->pullup_ref[micb_index] == 0)) {
+ if (pre_off_event)
+ blocking_notifier_call_chain(&tasha->notifier,
+ pre_off_event, &tasha->mbhc);
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00);
+ if (post_off_event)
+ blocking_notifier_call_chain(&tasha->notifier,
+ post_off_event, &tasha->mbhc);
+ }
+ if (is_dapm && post_dapm_off)
+ blocking_notifier_call_chain(&tasha->notifier,
+ post_dapm_off, &tasha->mbhc);
+ break;
+ };
+
+ dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n",
+ __func__, micb_num, tasha->micb_ref[micb_index],
+ tasha->pullup_ref[micb_index]);
+
+ mutex_unlock(&tasha->micb_lock);
+
+ return 0;
+}
+
+static int tasha_mbhc_request_micbias(struct snd_soc_codec *codec,
+ int micb_num, int req)
+{
+ int ret;
+
+ /*
+ * If micbias is requested, make sure that there
+ * is vote to enable mclk
+ */
+ if (req == MICB_ENABLE)
+ tasha_cdc_mclk_enable(codec, true, false);
+
+ ret = tasha_micbias_control(codec, micb_num, req, false);
+
+ /*
+ * Release vote for mclk while requesting for
+ * micbias disable
+ */
+ if (req == MICB_DISABLE)
+ tasha_cdc_mclk_enable(codec, false, false);
+
+ return ret;
+}
+
+static void tasha_mbhc_micb_ramp_control(struct snd_soc_codec *codec,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP,
+ 0x1C, 0x0C);
+ snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP,
+ 0x80, 0x80);
+ } else {
+ snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP,
+ 0x80, 0x00);
+ snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP,
+ 0x1C, 0x00);
+ }
+}
+
+static struct firmware_cal *tasha_get_hwdep_fw_cal(struct wcd_mbhc *mbhc,
+ enum wcd_cal_type type)
+{
+ struct tasha_priv *tasha;
+ struct firmware_cal *hwdep_cal;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ if (!codec) {
+ pr_err("%s: NULL codec pointer\n", __func__);
+ return NULL;
+ }
+ tasha = snd_soc_codec_get_drvdata(codec);
+ hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, type);
+ if (!hwdep_cal)
+ dev_err(codec->dev, "%s: cal not sent by %d\n",
+ __func__, type);
+
+ return hwdep_cal;
+}
+
+static int tasha_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec,
+ int req_volt,
+ int micb_num)
+{
+ int cur_vout_ctl, req_vout_ctl;
+ int micb_reg, micb_val, micb_en;
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD9335_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD9335_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD9335_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD9335_ANA_MICB4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * If requested micbias voltage is same as current micbias
+ * voltage, then just return. Otherwise, adjust voltage as
+ * per requested value. If micbias is already enabled, then
+ * to avoid slow micbias ramp-up or down enable pull-up
+ * momentarily, change the micbias value and then re-enable
+ * micbias.
+ */
+ micb_val = snd_soc_read(codec, micb_reg);
+ micb_en = (micb_val & 0xC0) >> 6;
+ cur_vout_ctl = micb_val & 0x3F;
+
+ req_vout_ctl = wcd9335_get_micb_vout_ctl_val(req_volt);
+ if (IS_ERR_VALUE(req_vout_ctl))
+ return -EINVAL;
+ if (cur_vout_ctl == req_vout_ctl)
+ return 0;
+
+ dev_dbg(codec->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n",
+ __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl),
+ req_volt, micb_en);
+
+ if (micb_en == 0x1)
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80);
+
+ snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl);
+
+ if (micb_en == 0x1) {
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40);
+ /*
+ * Add 2ms delay as per HW requirement after enabling
+ * micbias
+ */
+ usleep_range(2000, 2100);
+ }
+
+ return 0;
+}
+
+static int tasha_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec,
+ int micb_num, bool req_en)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+ int rc, micb_mv;
+
+ if (micb_num != MIC_BIAS_2)
+ return -EINVAL;
+
+ /*
+ * If device tree micbias level is already above the minimum
+ * voltage needed to detect threshold microphone, then do
+ * not change the micbias, just return.
+ */
+ if (pdata->micbias.micb2_mv >= WCD_MBHC_THR_HS_MICB_MV)
+ return 0;
+
+ micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : pdata->micbias.micb2_mv;
+
+ mutex_lock(&tasha->micb_lock);
+ rc = tasha_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_2);
+ mutex_unlock(&tasha->micb_lock);
+
+ return rc;
+}
+
+static inline void tasha_mbhc_get_result_params(struct wcd9xxx *wcd9xxx,
+ s16 *d1_a, u16 noff,
+ int32_t *zdet)
+{
+ int i;
+ int val, val1;
+ s16 c1;
+ s32 x1, d1;
+ int32_t denom;
+ int minCode_param[] = {
+ 3277, 1639, 820, 410, 205, 103, 52, 26
+ };
+
+ regmap_update_bits(wcd9xxx->regmap, WCD9335_ANA_MBHC_ZDET, 0x20, 0x20);
+ for (i = 0; i < TASHA_ZDET_NUM_MEASUREMENTS; i++) {
+ regmap_read(wcd9xxx->regmap, WCD9335_ANA_MBHC_RESULT_2, &val);
+ if (val & 0x80)
+ break;
+ }
+ val = val << 0x8;
+ regmap_read(wcd9xxx->regmap, WCD9335_ANA_MBHC_RESULT_1, &val1);
+ val |= val1;
+ regmap_update_bits(wcd9xxx->regmap, WCD9335_ANA_MBHC_ZDET, 0x20, 0x00);
+ x1 = TASHA_MBHC_GET_X1(val);
+ c1 = TASHA_MBHC_GET_C1(val);
+ /* If ramp is not complete, give additional 5ms */
+ if ((c1 < 2) && x1)
+ usleep_range(5000, 5050);
+
+ if (!c1 || !x1) {
+ dev_dbg(wcd9xxx->dev,
+ "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n",
+ __func__, c1, x1);
+ goto ramp_down;
+ }
+ d1 = d1_a[c1];
+ denom = (x1 * d1) - (1 << (14 - noff));
+ if (denom > 0)
+ *zdet = (TASHA_MBHC_ZDET_CONST * 1000) / denom;
+ else if (x1 < minCode_param[noff])
+ *zdet = TASHA_ZDET_FLOATING_IMPEDANCE;
+
+ dev_dbg(wcd9xxx->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n",
+ __func__, d1, c1, x1, *zdet);
+ramp_down:
+ i = 0;
+ while (x1) {
+ regmap_bulk_read(wcd9xxx->regmap,
+ WCD9335_ANA_MBHC_RESULT_1, (u8 *)&val, 2);
+ x1 = TASHA_MBHC_GET_X1(val);
+ i++;
+ if (i == TASHA_ZDET_NUM_MEASUREMENTS)
+ break;
+ }
+}
+
+/*
+ * tasha_mbhc_zdet_gpio_ctrl: Register callback function for
+ * controlling the switch on hifi amps. Default switch state
+ * will put a 51ohm load in parallel to the hph load. So,
+ * impedance detection function will pull the gpio high
+ * to make the switch open.
+ *
+ * @zdet_gpio_cb: callback function from machine driver
+ * @codec: Codec instance
+ *
+ * Return: none
+ */
+void tasha_mbhc_zdet_gpio_ctrl(
+ int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high),
+ struct snd_soc_codec *codec)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ tasha->zdet_gpio_cb = zdet_gpio_cb;
+}
+EXPORT_SYMBOL(tasha_mbhc_zdet_gpio_ctrl);
+
+static void tasha_mbhc_zdet_ramp(struct snd_soc_codec *codec,
+ struct tasha_mbhc_zdet_param *zdet_param,
+ int32_t *zl, int32_t *zr, s16 *d1_a)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ int32_t zdet = 0;
+
+ snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_ANA_CTL, 0x70,
+ zdet_param->ldo_ctl << 4);
+ snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN5, 0xFC,
+ zdet_param->btn5);
+ snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN6, 0xFC,
+ zdet_param->btn6);
+ snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN7, 0xFC,
+ zdet_param->btn7);
+ snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_ANA_CTL, 0x0F,
+ zdet_param->noff);
+ snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x0F,
+ zdet_param->nshift);
+
+ if (!zl)
+ goto z_right;
+ /* Start impedance measurement for HPH_L */
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD9335_ANA_MBHC_ZDET, 0x80, 0x80);
+ dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_L, noff = %d\n",
+ __func__, zdet_param->noff);
+ tasha_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD9335_ANA_MBHC_ZDET, 0x80, 0x00);
+
+ *zl = zdet;
+
+z_right:
+ if (!zr)
+ return;
+ /* Start impedance measurement for HPH_R */
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD9335_ANA_MBHC_ZDET, 0x40, 0x40);
+ dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_R, noff = %d\n",
+ __func__, zdet_param->noff);
+ tasha_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD9335_ANA_MBHC_ZDET, 0x40, 0x00);
+
+ *zr = zdet;
+}
+
+static inline void tasha_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec,
+ int32_t *z_val, int flag_l_r)
+{
+ s16 q1;
+ int q1_cal;
+
+ if (*z_val < (TASHA_ZDET_VAL_400/1000))
+ q1 = snd_soc_read(codec,
+ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r));
+ else
+ q1 = snd_soc_read(codec,
+ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (2 * flag_l_r));
+ if (q1 & 0x80)
+ q1_cal = (10000 - ((q1 & 0x7F) * 25));
+ else
+ q1_cal = (10000 + (q1 * 25));
+ if (q1_cal > 0)
+ *z_val = ((*z_val) * 10000) / q1_cal;
+}
+
+static void tasha_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+ s16 reg0, reg1, reg2, reg3, reg4;
+ int32_t z1L, z1R, z1Ls;
+ int zMono, z_diff1, z_diff2;
+ bool is_fsm_disable = false;
+ bool is_change = false;
+ struct tasha_mbhc_zdet_param zdet_param[] = {
+ {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */
+ {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */
+ {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */
+ {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */
+ };
+ struct tasha_mbhc_zdet_param *zdet_param_ptr = NULL;
+ s16 d1_a[][4] = {
+ {0, 30, 90, 30},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ };
+ s16 *d1 = NULL;
+
+ if (!TASHA_IS_2_0(wcd9xxx)) {
+ dev_dbg(codec->dev, "%s: Z-det is not supported for this codec version\n",
+ __func__);
+ *zl = 0;
+ *zr = 0;
+ return;
+ }
+ WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+ if (tasha->zdet_gpio_cb)
+ is_change = tasha->zdet_gpio_cb(codec, true);
+
+ reg0 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN5);
+ reg1 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN6);
+ reg2 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN7);
+ reg3 = snd_soc_read(codec, WCD9335_MBHC_CTL_1);
+ reg4 = snd_soc_read(codec, WCD9335_MBHC_ZDET_ANA_CTL);
+
+ if (snd_soc_read(codec, WCD9335_ANA_MBHC_ELECT) & 0x80) {
+ is_fsm_disable = true;
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD9335_ANA_MBHC_ELECT, 0x80, 0x00);
+ }
+
+ /* For NO-jack, disable L_DET_EN before Z-det measurements */
+ if (mbhc->hphl_swh)
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD9335_ANA_MBHC_MECH, 0x80, 0x00);
+
+ /* Enable AZ */
+ snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1, 0x0C, 0x04);
+ /* Turn off 100k pull down on HPHL */
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD9335_ANA_MBHC_MECH, 0x01, 0x00);
+
+ /* First get impedance on Left */
+ d1 = d1_a[1];
+ zdet_param_ptr = &zdet_param[1];
+ tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1);
+
+ if (!TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z1L))
+ goto left_ch_impedance;
+
+ /* second ramp for left ch */
+ if (z1L < TASHA_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1L > TASHA_ZDET_VAL_400) && (z1L <= TASHA_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1L > TASHA_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1);
+
+left_ch_impedance:
+ if ((z1L == TASHA_ZDET_FLOATING_IMPEDANCE) ||
+ (z1L > TASHA_ZDET_VAL_100K)) {
+ *zl = TASHA_ZDET_FLOATING_IMPEDANCE;
+ zdet_param_ptr = &zdet_param[1];
+ d1 = d1_a[1];
+ } else {
+ *zl = z1L/1000;
+ tasha_wcd_mbhc_qfuse_cal(codec, zl, 0);
+ }
+ dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n",
+ __func__, *zl);
+
+ /* start of right impedance ramp and calculation */
+ tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1);
+ if (TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) {
+ if (((z1R > TASHA_ZDET_VAL_1200) &&
+ (zdet_param_ptr->noff == 0x6)) ||
+ ((*zl) != TASHA_ZDET_FLOATING_IMPEDANCE))
+ goto right_ch_impedance;
+ /* second ramp for right ch */
+ if (z1R < TASHA_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1R > TASHA_ZDET_VAL_400) &&
+ (z1R <= TASHA_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1R > TASHA_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1);
+ }
+right_ch_impedance:
+ if ((z1R == TASHA_ZDET_FLOATING_IMPEDANCE) ||
+ (z1R > TASHA_ZDET_VAL_100K)) {
+ *zr = TASHA_ZDET_FLOATING_IMPEDANCE;
+ } else {
+ *zr = z1R/1000;
+ tasha_wcd_mbhc_qfuse_cal(codec, zr, 1);
+ }
+ dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n",
+ __func__, *zr);
+
+ /* mono/stereo detection */
+ if ((*zl == TASHA_ZDET_FLOATING_IMPEDANCE) &&
+ (*zr == TASHA_ZDET_FLOATING_IMPEDANCE)) {
+ dev_dbg(codec->dev,
+ "%s: plug type is invalid or extension cable\n",
+ __func__);
+ goto zdet_complete;
+ }
+ if ((*zl == TASHA_ZDET_FLOATING_IMPEDANCE) ||
+ (*zr == TASHA_ZDET_FLOATING_IMPEDANCE) ||
+ ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) ||
+ ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) {
+ dev_dbg(codec->dev,
+ "%s: Mono plug type with one ch floating or shorted to GND\n",
+ __func__);
+ mbhc->hph_type = WCD_MBHC_HPH_MONO;
+ goto zdet_complete;
+ }
+ snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x01);
+ if (*zl < (TASHA_ZDET_VAL_32/1000))
+ tasha_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1);
+ else
+ tasha_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x00);
+ snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, 0x02, 0x00);
+ z1Ls /= 1000;
+ tasha_wcd_mbhc_qfuse_cal(codec, &z1Ls, 0);
+ /* parallel of left Z and 9 ohm pull down resistor */
+ zMono = ((*zl) * 9) / ((*zl) + 9);
+ z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls);
+ z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl));
+ if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) {
+ dev_dbg(codec->dev, "%s: stereo plug type detected\n",
+ __func__);
+ mbhc->hph_type = WCD_MBHC_HPH_STEREO;
+ } else {
+ dev_dbg(codec->dev, "%s: MONO plug type detected\n",
+ __func__);
+ mbhc->hph_type = WCD_MBHC_HPH_MONO;
+ }
+
+zdet_complete:
+ snd_soc_write(codec, WCD9335_ANA_MBHC_BTN5, reg0);
+ snd_soc_write(codec, WCD9335_ANA_MBHC_BTN6, reg1);
+ snd_soc_write(codec, WCD9335_ANA_MBHC_BTN7, reg2);
+ /* Turn on 100k pull down on HPHL */
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD9335_ANA_MBHC_MECH, 0x01, 0x01);
+
+ /* For NO-jack, re-enable L_DET_EN after Z-det measurements */
+ if (mbhc->hphl_swh)
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD9335_ANA_MBHC_MECH, 0x80, 0x80);
+
+ snd_soc_write(codec, WCD9335_MBHC_ZDET_ANA_CTL, reg4);
+ snd_soc_write(codec, WCD9335_MBHC_CTL_1, reg3);
+ if (is_fsm_disable)
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD9335_ANA_MBHC_ELECT, 0x80, 0x80);
+ if (tasha->zdet_gpio_cb && is_change)
+ tasha->zdet_gpio_cb(codec, false);
+}
+
+static void tasha_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+ if (enable) {
+ snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH,
+ 0x40, 0x40);
+ } else {
+ snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH,
+ 0x40, 0x00);
+ snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH,
+ 0x02, 0x00);
+ }
+}
+
+static void tasha_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec,
+ bool enable)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ if (enable) {
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
+ 0x40, 0x40);
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
+ 0x10, 0x10);
+ } else {
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
+ 0x40, 0x00);
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
+ 0x10, 0x00);
+ }
+}
+
+static void tasha_mbhc_moisture_config(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ if (mbhc->moist_vref == V_OFF)
+ return;
+
+ /* Donot enable moisture detection if jack type is NC */
+ if (!mbhc->hphl_swh) {
+ dev_dbg(codec->dev, "%s: disable moisture detection for NC\n",
+ __func__);
+ return;
+ }
+
+ snd_soc_update_bits(codec, WCD9335_MBHC_CTL_2,
+ 0x0C, mbhc->moist_vref << 2);
+ tasha_mbhc_hph_l_pull_up_control(codec, mbhc->moist_iref);
+}
+
+static void tasha_update_anc_state(struct snd_soc_codec *codec, bool enable,
+ int anc_num)
+{
+ if (enable)
+ snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CFG0 +
+ (20 * anc_num), 0x10, 0x10);
+ else
+ snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CFG0 +
+ (20 * anc_num), 0x10, 0x00);
+}
+
+static bool tasha_is_anc_on(struct wcd_mbhc *mbhc)
+{
+ bool anc_on = false;
+ u16 ancl, ancr;
+
+ ancl =
+ (snd_soc_read(mbhc->codec, WCD9335_CDC_RX1_RX_PATH_CFG0)) & 0x10;
+ ancr =
+ (snd_soc_read(mbhc->codec, WCD9335_CDC_RX2_RX_PATH_CFG0)) & 0x10;
+
+ anc_on = !!(ancl | ancr);
+
+ return anc_on;
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .request_irq = tasha_mbhc_request_irq,
+ .irq_control = tasha_mbhc_irq_control,
+ .free_irq = tasha_mbhc_free_irq,
+ .clk_setup = tasha_mbhc_clk_setup,
+ .map_btn_code_to_num = tasha_mbhc_btn_to_num,
+ .enable_mb_source = tasha_enable_ext_mb_source,
+ .mbhc_bias = tasha_mbhc_mbhc_bias_control,
+ .set_btn_thr = tasha_mbhc_program_btn_thr,
+ .lock_sleep = tasha_mbhc_lock_sleep,
+ .register_notifier = tasha_mbhc_register_notifier,
+ .micbias_enable_status = tasha_mbhc_micb_en_status,
+ .hph_pa_on_status = tasha_mbhc_hph_pa_on_status,
+ .hph_pull_up_control = tasha_mbhc_hph_l_pull_up_control,
+ .mbhc_micbias_control = tasha_mbhc_request_micbias,
+ .mbhc_micb_ramp_control = tasha_mbhc_micb_ramp_control,
+ .get_hwdep_fw_cal = tasha_get_hwdep_fw_cal,
+ .mbhc_micb_ctrl_thr_mic = tasha_mbhc_micb_ctrl_threshold_mic,
+ .compute_impedance = tasha_wcd_mbhc_calc_impedance,
+ .mbhc_gnd_det_ctrl = tasha_mbhc_gnd_det_ctrl,
+ .hph_pull_down_ctrl = tasha_mbhc_hph_pull_down_ctrl,
+ .mbhc_moisture_config = tasha_mbhc_moisture_config,
+ .update_anc_state = tasha_update_anc_state,
+ .is_anc_on = tasha_is_anc_on,
+};
+
+static int tasha_get_anc_slot(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tasha->anc_slot;
+ return 0;
+}
+
+static int tasha_put_anc_slot(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ tasha->anc_slot = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int tasha_get_anc_func(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = (tasha->anc_func == true ? 1 : 0);
+ return 0;
+}
+
+static int tasha_put_anc_func(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ mutex_lock(&tasha->codec_mutex);
+ tasha->anc_func = (!ucontrol->value.integer.value[0] ? false : true);
+
+ dev_dbg(codec->dev, "%s: anc_func %x", __func__, tasha->anc_func);
+
+ if (tasha->anc_func == true) {
+ snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT2 PA");
+ snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT2");
+ snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT1 PA");
+ snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT1");
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHR PA");
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHL PA");
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_enable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_enable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA");
+ snd_soc_dapm_disable_pin(dapm, "LINEOUT2");
+ snd_soc_dapm_disable_pin(dapm, "LINEOUT2 PA");
+ snd_soc_dapm_disable_pin(dapm, "LINEOUT1");
+ snd_soc_dapm_disable_pin(dapm, "LINEOUT1 PA");
+ snd_soc_dapm_disable_pin(dapm, "HPHR");
+ snd_soc_dapm_disable_pin(dapm, "HPHL");
+ snd_soc_dapm_disable_pin(dapm, "HPHR PA");
+ snd_soc_dapm_disable_pin(dapm, "HPHL PA");
+ snd_soc_dapm_disable_pin(dapm, "EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "EAR");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2 PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2");
+ snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1 PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA");
+ snd_soc_dapm_enable_pin(dapm, "LINEOUT2");
+ snd_soc_dapm_enable_pin(dapm, "LINEOUT2 PA");
+ snd_soc_dapm_enable_pin(dapm, "LINEOUT1");
+ snd_soc_dapm_enable_pin(dapm, "LINEOUT1 PA");
+ snd_soc_dapm_enable_pin(dapm, "HPHR");
+ snd_soc_dapm_enable_pin(dapm, "HPHL");
+ snd_soc_dapm_enable_pin(dapm, "HPHR PA");
+ snd_soc_dapm_enable_pin(dapm, "HPHL PA");
+ snd_soc_dapm_enable_pin(dapm, "EAR PA");
+ snd_soc_dapm_enable_pin(dapm, "EAR");
+ }
+ mutex_unlock(&tasha->codec_mutex);
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static int tasha_get_clkmode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = tasha->clk_mode;
+ dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode);
+
+ return 0;
+}
+
+static int tasha_put_clkmode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ tasha->clk_mode = ucontrol->value.enumerated.item[0];
+ dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode);
+
+ return 0;
+}
+
+static int tasha_get_iir_enable_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ /* IIR filter band registers are at integer multiples of 16 */
+ u16 iir_reg = WCD9335_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx;
+
+ ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) &
+ (1 << band_idx)) != 0;
+
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
+ iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int tasha_hph_impedance_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t zl, zr;
+ bool hphr;
+ struct soc_multi_mixer_control *mc;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+ wcd_mbhc_get_impedance(&priv->mbhc, &zl, &zr);
+ dev_dbg(codec->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr);
+ ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+ SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+ tasha_hph_impedance_get, NULL),
+ SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+ tasha_hph_impedance_get, NULL),
+};
+
+static int tasha_get_hph_type(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct wcd_mbhc *mbhc;
+
+ if (!priv) {
+ dev_dbg(codec->dev, "%s: wcd9335 private data is NULL\n",
+ __func__);
+ return 0;
+ }
+
+ mbhc = &priv->mbhc;
+ if (!mbhc) {
+ dev_dbg(codec->dev, "%s: mbhc not initialized\n", __func__);
+ return 0;
+ }
+
+ ucontrol->value.integer.value[0] = (u32) mbhc->hph_type;
+ dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+ SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+ tasha_get_hph_type, NULL),
+};
+
+static int tasha_vi_feed_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tasha_p->vi_feed_value;
+
+ return 0;
+}
+
+static int tasha_vi_feed_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = tasha_p->wcd9xxx;
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n",
+ __func__, enable, port_id, dai_id);
+
+ tasha_p->vi_feed_value = ucontrol->value.integer.value[0];
+
+ mutex_lock(&tasha_p->codec_mutex);
+ if (enable) {
+ if (port_id == TASHA_TX14 && !test_bit(VI_SENSE_1,
+ &tasha_p->status_mask)) {
+ list_add_tail(&core->tx_chs[TASHA_TX14].list,
+ &tasha_p->dai[dai_id].wcd9xxx_ch_list);
+ set_bit(VI_SENSE_1, &tasha_p->status_mask);
+ }
+ if (port_id == TASHA_TX15 && !test_bit(VI_SENSE_2,
+ &tasha_p->status_mask)) {
+ list_add_tail(&core->tx_chs[TASHA_TX15].list,
+ &tasha_p->dai[dai_id].wcd9xxx_ch_list);
+ set_bit(VI_SENSE_2, &tasha_p->status_mask);
+ }
+ } else {
+ if (port_id == TASHA_TX14 && test_bit(VI_SENSE_1,
+ &tasha_p->status_mask)) {
+ list_del_init(&core->tx_chs[TASHA_TX14].list);
+ clear_bit(VI_SENSE_1, &tasha_p->status_mask);
+ }
+ if (port_id == TASHA_TX15 && test_bit(VI_SENSE_2,
+ &tasha_p->status_mask)) {
+ list_del_init(&core->tx_chs[TASHA_TX15].list);
+ clear_bit(VI_SENSE_2, &tasha_p->status_mask);
+ }
+ }
+ mutex_unlock(&tasha_p->codec_mutex);
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL);
+
+ return 0;
+}
+
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tasha_p->tx_port_value;
+ return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct snd_soc_dapm_update *update = NULL;
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+ u32 vtable;
+
+
+ dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n",
+ __func__,
+ widget->name, ucontrol->id.name, tasha_p->tx_port_value,
+ widget->shift, ucontrol->value.integer.value[0]);
+
+ mutex_lock(&tasha_p->codec_mutex);
+
+ if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (dai_id != AIF1_CAP) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ mutex_unlock(&tasha_p->codec_mutex);
+ return -EINVAL;
+ }
+ vtable = vport_slim_check_table[dai_id];
+ } else {
+ if (dai_id >= ARRAY_SIZE(vport_i2s_check_table)) {
+ dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n",
+ __func__, dai_id);
+ mutex_unlock(&tasha_p->codec_mutex);
+ return -EINVAL;
+ }
+ vtable = vport_i2s_check_table[dai_id];
+ }
+ switch (dai_id) {
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ /* only add to the list if value not set */
+ if (enable && !(tasha_p->tx_port_value & 1 << port_id)) {
+
+ if (wcd9xxx_tx_vport_validation(vtable, port_id,
+ tasha_p->dai, NUM_CODEC_DAIS)) {
+ dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
+ __func__, port_id);
+ mutex_unlock(&tasha_p->codec_mutex);
+ return 0;
+ }
+ tasha_p->tx_port_value |= 1 << port_id;
+ list_add_tail(&core->tx_chs[port_id].list,
+ &tasha_p->dai[dai_id].wcd9xxx_ch_list
+ );
+ } else if (!enable && (tasha_p->tx_port_value &
+ 1 << port_id)) {
+ tasha_p->tx_port_value &= ~(1 << port_id);
+ list_del_init(&core->tx_chs[port_id].list);
+ } else {
+ if (enable)
+ dev_dbg(codec->dev, "%s: TX%u port is used by\n"
+ "this virtual port\n",
+ __func__, port_id);
+ else
+ dev_dbg(codec->dev, "%s: TX%u port is not used by\n"
+ "this virtual port\n",
+ __func__, port_id);
+ /* avoid update power function */
+ mutex_unlock(&tasha_p->codec_mutex);
+ return 0;
+ }
+ break;
+ case AIF4_MAD_TX:
+ case AIF5_CPE_TX:
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", dai_id);
+ mutex_unlock(&tasha_p->codec_mutex);
+ return -EINVAL;
+ }
+ pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+ widget->name, widget->sname, tasha_p->tx_port_value,
+ widget->shift);
+
+ mutex_unlock(&tasha_p->codec_mutex);
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
+
+ return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] =
+ tasha_p->rx_port_value[widget->shift];
+ return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB", "AIF_MIX1_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+ unsigned int rx_port_value;
+ u32 port_id = widget->shift;
+
+ tasha_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0];
+ rx_port_value = tasha_p->rx_port_value[port_id];
+
+ pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+ widget->name, ucontrol->id.name, rx_port_value,
+ widget->shift, ucontrol->value.integer.value[0]);
+
+ mutex_lock(&tasha_p->codec_mutex);
+
+ if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ if (rx_port_value > 2) {
+ dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+ __func__);
+ goto err;
+ }
+ }
+ /* value need to match the Virtual port and AIF number */
+ switch (rx_port_value) {
+ case 0:
+ list_del_init(&core->rx_chs[port_id].list);
+ break;
+ case 1:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TASHA_RX_PORT_START_NUMBER,
+ &tasha_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tasha_p->dai[AIF1_PB].wcd9xxx_ch_list);
+ break;
+ case 2:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TASHA_RX_PORT_START_NUMBER,
+ &tasha_p->dai[AIF2_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tasha_p->dai[AIF2_PB].wcd9xxx_ch_list);
+ break;
+ case 3:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TASHA_RX_PORT_START_NUMBER,
+ &tasha_p->dai[AIF3_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tasha_p->dai[AIF3_PB].wcd9xxx_ch_list);
+ break;
+ case 4:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TASHA_RX_PORT_START_NUMBER,
+ &tasha_p->dai[AIF4_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tasha_p->dai[AIF4_PB].wcd9xxx_ch_list);
+ break;
+ case 5:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ TASHA_RX_PORT_START_NUMBER,
+ &tasha_p->dai[AIF_MIX1_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tasha_p->dai[AIF_MIX1_PB].wcd9xxx_ch_list);
+ break;
+ default:
+ pr_err("Unknown AIF %d\n", rx_port_value);
+ goto err;
+ }
+rtn:
+ mutex_unlock(&tasha_p->codec_mutex);
+ snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+ rx_port_value, e, update);
+
+ return 0;
+err:
+ mutex_unlock(&tasha_p->codec_mutex);
+ return -EINVAL;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TASHA_RX_MAX] = {
+ SOC_DAPM_ENUM_EXT("SLIM RX0 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+ SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+ slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif4_vi_mixer[] = {
+ SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, TASHA_TX14, 1, 0,
+ tasha_vi_feed_mixer_get, tasha_vi_feed_mixer_put),
+ SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, TASHA_TX15, 1, 0,
+ tasha_vi_feed_mixer_get, tasha_vi_feed_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif1_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif2_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif3_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif4_mad_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX12", SND_SOC_NOPM, TASHA_TX12, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, 0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+
+};
+
+static const struct snd_kcontrol_new rx_int1_spline_mix_switch[] = {
+ SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int2_spline_mix_switch[] = {
+ SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int3_spline_mix_switch[] = {
+ SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int4_spline_mix_switch[] = {
+ SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int5_spline_mix_switch[] = {
+ SOC_DAPM_SINGLE("LO3 Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int6_spline_mix_switch[] = {
+ SOC_DAPM_SINGLE("LO4 Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int7_spline_mix_switch[] = {
+ SOC_DAPM_SINGLE("SPKRL Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int8_spline_mix_switch[] = {
+ SOC_DAPM_SINGLE("SPKRR Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int5_vbat_mix_switch[] = {
+ SOC_DAPM_SINGLE("LO3 VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int6_vbat_mix_switch[] = {
+ SOC_DAPM_SINGLE("LO4 VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int7_vbat_mix_switch[] = {
+ SOC_DAPM_SINGLE("SPKRL VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int8_vbat_mix_switch[] = {
+ SOC_DAPM_SINGLE("SPKRR VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new cpe_in_mix_switch[] = {
+ SOC_DAPM_SINGLE("MAD_BYPASS", SND_SOC_NOPM, 0, 1, 0)
+};
+
+
+
+static int tasha_put_iir_enable_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ bool iir_band_en_status;
+ int value = ucontrol->value.integer.value[0];
+ u16 iir_reg = WCD9335_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx;
+
+ /* Mask first 5 bits, 6-8 are reserved */
+ snd_soc_update_bits(codec, iir_reg, (1 << band_idx),
+ (value << band_idx));
+
+ iir_band_en_status = ((snd_soc_read(codec, iir_reg) &
+ (1 << band_idx)) != 0);
+ pr_debug("%s: IIR #%d band #%d enable %d\n", __func__,
+ iir_idx, band_idx, iir_band_en_status);
+ return 0;
+}
+
+static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec,
+ int iir_idx, int band_idx,
+ int coeff_idx)
+{
+ uint32_t value = 0;
+
+ /* Address does not automatically update if reading */
+ snd_soc_write(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t)) & 0x7F);
+
+ value |= snd_soc_read(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx));
+
+ snd_soc_write(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 1) & 0x7F);
+
+ value |= (snd_soc_read(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+ 16 * iir_idx)) << 8);
+
+ snd_soc_write(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 2) & 0x7F);
+
+ value |= (snd_soc_read(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+ 16 * iir_idx)) << 16);
+
+ snd_soc_write(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 3) & 0x7F);
+
+ /* Mask bits top 2 bits since they are reserved */
+ value |= ((snd_soc_read(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+ 16 * iir_idx)) & 0x3F) << 24);
+
+ return value;
+}
+
+static int tasha_get_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 0);
+ ucontrol->value.integer.value[1] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 1);
+ ucontrol->value.integer.value[2] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 2);
+ ucontrol->value.integer.value[3] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 3);
+ ucontrol->value.integer.value[4] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 4);
+
+ pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+ "%s: IIR #%d band #%d b1 = 0x%x\n"
+ "%s: IIR #%d band #%d b2 = 0x%x\n"
+ "%s: IIR #%d band #%d a1 = 0x%x\n"
+ "%s: IIR #%d band #%d a2 = 0x%x\n",
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[0],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[1],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[2],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[3],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[4]);
+ return 0;
+}
+
+static void set_iir_band_coeff(struct snd_soc_codec *codec,
+ int iir_idx, int band_idx,
+ uint32_t value)
+{
+ snd_soc_write(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+ (value & 0xFF));
+
+ snd_soc_write(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+ (value >> 8) & 0xFF);
+
+ snd_soc_write(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+ (value >> 16) & 0xFF);
+
+ /* Mask top 2 bits, 7-8 are reserved */
+ snd_soc_write(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+ (value >> 24) & 0x3F);
+}
+
+static void tasha_codec_enable_int_port(struct wcd9xxx_codec_dai_data *dai,
+ struct snd_soc_codec *codec)
+{
+ struct wcd9xxx_ch *ch;
+ int port_num = 0;
+ unsigned short reg = 0;
+ u8 val = 0;
+ struct tasha_priv *tasha_p;
+
+ if (!dai || !codec) {
+ pr_err("%s: Invalid params\n", __func__);
+ return;
+ }
+
+ tasha_p = snd_soc_codec_get_drvdata(codec);
+ list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+ if (ch->port >= TASHA_RX_PORT_START_NUMBER) {
+ port_num = ch->port - TASHA_RX_PORT_START_NUMBER;
+ reg = TASHA_SLIM_PGD_PORT_INT_EN0 + (port_num / 8);
+ val = wcd9xxx_interface_reg_read(tasha_p->wcd9xxx,
+ reg);
+ if (!(val & BYTE_BIT_MASK(port_num))) {
+ val |= BYTE_BIT_MASK(port_num);
+ wcd9xxx_interface_reg_write(
+ tasha_p->wcd9xxx, reg, val);
+ val = wcd9xxx_interface_reg_read(
+ tasha_p->wcd9xxx, reg);
+ }
+ } else {
+ port_num = ch->port;
+ reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8);
+ val = wcd9xxx_interface_reg_read(tasha_p->wcd9xxx,
+ reg);
+ if (!(val & BYTE_BIT_MASK(port_num))) {
+ val |= BYTE_BIT_MASK(port_num);
+ wcd9xxx_interface_reg_write(tasha_p->wcd9xxx,
+ reg, val);
+ val = wcd9xxx_interface_reg_read(
+ tasha_p->wcd9xxx, reg);
+ }
+ }
+ }
+}
+
+static int tasha_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai,
+ bool up)
+{
+ int ret = 0;
+ struct wcd9xxx_ch *ch;
+
+ if (up) {
+ list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+ ret = wcd9xxx_get_slave_port(ch->ch_num);
+ if (ret < 0) {
+ pr_err("%s: Invalid slave port ID: %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ } else {
+ set_bit(ret, &dai->ch_mask);
+ }
+ }
+ } else {
+ ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0),
+ msecs_to_jiffies(
+ TASHA_SLIM_CLOSE_TIMEOUT));
+ if (!ret) {
+ pr_err("%s: Slim close tx/rx wait timeout, ch_mask:0x%lx\n",
+ __func__, dai->ch_mask);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int tasha_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct wcd9xxx *core;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ struct wcd9xxx_codec_dai_data *dai;
+
+ core = dev_get_drvdata(codec->dev->parent);
+
+ dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d\n"
+ "stream name %s event %d\n",
+ __func__, codec->component.name,
+ codec->component.num_dai, w->sname, event);
+
+ /* Execute the callback only if interface type is slimbus */
+ if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ return 0;
+
+ dai = &tasha_p->dai[w->shift];
+ dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n",
+ __func__, w->name, w->shift, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dai->bus_down_in_recovery = false;
+ tasha_codec_enable_int_port(dai, codec);
+ (void) tasha_codec_enable_slim_chmask(dai, true);
+ ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ tasha_codec_vote_max_bw(codec, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n",
+ __func__, ret);
+
+ if (!dai->bus_down_in_recovery)
+ ret = tasha_codec_enable_slim_chmask(dai, false);
+ else
+ dev_dbg(codec->dev,
+ "%s: bus in recovery skip enable slim_chmask",
+ __func__);
+ ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ break;
+ }
+ return ret;
+}
+
+static int tasha_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct wcd9xxx *core = NULL;
+ struct snd_soc_codec *codec = NULL;
+ struct tasha_priv *tasha_p = NULL;
+ int ret = 0;
+ struct wcd9xxx_codec_dai_data *dai = NULL;
+
+ if (!w) {
+ pr_err("%s invalid params\n", __func__);
+ return -EINVAL;
+ }
+ codec = snd_soc_dapm_to_codec(w->dapm);
+ tasha_p = snd_soc_codec_get_drvdata(codec);
+ core = tasha_p->wcd9xxx;
+
+ dev_dbg(codec->dev, "%s: num_dai %d stream name %s\n",
+ __func__, codec->component.num_dai, w->sname);
+
+ /* Execute the callback only if interface type is slimbus */
+ if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ dev_err(codec->dev, "%s Interface is not correct", __func__);
+ return 0;
+ }
+
+ dev_dbg(codec->dev, "%s(): w->name %s event %d w->shift %d\n",
+ __func__, w->name, event, w->shift);
+ if (w->shift != AIF4_VIFEED) {
+ pr_err("%s Error in enabling the tx path\n", __func__);
+ ret = -EINVAL;
+ goto out_vi;
+ }
+ dai = &tasha_p->dai[w->shift];
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (test_bit(VI_SENSE_1, &tasha_p->status_mask)) {
+ dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__);
+ /* Enable V&I sensing */
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F,
+ 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+ 0x00);
+ }
+ if (test_bit(VI_SENSE_2, &tasha_p->status_mask)) {
+ pr_debug("%s: spkr2 enabled\n", __func__);
+ /* Enable V&I sensing */
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F,
+ 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F,
+ 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+ 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+ 0x00);
+ }
+ dai->bus_down_in_recovery = false;
+ tasha_codec_enable_int_port(dai, codec);
+ (void) tasha_codec_enable_slim_chmask(dai, true);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ if (ret)
+ dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n",
+ __func__, ret);
+ if (!dai->bus_down_in_recovery)
+ ret = tasha_codec_enable_slim_chmask(dai, false);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n",
+ __func__, ret);
+ }
+ if (test_bit(VI_SENSE_1, &tasha_p->status_mask)) {
+ /* Disable V&I sensing */
+ dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10,
+ 0x00);
+ }
+ if (test_bit(VI_SENSE_2, &tasha_p->status_mask)) {
+ /* Disable V&I sensing */
+ dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10,
+ 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10,
+ 0x00);
+ }
+ break;
+ }
+out_vi:
+ return ret;
+}
+
+/*
+ * __tasha_codec_enable_slimtx: Enable the slimbus slave port
+ * for TX path
+ * @codec: Handle to the codec for which the slave port is to be
+ * enabled.
+ * @dai_data: The dai specific data for dai which is enabled.
+ */
+static int __tasha_codec_enable_slimtx(struct snd_soc_codec *codec,
+ int event, struct wcd9xxx_codec_dai_data *dai)
+{
+ struct wcd9xxx *core;
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ /* Execute the callback only if interface type is slimbus */
+ if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ return 0;
+
+ dev_dbg(codec->dev,
+ "%s: event = %d\n", __func__, event);
+ core = dev_get_drvdata(codec->dev->parent);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dai->bus_down_in_recovery = false;
+ tasha_codec_enable_int_port(dai, codec);
+ (void) tasha_codec_enable_slim_chmask(dai, true);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ if (!dai->bus_down_in_recovery)
+ ret = tasha_codec_enable_slim_chmask(dai, false);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ pr_debug("%s: Disconnect TX port, ret = %d\n",
+ __func__, ret);
+ }
+
+ break;
+ }
+
+ return ret;
+}
+
+static int tasha_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_codec_dai_data *dai;
+
+ dev_dbg(codec->dev,
+ "%s: w->name %s, w->shift = %d, num_dai %d stream name %s\n",
+ __func__, w->name, w->shift,
+ codec->component.num_dai, w->sname);
+
+ dai = &tasha_p->dai[w->shift];
+ return __tasha_codec_enable_slimtx(codec, event, dai);
+}
+
+static void tasha_codec_cpe_pp_set_cfg(struct snd_soc_codec *codec, int event)
+{
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_codec_dai_data *dai;
+ u8 bit_width, rate, buf_period;
+
+ dai = &tasha_p->dai[AIF4_MAD_TX];
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ switch (dai->bit_width) {
+ case 32:
+ bit_width = 0xF;
+ break;
+ case 24:
+ bit_width = 0xE;
+ break;
+ case 20:
+ bit_width = 0xD;
+ break;
+ case 16:
+ default:
+ bit_width = 0x0;
+ break;
+ }
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x0F,
+ bit_width);
+
+ switch (dai->rate) {
+ case 384000:
+ rate = 0x30;
+ break;
+ case 192000:
+ rate = 0x20;
+ break;
+ case 48000:
+ rate = 0x10;
+ break;
+ case 16000:
+ default:
+ rate = 0x00;
+ break;
+ }
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x70,
+ rate);
+
+ buf_period = (dai->rate * (dai->bit_width/8)) / (16*1000);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD,
+ 0xFF, buf_period);
+ dev_dbg(codec->dev, "%s: PP buffer period= 0x%x\n",
+ __func__, buf_period);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_write(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x3C);
+ snd_soc_write(codec, WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD, 0x60);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * tasha_codec_get_mad_port_id: Callback function that will be invoked
+ * to get the port ID for MAD.
+ * @codec: Handle to the codec
+ * @port_id: cpe port_id needs to enable
+ */
+static int tasha_codec_get_mad_port_id(struct snd_soc_codec *codec,
+ u16 *port_id)
+{
+ struct tasha_priv *tasha_p;
+ struct wcd9xxx_codec_dai_data *dai;
+ struct wcd9xxx_ch *ch;
+
+ if (!port_id || !codec)
+ return -EINVAL;
+
+ tasha_p = snd_soc_codec_get_drvdata(codec);
+ if (!tasha_p)
+ return -EINVAL;
+
+ dai = &tasha_p->dai[AIF4_MAD_TX];
+ list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+ if (ch->port == TASHA_TX12)
+ *port_id = WCD_CPE_AFE_OUT_PORT_2;
+ else if (ch->port == TASHA_TX13)
+ *port_id = WCD_CPE_AFE_OUT_PORT_4;
+ else {
+ dev_err(codec->dev, "%s: invalid mad_port = %d\n",
+ __func__, ch->port);
+ return -EINVAL;
+ }
+ }
+ dev_dbg(codec->dev, "%s: port_id = %d\n", __func__, *port_id);
+
+ return 0;
+}
+
+/*
+ * tasha_codec_enable_slimtx_mad: Callback function that will be invoked
+ * to setup the slave port for MAD.
+ * @codec: Handle to the codec
+ * @event: Indicates whether to enable or disable the slave port
+ */
+static int tasha_codec_enable_slimtx_mad(struct snd_soc_codec *codec,
+ u8 event)
+{
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_codec_dai_data *dai;
+ struct wcd9xxx_ch *ch;
+ int dapm_event = SND_SOC_DAPM_POST_PMU;
+ u16 port = 0;
+ int ret = 0;
+
+ dai = &tasha_p->dai[AIF4_MAD_TX];
+
+ if (event == 0)
+ dapm_event = SND_SOC_DAPM_POST_PMD;
+
+ dev_dbg(codec->dev,
+ "%s: mad_channel, event = 0x%x\n",
+ __func__, event);
+
+ list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+ dev_dbg(codec->dev, "%s: mad_port = %d, event = 0x%x\n",
+ __func__, ch->port, event);
+ if (ch->port == TASHA_TX13) {
+ tasha_codec_cpe_pp_set_cfg(codec, dapm_event);
+ port = TASHA_TX13;
+ break;
+ }
+ }
+
+ ret = __tasha_codec_enable_slimtx(codec, dapm_event, dai);
+
+ if (port == TASHA_TX13) {
+ switch (dapm_event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec,
+ WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN,
+ 0x20, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG,
+ 0x03, 0x02);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG,
+ 0x80, 0x80);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec,
+ WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN,
+ 0x20, 0x20);
+ snd_soc_update_bits(codec,
+ WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG,
+ 0x03, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG,
+ 0x80, 0x00);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int tasha_put_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ /*
+ * Mask top bit it is reserved
+ * Updates addr automatically for each B2 write
+ */
+ snd_soc_write(codec,
+ (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+ (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[0]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[1]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[2]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[3]);
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ ucontrol->value.integer.value[4]);
+
+ pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+ "%s: IIR #%d band #%d b1 = 0x%x\n"
+ "%s: IIR #%d band #%d b2 = 0x%x\n"
+ "%s: IIR #%d band #%d a1 = 0x%x\n"
+ "%s: IIR #%d band #%d a2 = 0x%x\n",
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 0),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 1),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 2),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 3),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 4));
+ return 0;
+}
+
+static int tasha_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int comp = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tasha->comp_enabled[comp];
+ return 0;
+}
+
+static int tasha_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int comp = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: Compander %d enable current %d, new %d\n",
+ __func__, comp + 1, tasha->comp_enabled[comp], value);
+ tasha->comp_enabled[comp] = value;
+
+ /* Any specific register configuration for compander */
+ switch (comp) {
+ case COMPANDER_1:
+ /* Set Gain Source Select based on compander enable/disable */
+ snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0x20,
+ (value ? 0x00:0x20));
+ break;
+ case COMPANDER_2:
+ snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0x20,
+ (value ? 0x00:0x20));
+ break;
+ case COMPANDER_3:
+ break;
+ case COMPANDER_4:
+ break;
+ case COMPANDER_5:
+ snd_soc_update_bits(codec, WCD9335_SE_LO_LO3_GAIN, 0x20,
+ (value ? 0x00:0x20));
+ break;
+ case COMPANDER_6:
+ snd_soc_update_bits(codec, WCD9335_SE_LO_LO4_GAIN, 0x20,
+ (value ? 0x00:0x20));
+ break;
+ case COMPANDER_7:
+ break;
+ case COMPANDER_8:
+ break;
+ default:
+ /*
+ * if compander is not enabled for any interpolator,
+ * it does not cause any audio failure, so do not
+ * return error in this case, but just print a log
+ */
+ dev_warn(codec->dev, "%s: unknown compander: %d\n",
+ __func__, comp);
+ };
+ return 0;
+}
+
+static void tasha_codec_init_flyback(struct snd_soc_codec *codec)
+{
+ snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0xC0, 0x00);
+ snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0xC0, 0x00);
+ snd_soc_update_bits(codec, WCD9335_RX_BIAS_FLYB_BUFF, 0x0F, 0x00);
+ snd_soc_update_bits(codec, WCD9335_RX_BIAS_FLYB_BUFF, 0xF0, 0x00);
+}
+
+static int tasha_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tasha->rx_bias_count++;
+ if (tasha->rx_bias_count == 1) {
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
+ tasha_codec_init_flyback(codec);
+ snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES,
+ 0x01, 0x01);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tasha->rx_bias_count--;
+ if (!tasha->rx_bias_count)
+ snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES,
+ 0x01, 0x00);
+ break;
+ };
+ dev_dbg(codec->dev, "%s: Current RX BIAS user count: %d\n", __func__,
+ tasha->rx_bias_count);
+
+ return 0;
+}
+
+static void tasha_realign_anc_coeff(struct snd_soc_codec *codec,
+ u16 reg1, u16 reg2)
+{
+ u8 val1, val2, tmpval1, tmpval2;
+
+ snd_soc_write(codec, reg1, 0x00);
+ tmpval1 = snd_soc_read(codec, reg2);
+ tmpval2 = snd_soc_read(codec, reg2);
+ snd_soc_write(codec, reg1, 0x00);
+ snd_soc_write(codec, reg2, 0xFF);
+ snd_soc_write(codec, reg1, 0x01);
+ snd_soc_write(codec, reg2, 0xFF);
+
+ snd_soc_write(codec, reg1, 0x00);
+ val1 = snd_soc_read(codec, reg2);
+ val2 = snd_soc_read(codec, reg2);
+
+ if (val1 == 0x0F && val2 == 0xFF) {
+ dev_dbg(codec->dev, "%s: ANC0 co-eff index re-aligned\n",
+ __func__);
+ snd_soc_read(codec, reg2);
+ snd_soc_write(codec, reg1, 0x00);
+ snd_soc_write(codec, reg2, tmpval2);
+ snd_soc_write(codec, reg1, 0x01);
+ snd_soc_write(codec, reg2, tmpval1);
+ } else if (val1 == 0xFF && val2 == 0x0F) {
+ dev_dbg(codec->dev, "%s: ANC1 co-eff index already aligned\n",
+ __func__);
+ snd_soc_write(codec, reg1, 0x00);
+ snd_soc_write(codec, reg2, tmpval1);
+ snd_soc_write(codec, reg1, 0x01);
+ snd_soc_write(codec, reg2, tmpval2);
+ } else {
+ dev_err(codec->dev, "%s: ANC0 co-eff index not aligned\n",
+ __func__);
+ }
+}
+
+static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ const char *filename;
+ const struct firmware *fw;
+ int i;
+ int ret = 0;
+ int num_anc_slots;
+ struct wcd9xxx_anc_header *anc_head;
+ struct firmware_cal *hwdep_cal = NULL;
+ u32 anc_writes_size = 0;
+ u32 anc_cal_size = 0;
+ int anc_size_remaining;
+ u32 *anc_ptr;
+ u16 reg;
+ u8 mask, val;
+ size_t cal_size;
+ const void *data;
+
+ if (!tasha->anc_func)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_ANC_CAL);
+ if (hwdep_cal) {
+ data = hwdep_cal->data;
+ cal_size = hwdep_cal->size;
+ dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+ __func__);
+ } else {
+ filename = "wcd9335/wcd9335_anc.bin";
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to acquire ANC data: %d\n", ret);
+ return -ENODEV;
+ }
+ if (!fw) {
+ dev_err(codec->dev, "failed to get anc fw");
+ return -ENODEV;
+ }
+ data = fw->data;
+ cal_size = fw->size;
+ dev_dbg(codec->dev,
+ "%s: using request_firmware calibration\n", __func__);
+ }
+ if (cal_size < sizeof(struct wcd9xxx_anc_header)) {
+ dev_err(codec->dev, "Not enough data\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ /* First number is the number of register writes */
+ anc_head = (struct wcd9xxx_anc_header *)(data);
+ anc_ptr = (u32 *)(data +
+ sizeof(struct wcd9xxx_anc_header));
+ anc_size_remaining = cal_size -
+ sizeof(struct wcd9xxx_anc_header);
+ num_anc_slots = anc_head->num_anc_slots;
+
+ if (tasha->anc_slot >= num_anc_slots) {
+ dev_err(codec->dev, "Invalid ANC slot selected\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ for (i = 0; i < num_anc_slots; i++) {
+ if (anc_size_remaining < TASHA_PACKED_REG_SIZE) {
+ dev_err(codec->dev,
+ "Invalid register format\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ anc_writes_size = (u32)(*anc_ptr);
+ anc_size_remaining -= sizeof(u32);
+ anc_ptr += 1;
+
+ if (anc_writes_size * TASHA_PACKED_REG_SIZE
+ > anc_size_remaining) {
+ dev_err(codec->dev,
+ "Invalid register format\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (tasha->anc_slot == i)
+ break;
+
+ anc_size_remaining -= (anc_writes_size *
+ TASHA_PACKED_REG_SIZE);
+ anc_ptr += anc_writes_size;
+ }
+ if (i == num_anc_slots) {
+ dev_err(codec->dev, "Selected ANC slot not present\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ i = 0;
+ anc_cal_size = anc_writes_size;
+
+ if (!strcmp(w->name, "RX INT0 DAC") ||
+ !strcmp(w->name, "ANC SPK1 PA"))
+ tasha_realign_anc_coeff(codec,
+ WCD9335_CDC_ANC0_IIR_COEFF_1_CTL,
+ WCD9335_CDC_ANC0_IIR_COEFF_2_CTL);
+
+ if (!strcmp(w->name, "RX INT1 DAC") ||
+ !strcmp(w->name, "RX INT3 DAC")) {
+ tasha_realign_anc_coeff(codec,
+ WCD9335_CDC_ANC0_IIR_COEFF_1_CTL,
+ WCD9335_CDC_ANC0_IIR_COEFF_2_CTL);
+ anc_writes_size = anc_cal_size / 2;
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x39, 0x39);
+ } else if (!strcmp(w->name, "RX INT2 DAC") ||
+ !strcmp(w->name, "RX INT4 DAC")) {
+ tasha_realign_anc_coeff(codec,
+ WCD9335_CDC_ANC1_IIR_COEFF_1_CTL,
+ WCD9335_CDC_ANC1_IIR_COEFF_2_CTL);
+ i = anc_cal_size / 2;
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x39, 0x39);
+ }
+
+ for (; i < anc_writes_size; i++) {
+ TASHA_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val);
+ snd_soc_write(codec, reg, (val & mask));
+ }
+ if (!strcmp(w->name, "RX INT1 DAC") ||
+ !strcmp(w->name, "RX INT3 DAC")) {
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x08, 0x08);
+ } else if (!strcmp(w->name, "RX INT2 DAC") ||
+ !strcmp(w->name, "RX INT4 DAC")) {
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x08, 0x08);
+ }
+
+ if (!hwdep_cal)
+ release_firmware(fw);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Remove ANC Rx from reset */
+ snd_soc_update_bits(codec, WCD9335_CDC_ANC0_CLK_RESET_CTL,
+ 0x08, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CDC_ANC1_CLK_RESET_CTL,
+ 0x08, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!strcmp(w->name, "ANC HPHL PA") ||
+ !strcmp(w->name, "ANC EAR PA") ||
+ !strcmp(w->name, "ANC SPK1 PA") ||
+ !strcmp(w->name, "ANC LINEOUT1 PA")) {
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC0_MODE_1_CTL, 0x30, 0x00);
+ msleep(50);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC0_MODE_1_CTL, 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x38);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x07, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x00);
+ } else if (!strcmp(w->name, "ANC HPHR PA") ||
+ !strcmp(w->name, "ANC LINEOUT2 PA")) {
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC1_MODE_1_CTL, 0x30, 0x00);
+ msleep(50);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC1_MODE_1_CTL, 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x38);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x07, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x00);
+ }
+ break;
+ }
+
+ return 0;
+err:
+ if (!hwdep_cal)
+ release_firmware(fw);
+ return ret;
+}
+
+static void tasha_codec_clear_anc_tx_hold(struct tasha_priv *tasha)
+{
+ if (test_and_clear_bit(ANC_MIC_AMIC1, &tasha->status_mask))
+ tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC1, false);
+ if (test_and_clear_bit(ANC_MIC_AMIC2, &tasha->status_mask))
+ tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC2, false);
+ if (test_and_clear_bit(ANC_MIC_AMIC3, &tasha->status_mask))
+ tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC3, false);
+ if (test_and_clear_bit(ANC_MIC_AMIC4, &tasha->status_mask))
+ tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC4, false);
+ if (test_and_clear_bit(ANC_MIC_AMIC5, &tasha->status_mask))
+ tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC5, false);
+ if (test_and_clear_bit(ANC_MIC_AMIC6, &tasha->status_mask))
+ tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC6, false);
+}
+
+static void tasha_codec_hph_post_pa_config(struct tasha_priv *tasha,
+ int mode, int event)
+{
+ u8 scale_val = 0;
+
+ if (!TASHA_IS_2_0(tasha->wcd9xxx))
+ return;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ switch (mode) {
+ case CLS_H_HIFI:
+ scale_val = 0x3;
+ break;
+ case CLS_H_LOHIFI:
+ scale_val = 0x1;
+ break;
+ }
+ if (tasha->anc_func) {
+ /* Clear Tx FE HOLD if both PAs are enabled */
+ if ((snd_soc_read(tasha->codec, WCD9335_ANA_HPH) &
+ 0xC0) == 0xC0) {
+ tasha_codec_clear_anc_tx_hold(tasha);
+ }
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ scale_val = 0x6;
+ break;
+ }
+
+ if (scale_val)
+ snd_soc_update_bits(tasha->codec, WCD9335_HPH_PA_CTL1, 0x0E,
+ scale_val << 1);
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (tasha->comp_enabled[COMPANDER_1] ||
+ tasha->comp_enabled[COMPANDER_2]) {
+ snd_soc_update_bits(tasha->codec, WCD9335_HPH_L_EN,
+ 0x20, 0x00);
+ snd_soc_update_bits(tasha->codec, WCD9335_HPH_R_EN,
+ 0x20, 0x00);
+ snd_soc_update_bits(tasha->codec, WCD9335_HPH_AUTO_CHOP,
+ 0x20, 0x20);
+ }
+ snd_soc_update_bits(tasha->codec, WCD9335_HPH_L_EN, 0x1F,
+ tasha->hph_l_gain);
+ snd_soc_update_bits(tasha->codec, WCD9335_HPH_R_EN, 0x1F,
+ tasha->hph_r_gain);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(tasha->codec, WCD9335_HPH_AUTO_CHOP, 0x20,
+ 0x00);
+ }
+}
+
+static void tasha_codec_override(struct snd_soc_codec *codec,
+ int mode,
+ int event)
+{
+ if (mode == CLS_AB) {
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!(snd_soc_read(codec,
+ WCD9335_CDC_RX2_RX_PATH_CTL) & 0x10) &&
+ (!(snd_soc_read(codec,
+ WCD9335_CDC_RX1_RX_PATH_CTL) & 0x10)))
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00);
+ break;
+ }
+ }
+}
+
+static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int hph_mode = tasha->hph_mode;
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if ((!(strcmp(w->name, "ANC HPHR PA"))) &&
+ (test_bit(HPH_PA_DELAY, &tasha->status_mask))) {
+ snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0);
+ }
+ set_bit(HPH_PA_DELAY, &tasha->status_mask);
+ if (!(strcmp(w->name, "HPHR PA")))
+ snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x40, 0x40);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (!(strcmp(w->name, "ANC HPHR PA"))) {
+ if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0)
+ != 0xC0)
+ /*
+ * If PA_EN is not set (potentially in ANC case)
+ * then do nothing for POST_PMU and let left
+ * channel handle everything.
+ */
+ break;
+ }
+ /*
+ * 7ms sleep is required after PA is enabled as per
+ * HW requirement
+ */
+ if (test_bit(HPH_PA_DELAY, &tasha->status_mask)) {
+ usleep_range(7000, 7100);
+ clear_bit(HPH_PA_DELAY, &tasha->status_mask);
+ }
+ tasha_codec_hph_post_pa_config(tasha, hph_mode, event);
+ snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_CTL,
+ 0x10, 0x00);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec, WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) &
+ 0x10)
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX2_RX_PATH_MIX_CTL,
+ 0x10, 0x00);
+
+ if (!(strcmp(w->name, "ANC HPHR PA"))) {
+ /* Do everything needed for left channel */
+ snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CTL,
+ 0x10, 0x00);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec,
+ WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) &
+ 0x10)
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX1_RX_PATH_MIX_CTL,
+ 0x10, 0x00);
+ /* Remove ANC Rx from reset */
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ }
+ tasha_codec_override(codec, hph_mode, event);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ blocking_notifier_call_chain(&tasha->notifier,
+ WCD_EVENT_PRE_HPHR_PA_OFF,
+ &tasha->mbhc);
+ tasha_codec_hph_post_pa_config(tasha, hph_mode, event);
+ if (!(strcmp(w->name, "ANC HPHR PA")))
+ snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x40, 0x00);
+ if (!(strcmp(w->name, "HPHR PA")))
+ snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x40, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA is disabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ tasha_codec_override(codec, hph_mode, event);
+ blocking_notifier_call_chain(&tasha->notifier,
+ WCD_EVENT_POST_HPHR_PA_OFF,
+ &tasha->mbhc);
+
+ if (!(strcmp(w->name, "ANC HPHR PA"))) {
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX2_RX_PATH_CFG0, 0x10, 0x00);
+ }
+ break;
+ };
+
+ return ret;
+}
+
+static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int hph_mode = tasha->hph_mode;
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if ((!(strcmp(w->name, "ANC HPHL PA"))) &&
+ (test_bit(HPH_PA_DELAY, &tasha->status_mask))) {
+ snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0);
+ }
+ if (!(strcmp(w->name, "HPHL PA")))
+ snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x80, 0x80);
+ set_bit(HPH_PA_DELAY, &tasha->status_mask);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (!(strcmp(w->name, "ANC HPHL PA"))) {
+ if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0)
+ != 0xC0)
+ /*
+ * If PA_EN is not set (potentially in ANC case)
+ * then do nothing for POST_PMU and let right
+ * channel handle everything.
+ */
+ break;
+ }
+ /*
+ * 7ms sleep is required after PA is enabled as per
+ * HW requirement
+ */
+ if (test_bit(HPH_PA_DELAY, &tasha->status_mask)) {
+ usleep_range(7000, 7100);
+ clear_bit(HPH_PA_DELAY, &tasha->status_mask);
+ }
+
+ tasha_codec_hph_post_pa_config(tasha, hph_mode, event);
+ snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CTL,
+ 0x10, 0x00);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec, WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) &
+ 0x10)
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX1_RX_PATH_MIX_CTL,
+ 0x10, 0x00);
+
+ if (!(strcmp(w->name, "ANC HPHL PA"))) {
+ /* Do everything needed for right channel */
+ snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_CTL,
+ 0x10, 0x00);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec,
+ WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) &
+ 0x10)
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX2_RX_PATH_MIX_CTL,
+ 0x10, 0x00);
+
+ /* Remove ANC Rx from reset */
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ }
+ tasha_codec_override(codec, hph_mode, event);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ blocking_notifier_call_chain(&tasha->notifier,
+ WCD_EVENT_PRE_HPHL_PA_OFF,
+ &tasha->mbhc);
+ tasha_codec_hph_post_pa_config(tasha, hph_mode, event);
+ if (!(strcmp(w->name, "ANC HPHL PA")))
+ snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x80, 0x00);
+ if (!(strcmp(w->name, "HPHL PA")))
+ snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x80, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA is disabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ tasha_codec_override(codec, hph_mode, event);
+ blocking_notifier_call_chain(&tasha->notifier,
+ WCD_EVENT_POST_HPHL_PA_OFF,
+ &tasha->mbhc);
+
+ if (!(strcmp(w->name, "ANC HPHL PA"))) {
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX1_RX_PATH_CFG0, 0x10, 0x00);
+ }
+ break;
+ };
+
+ return ret;
+}
+
+static int tasha_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 lineout_vol_reg = 0, lineout_mix_vol_reg = 0;
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ if (w->reg == WCD9335_ANA_LO_1_2) {
+ if (w->shift == 7) {
+ lineout_vol_reg = WCD9335_CDC_RX3_RX_PATH_CTL;
+ lineout_mix_vol_reg = WCD9335_CDC_RX3_RX_PATH_MIX_CTL;
+ } else if (w->shift == 6) {
+ lineout_vol_reg = WCD9335_CDC_RX4_RX_PATH_CTL;
+ lineout_mix_vol_reg = WCD9335_CDC_RX4_RX_PATH_MIX_CTL;
+ }
+ } else if (w->reg == WCD9335_ANA_LO_3_4) {
+ if (w->shift == 7) {
+ lineout_vol_reg = WCD9335_CDC_RX5_RX_PATH_CTL;
+ lineout_mix_vol_reg = WCD9335_CDC_RX5_RX_PATH_MIX_CTL;
+ } else if (w->shift == 6) {
+ lineout_vol_reg = WCD9335_CDC_RX6_RX_PATH_CTL;
+ lineout_mix_vol_reg = WCD9335_CDC_RX6_RX_PATH_MIX_CTL;
+ }
+ } else {
+ dev_err(codec->dev, "%s: Error enabling lineout PA\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* 5ms sleep is required after PA is enabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ snd_soc_update_bits(codec, lineout_vol_reg,
+ 0x10, 0x00);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec, lineout_mix_vol_reg)) & 0x10)
+ snd_soc_update_bits(codec,
+ lineout_mix_vol_reg,
+ 0x10, 0x00);
+ if (!(strcmp(w->name, "ANC LINEOUT1 PA")) ||
+ !(strcmp(w->name, "ANC LINEOUT2 PA")))
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ tasha_codec_override(codec, CLS_AB, event);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA is disabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ tasha_codec_override(codec, CLS_AB, event);
+ if (!(strcmp(w->name, "ANC LINEOUT1 PA")) ||
+ !(strcmp(w->name, "ANC LINEOUT2 PA"))) {
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ if (!(strcmp(w->name, "ANC LINEOUT1 PA")))
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX3_RX_PATH_CFG0, 0x10, 0x10);
+ else
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX4_RX_PATH_CFG0, 0x10, 0x10);
+ }
+ break;
+ };
+
+ return ret;
+}
+
+static void tasha_spk_anc_update_callback(struct work_struct *work)
+{
+ struct spk_anc_work *spk_anc_dwork;
+ struct tasha_priv *tasha;
+ struct delayed_work *delayed_work;
+ struct snd_soc_codec *codec;
+
+ delayed_work = to_delayed_work(work);
+ spk_anc_dwork = container_of(delayed_work, struct spk_anc_work, dwork);
+ tasha = spk_anc_dwork->tasha;
+ codec = tasha->codec;
+
+ snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0, 0x10, 0x10);
+}
+
+static int tasha_codec_enable_spk_anc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %s %d %d\n", __func__, w->name, event,
+ tasha->anc_func);
+
+ if (!tasha->anc_func)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ schedule_delayed_work(&tasha->spk_anc_dwork.dwork,
+ msecs_to_jiffies(spk_anc_en_delay));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cancel_delayed_work_sync(&tasha->spk_anc_dwork.dwork);
+ snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0,
+ 0x10, 0x00);
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ break;
+ }
+ return ret;
+}
+
+static int tasha_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* 5ms sleep is required after PA is enabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ snd_soc_update_bits(codec, WCD9335_CDC_RX0_RX_PATH_CTL,
+ 0x10, 0x00);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec, WCD9335_CDC_RX0_RX_PATH_MIX_CTL)) &
+ 0x10)
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX0_RX_PATH_MIX_CTL,
+ 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA is disabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+
+ if (!(strcmp(w->name, "ANC EAR PA"))) {
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX0_RX_PATH_CFG0, 0x10, 0x00);
+ }
+ break;
+ };
+
+ return ret;
+}
+
+static void tasha_codec_hph_mode_gain_opt(struct snd_soc_codec *codec,
+ u8 gain)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u8 hph_l_en, hph_r_en;
+ u8 l_val, r_val;
+ u8 hph_pa_status;
+ bool is_hphl_pa, is_hphr_pa;
+
+ hph_pa_status = snd_soc_read(codec, WCD9335_ANA_HPH);
+ is_hphl_pa = hph_pa_status >> 7;
+ is_hphr_pa = (hph_pa_status & 0x40) >> 6;
+
+ hph_l_en = snd_soc_read(codec, WCD9335_HPH_L_EN);
+ hph_r_en = snd_soc_read(codec, WCD9335_HPH_R_EN);
+
+ l_val = (hph_l_en & 0xC0) | 0x20 | gain;
+ r_val = (hph_r_en & 0xC0) | 0x20 | gain;
+
+ /*
+ * Set HPH_L & HPH_R gain source selection to REGISTER
+ * for better click and pop only if corresponding PAs are
+ * not enabled. Also cache the values of the HPHL/R
+ * PA gains to be applied after PAs are enabled
+ */
+ if ((l_val != hph_l_en) && !is_hphl_pa) {
+ snd_soc_write(codec, WCD9335_HPH_L_EN, l_val);
+ tasha->hph_l_gain = hph_l_en & 0x1F;
+ }
+
+ if ((r_val != hph_r_en) && !is_hphr_pa) {
+ snd_soc_write(codec, WCD9335_HPH_R_EN, r_val);
+ tasha->hph_r_gain = hph_r_en & 0x1F;
+ }
+}
+
+static void tasha_codec_hph_lohifi_config(struct snd_soc_codec *codec,
+ int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_PA, 0x0F, 0x06);
+ snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2,
+ 0xF0, 0x40);
+ snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C);
+ tasha_codec_hph_mode_gain_opt(codec, 0x11);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00);
+ snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02);
+ snd_soc_write(codec, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8A);
+ snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_PA, 0x0F, 0x0A);
+ }
+}
+
+static void tasha_codec_hph_lp_config(struct snd_soc_codec *codec,
+ int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C);
+ tasha_codec_hph_mode_gain_opt(codec, 0x10);
+ snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x20, 0x20);
+ snd_soc_update_bits(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x07,
+ 0x01);
+ snd_soc_update_bits(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x70,
+ 0x10);
+ snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO,
+ 0x0F, 0x01);
+ snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO,
+ 0xF0, 0x10);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_write(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO, 0x88);
+ snd_soc_write(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x33);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x20, 0x00);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x04, 0x00);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00);
+ snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02);
+ snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0xC0, 0x80);
+ snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0xC0, 0x80);
+ }
+}
+
+static void tasha_codec_hph_hifi_config(struct snd_soc_codec *codec,
+ int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08);
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C);
+ tasha_codec_hph_mode_gain_opt(codec, 0x11);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00);
+ snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02);
+ }
+}
+
+static void tasha_codec_hph_mode_config(struct snd_soc_codec *codec,
+ int event, int mode)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ if (!TASHA_IS_2_0(tasha->wcd9xxx))
+ return;
+
+ switch (mode) {
+ case CLS_H_LP:
+ tasha_codec_hph_lp_config(codec, event);
+ break;
+ case CLS_H_LOHIFI:
+ tasha_codec_hph_lohifi_config(codec, event);
+ break;
+ case CLS_H_HIFI:
+ tasha_codec_hph_hifi_config(codec, event);
+ break;
+ }
+}
+
+static int tasha_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ int hph_mode = tasha->hph_mode;
+ u8 dem_inp;
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__,
+ w->name, event, hph_mode);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (!(strcmp(w->name, "RX INT2 DAC"))) {
+ snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x20, 0x20);
+ snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x10, 0x10);
+ }
+ if (tasha->anc_func) {
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ /* 40 msec delay is needed to avoid click and pop */
+ msleep(40);
+ }
+
+ /* Read DEM INP Select */
+ dem_inp = snd_soc_read(codec, WCD9335_CDC_RX2_RX_PATH_SEC0) &
+ 0x03;
+ if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+ (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+ dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n",
+ __func__, hph_mode);
+ return -EINVAL;
+ }
+ wcd_clsh_fsm(codec, &tasha->clsh_d,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHR,
+ ((hph_mode == CLS_H_LOHIFI) ?
+ CLS_H_HIFI : hph_mode));
+
+ tasha_codec_hph_mode_config(codec, event, hph_mode);
+
+ if (tasha->anc_func)
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX2_RX_PATH_CFG0, 0x10, 0x10);
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* 1000us required as per HW requirement */
+ usleep_range(1000, 1100);
+ if ((hph_mode == CLS_H_LP) &&
+ (TASHA_IS_1_1(wcd9xxx))) {
+ snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
+ 0x03, 0x03);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (!(strcmp(w->name, "RX INT2 DAC")))
+ snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x30, 0x00);
+ if ((hph_mode == CLS_H_LP) &&
+ (TASHA_IS_1_1(wcd9xxx))) {
+ snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
+ 0x03, 0x00);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 1000us required as per HW requirement */
+ usleep_range(1000, 1100);
+
+ if (!(wcd_clsh_get_clsh_state(&tasha->clsh_d) &
+ WCD_CLSH_STATE_HPHL))
+ tasha_codec_hph_mode_config(codec, event, hph_mode);
+
+ wcd_clsh_fsm(codec, &tasha->clsh_d,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHR,
+ ((hph_mode == CLS_H_LOHIFI) ?
+ CLS_H_HIFI : hph_mode));
+ break;
+ };
+
+ return ret;
+}
+
+static int tasha_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ int hph_mode = tasha->hph_mode;
+ u8 dem_inp;
+ int ret = 0;
+ uint32_t impedl = 0, impedr = 0;
+
+ dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__,
+ w->name, event, hph_mode);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (tasha->anc_func) {
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ /* 40 msec delay is needed to avoid click and pop */
+ msleep(40);
+ }
+
+ /* Read DEM INP Select */
+ dem_inp = snd_soc_read(codec, WCD9335_CDC_RX1_RX_PATH_SEC0) &
+ 0x03;
+ if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+ (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+ dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n",
+ __func__, hph_mode);
+ return -EINVAL;
+ }
+ wcd_clsh_fsm(codec, &tasha->clsh_d,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHL,
+ ((hph_mode == CLS_H_LOHIFI) ?
+ CLS_H_HIFI : hph_mode));
+
+ tasha_codec_hph_mode_config(codec, event, hph_mode);
+
+ if (tasha->anc_func)
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX1_RX_PATH_CFG0, 0x10, 0x10);
+
+ ret = wcd_mbhc_get_impedance(&tasha->mbhc,
+ &impedl, &impedr);
+ if (!ret) {
+ wcd_clsh_imped_config(codec, impedl, false);
+ set_bit(CLASSH_CONFIG, &tasha->status_mask);
+ } else {
+ dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n",
+ __func__, ret);
+ ret = 0;
+ }
+
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* 1000us required as per HW requirement */
+ usleep_range(1000, 1100);
+ if ((hph_mode == CLS_H_LP) &&
+ (TASHA_IS_1_1(wcd9xxx))) {
+ snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
+ 0x03, 0x03);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if ((hph_mode == CLS_H_LP) &&
+ (TASHA_IS_1_1(wcd9xxx))) {
+ snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
+ 0x03, 0x00);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 1000us required as per HW requirement */
+ usleep_range(1000, 1100);
+
+ if (!(wcd_clsh_get_clsh_state(&tasha->clsh_d) &
+ WCD_CLSH_STATE_HPHR))
+ tasha_codec_hph_mode_config(codec, event, hph_mode);
+ wcd_clsh_fsm(codec, &tasha->clsh_d,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHL,
+ ((hph_mode == CLS_H_LOHIFI) ?
+ CLS_H_HIFI : hph_mode));
+
+ if (test_bit(CLASSH_CONFIG, &tasha->status_mask)) {
+ wcd_clsh_imped_config(codec, impedl, true);
+ clear_bit(CLASSH_CONFIG, &tasha->status_mask);
+ } else
+ dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n",
+ __func__, ret);
+
+
+ break;
+ };
+
+ return ret;
+}
+
+static int tasha_codec_lineout_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (tasha->anc_func &&
+ (!strcmp(w->name, "RX INT3 DAC") ||
+ !strcmp(w->name, "RX INT4 DAC")))
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+
+ wcd_clsh_fsm(codec, &tasha->clsh_d,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_LO,
+ CLS_AB);
+
+ if (tasha->anc_func) {
+ if (!strcmp(w->name, "RX INT3 DAC"))
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX3_RX_PATH_CFG0, 0x10, 0x10);
+ else if (!strcmp(w->name, "RX INT4 DAC"))
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX4_RX_PATH_CFG0, 0x10, 0x10);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd_clsh_fsm(codec, &tasha->clsh_d,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_LO,
+ CLS_AB);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tasha_dapm_i2s_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("RX_I2S_CTL", WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TX_I2S_CTL", WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL,
+ 0, 0, NULL, 0),
+};
+
+static int tasha_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (tasha->anc_func)
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+
+ wcd_clsh_fsm(codec, &tasha->clsh_d,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_EAR,
+ CLS_H_NORMAL);
+ if (tasha->anc_func)
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX0_RX_PATH_CFG0, 0x10, 0x10);
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd_clsh_fsm(codec, &tasha->clsh_d,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_EAR,
+ CLS_H_NORMAL);
+ break;
+ };
+
+ return ret;
+}
+
+static int tasha_codec_spk_boost_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 boost_path_ctl, boost_path_cfg1;
+ u16 reg, reg_mix;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ if (!strcmp(w->name, "RX INT7 CHAIN")) {
+ boost_path_ctl = WCD9335_CDC_BOOST0_BOOST_PATH_CTL;
+ boost_path_cfg1 = WCD9335_CDC_RX7_RX_PATH_CFG1;
+ reg = WCD9335_CDC_RX7_RX_PATH_CTL;
+ reg_mix = WCD9335_CDC_RX7_RX_PATH_MIX_CTL;
+ } else if (!strcmp(w->name, "RX INT8 CHAIN")) {
+ boost_path_ctl = WCD9335_CDC_BOOST1_BOOST_PATH_CTL;
+ boost_path_cfg1 = WCD9335_CDC_RX8_RX_PATH_CFG1;
+ reg = WCD9335_CDC_RX8_RX_PATH_CTL;
+ reg_mix = WCD9335_CDC_RX8_RX_PATH_MIX_CTL;
+ } else {
+ dev_err(codec->dev, "%s: unknown widget: %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10);
+ snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01);
+ snd_soc_update_bits(codec, reg, 0x10, 0x00);
+ if ((snd_soc_read(codec, reg_mix)) & 0x10)
+ snd_soc_update_bits(codec, reg_mix, 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00);
+ snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00);
+ break;
+ };
+
+ return 0;
+}
+
+static u16 tasha_interp_get_primary_reg(u16 reg, u16 *ind)
+{
+ u16 prim_int_reg = 0;
+
+ switch (reg) {
+ case WCD9335_CDC_RX0_RX_PATH_CTL:
+ case WCD9335_CDC_RX0_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX0_RX_PATH_CTL;
+ *ind = 0;
+ break;
+ case WCD9335_CDC_RX1_RX_PATH_CTL:
+ case WCD9335_CDC_RX1_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX1_RX_PATH_CTL;
+ *ind = 1;
+ break;
+ case WCD9335_CDC_RX2_RX_PATH_CTL:
+ case WCD9335_CDC_RX2_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX2_RX_PATH_CTL;
+ *ind = 2;
+ break;
+ case WCD9335_CDC_RX3_RX_PATH_CTL:
+ case WCD9335_CDC_RX3_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX3_RX_PATH_CTL;
+ *ind = 3;
+ break;
+ case WCD9335_CDC_RX4_RX_PATH_CTL:
+ case WCD9335_CDC_RX4_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX4_RX_PATH_CTL;
+ *ind = 4;
+ break;
+ case WCD9335_CDC_RX5_RX_PATH_CTL:
+ case WCD9335_CDC_RX5_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX5_RX_PATH_CTL;
+ *ind = 5;
+ break;
+ case WCD9335_CDC_RX6_RX_PATH_CTL:
+ case WCD9335_CDC_RX6_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX6_RX_PATH_CTL;
+ *ind = 6;
+ break;
+ case WCD9335_CDC_RX7_RX_PATH_CTL:
+ case WCD9335_CDC_RX7_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX7_RX_PATH_CTL;
+ *ind = 7;
+ break;
+ case WCD9335_CDC_RX8_RX_PATH_CTL:
+ case WCD9335_CDC_RX8_RX_PATH_MIX_CTL:
+ prim_int_reg = WCD9335_CDC_RX8_RX_PATH_CTL;
+ *ind = 8;
+ break;
+ };
+
+ return prim_int_reg;
+}
+
+static void tasha_codec_hd2_control(struct snd_soc_codec *codec,
+ u16 prim_int_reg, int event)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u16 hd2_scale_reg;
+ u16 hd2_enable_reg = 0;
+
+ if (!TASHA_IS_2_0(tasha->wcd9xxx))
+ return;
+
+ if (prim_int_reg == WCD9335_CDC_RX1_RX_PATH_CTL) {
+ hd2_scale_reg = WCD9335_CDC_RX1_RX_PATH_SEC3;
+ hd2_enable_reg = WCD9335_CDC_RX1_RX_PATH_CFG0;
+ }
+ if (prim_int_reg == WCD9335_CDC_RX2_RX_PATH_CTL) {
+ hd2_scale_reg = WCD9335_CDC_RX2_RX_PATH_SEC3;
+ hd2_enable_reg = WCD9335_CDC_RX2_RX_PATH_CFG0;
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10);
+ snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01);
+ snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04);
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00);
+ snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00);
+ snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00);
+ }
+}
+
+static int tasha_codec_enable_prim_interpolator(
+ struct snd_soc_codec *codec,
+ u16 reg, int event)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u16 prim_int_reg;
+ u16 ind = 0;
+
+ prim_int_reg = tasha_interp_get_primary_reg(reg, &ind);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tasha->prim_int_users[ind]++;
+ if (tasha->prim_int_users[ind] == 1) {
+ snd_soc_update_bits(codec, prim_int_reg,
+ 0x10, 0x10);
+ tasha_codec_hd2_control(codec, prim_int_reg, event);
+ snd_soc_update_bits(codec, prim_int_reg,
+ 1 << 0x5, 1 << 0x5);
+ }
+ if ((reg != prim_int_reg) &&
+ ((snd_soc_read(codec, prim_int_reg)) & 0x10))
+ snd_soc_update_bits(codec, reg, 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tasha->prim_int_users[ind]--;
+ if (tasha->prim_int_users[ind] == 0) {
+ snd_soc_update_bits(codec, prim_int_reg,
+ 1 << 0x5, 0 << 0x5);
+ snd_soc_update_bits(codec, prim_int_reg,
+ 0x40, 0x40);
+ snd_soc_update_bits(codec, prim_int_reg,
+ 0x40, 0x00);
+ tasha_codec_hd2_control(codec, prim_int_reg, event);
+ }
+ break;
+ };
+
+ dev_dbg(codec->dev, "%s: primary interpolator: INT%d, users: %d\n",
+ __func__, ind, tasha->prim_int_users[ind]);
+ return 0;
+}
+
+static int tasha_codec_enable_spline_src(struct snd_soc_codec *codec,
+ int src_num,
+ int event)
+{
+ u16 src_paired_reg = 0;
+ struct tasha_priv *tasha;
+ u16 rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0;
+ u16 rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL;
+ int *src_users, count, spl_src = SPLINE_SRC0;
+ u16 src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0;
+
+ tasha = snd_soc_codec_get_drvdata(codec);
+
+ switch (src_num) {
+ case SRC_IN_HPHL:
+ rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0;
+ src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0;
+ src_paired_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0;
+ rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL;
+ spl_src = SPLINE_SRC0;
+ break;
+ case SRC_IN_LO1:
+ rx_path_cfg_reg = WCD9335_CDC_RX3_RX_PATH_CFG0;
+ src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0;
+ src_paired_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0;
+ rx_path_ctl_reg = WCD9335_CDC_RX3_RX_PATH_CTL;
+ spl_src = SPLINE_SRC0;
+ break;
+ case SRC_IN_HPHR:
+ rx_path_cfg_reg = WCD9335_CDC_RX2_RX_PATH_CFG0;
+ src_clk_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0;
+ src_paired_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0;
+ rx_path_ctl_reg = WCD9335_CDC_RX2_RX_PATH_CTL;
+ spl_src = SPLINE_SRC1;
+ break;
+ case SRC_IN_LO2:
+ rx_path_cfg_reg = WCD9335_CDC_RX4_RX_PATH_CFG0;
+ src_clk_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0;
+ src_paired_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0;
+ rx_path_ctl_reg = WCD9335_CDC_RX4_RX_PATH_CTL;
+ spl_src = SPLINE_SRC1;
+ break;
+ case SRC_IN_SPKRL:
+ rx_path_cfg_reg = WCD9335_CDC_RX7_RX_PATH_CFG0;
+ src_clk_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0;
+ src_paired_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0;
+ rx_path_ctl_reg = WCD9335_CDC_RX7_RX_PATH_CTL;
+ spl_src = SPLINE_SRC2;
+ break;
+ case SRC_IN_LO3:
+ rx_path_cfg_reg = WCD9335_CDC_RX5_RX_PATH_CFG0;
+ src_clk_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0;
+ src_paired_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0;
+ rx_path_ctl_reg = WCD9335_CDC_RX5_RX_PATH_CTL;
+ spl_src = SPLINE_SRC2;
+ break;
+ case SRC_IN_SPKRR:
+ rx_path_cfg_reg = WCD9335_CDC_RX8_RX_PATH_CFG0;
+ src_clk_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0;
+ src_paired_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0;
+ rx_path_ctl_reg = WCD9335_CDC_RX8_RX_PATH_CTL;
+ spl_src = SPLINE_SRC3;
+ break;
+ case SRC_IN_LO4:
+ rx_path_cfg_reg = WCD9335_CDC_RX6_RX_PATH_CFG0;
+ src_clk_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0;
+ src_paired_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0;
+ rx_path_ctl_reg = WCD9335_CDC_RX6_RX_PATH_CTL;
+ spl_src = SPLINE_SRC3;
+ break;
+ };
+
+ src_users = &tasha->spl_src_users[spl_src];
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ count = *src_users;
+ count++;
+ if (count == 1) {
+ if ((snd_soc_read(codec, src_clk_reg) & 0x02) ||
+ (snd_soc_read(codec, src_paired_reg) & 0x02)) {
+ snd_soc_update_bits(codec, src_clk_reg, 0x02,
+ 0x00);
+ snd_soc_update_bits(codec, src_paired_reg,
+ 0x02, 0x00);
+ }
+ snd_soc_update_bits(codec, src_clk_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, rx_path_cfg_reg, 0x80,
+ 0x80);
+ }
+ *src_users = count;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ count = *src_users;
+ count--;
+ if (count == 0) {
+ snd_soc_update_bits(codec, rx_path_cfg_reg, 0x80,
+ 0x00);
+ snd_soc_update_bits(codec, src_clk_reg, 0x03, 0x02);
+ /* default sample rate */
+ snd_soc_update_bits(codec, rx_path_ctl_reg, 0x0f,
+ 0x04);
+ }
+ *src_users = count;
+ break;
+ };
+
+ dev_dbg(codec->dev, "%s: Spline SRC%d, users: %d\n",
+ __func__, spl_src, *src_users);
+ return 0;
+}
+
+static int tasha_codec_enable_spline_resampler(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int ret = 0;
+ u8 src_in;
+
+ src_in = snd_soc_read(codec, WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0);
+ if (!(src_in & 0xFF)) {
+ dev_err(codec->dev, "%s: Spline SRC%u input not selected\n",
+ __func__, w->shift);
+ return -EINVAL;
+ }
+
+ switch (w->shift) {
+ case SPLINE_SRC0:
+ ret = tasha_codec_enable_spline_src(codec,
+ ((src_in & 0x03) == 1) ? SRC_IN_HPHL : SRC_IN_LO1,
+ event);
+ break;
+ case SPLINE_SRC1:
+ ret = tasha_codec_enable_spline_src(codec,
+ ((src_in & 0x0C) == 4) ? SRC_IN_HPHR : SRC_IN_LO2,
+ event);
+ break;
+ case SPLINE_SRC2:
+ ret = tasha_codec_enable_spline_src(codec,
+ ((src_in & 0x30) == 0x10) ? SRC_IN_LO3 : SRC_IN_SPKRL,
+ event);
+ break;
+ case SPLINE_SRC3:
+ ret = tasha_codec_enable_spline_src(codec,
+ ((src_in & 0xC0) == 0x40) ? SRC_IN_LO4 : SRC_IN_SPKRR,
+ event);
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid spline src:%u\n", __func__,
+ w->shift);
+ ret = -EINVAL;
+ };
+
+ return ret;
+}
+
+static int tasha_codec_enable_swr(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha;
+ int i, ch_cnt;
+
+ tasha = snd_soc_codec_get_drvdata(codec);
+
+ if (!tasha->nr)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) &&
+ !tasha->rx_7_count)
+ tasha->rx_7_count++;
+ if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) &&
+ !tasha->rx_8_count)
+ tasha->rx_8_count++;
+ ch_cnt = tasha->rx_7_count + tasha->rx_8_count;
+
+ for (i = 0; i < tasha->nr; i++) {
+ swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev,
+ SWR_DEVICE_UP, NULL);
+ swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev,
+ SWR_SET_NUM_RX_CH, &ch_cnt);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) &&
+ tasha->rx_7_count)
+ tasha->rx_7_count--;
+ if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) &&
+ tasha->rx_8_count)
+ tasha->rx_8_count--;
+ ch_cnt = tasha->rx_7_count + tasha->rx_8_count;
+
+ for (i = 0; i < tasha->nr; i++)
+ swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev,
+ SWR_SET_NUM_RX_CH, &ch_cnt);
+
+ break;
+ }
+ dev_dbg(tasha->dev, "%s: current swr ch cnt: %d\n",
+ __func__, tasha->rx_7_count + tasha->rx_8_count);
+
+ return 0;
+}
+
+static int tasha_codec_config_ear_spkr_gain(struct snd_soc_codec *codec,
+ int event, int gain_reg)
+{
+ int comp_gain_offset, val;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ switch (tasha->spkr_mode) {
+ /* Compander gain in SPKR_MODE1 case is 12 dB */
+ case SPKR_MODE_1:
+ comp_gain_offset = -12;
+ break;
+ /* Default case compander gain is 15 dB */
+ default:
+ comp_gain_offset = -15;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Apply ear spkr gain only if compander is enabled */
+ if (tasha->comp_enabled[COMPANDER_7] &&
+ (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) &&
+ (tasha->ear_spkr_gain != 0)) {
+ /* For example, val is -8(-12+5-1) for 4dB of gain */
+ val = comp_gain_offset + tasha->ear_spkr_gain - 1;
+ snd_soc_write(codec, gain_reg, val);
+
+ dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n",
+ __func__, val);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * Reset RX7 volume to 0 dB if compander is enabled and
+ * ear_spkr_gain is non-zero.
+ */
+ if (tasha->comp_enabled[COMPANDER_7] &&
+ (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) &&
+ (tasha->ear_spkr_gain != 0)) {
+ snd_soc_write(codec, gain_reg, 0x0);
+
+ dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n",
+ __func__);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u16 gain_reg;
+ int offset_val = 0;
+ int val = 0;
+
+ dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
+
+ switch (w->reg) {
+ case WCD9335_CDC_RX0_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX0_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX1_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX1_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX2_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX2_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX3_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX3_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX4_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX4_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX5_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX5_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX6_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX6_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX7_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX7_RX_VOL_MIX_CTL;
+ break;
+ case WCD9335_CDC_RX8_RX_PATH_MIX_CTL:
+ gain_reg = WCD9335_CDC_RX8_RX_VOL_MIX_CTL;
+ break;
+ default:
+ dev_err(codec->dev, "%s: No gain register avail for %s\n",
+ __func__, w->name);
+ return 0;
+ };
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
+ (tasha->comp_enabled[COMPANDER_7] ||
+ tasha->comp_enabled[COMPANDER_8]) &&
+ (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL ||
+ gain_reg == WCD9335_CDC_RX8_RX_VOL_MIX_CTL)) {
+ snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX7_RX_PATH_MIX_SEC0,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX8_RX_PATH_MIX_SEC0,
+ 0x01, 0x01);
+ offset_val = -2;
+ }
+ val = snd_soc_read(codec, gain_reg);
+ val += offset_val;
+ snd_soc_write(codec, gain_reg, val);
+ tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
+ (tasha->comp_enabled[COMPANDER_7] ||
+ tasha->comp_enabled[COMPANDER_8]) &&
+ (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL ||
+ gain_reg == WCD9335_CDC_RX8_RX_VOL_MIX_CTL)) {
+ snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX7_RX_PATH_MIX_SEC0,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX8_RX_PATH_MIX_SEC0,
+ 0x01, 0x00);
+ offset_val = 2;
+ val = snd_soc_read(codec, gain_reg);
+ val += offset_val;
+ snd_soc_write(codec, gain_reg, val);
+ }
+ tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
+ break;
+ };
+
+ return 0;
+}
+
+static int __tasha_cdc_native_clk_enable(struct tasha_priv *tasha,
+ bool enable)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = tasha->codec;
+
+ if (!tasha->wcd_native_clk) {
+ dev_err(tasha->dev, "%s: wcd native clock is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(tasha->dev, "%s: native_clk_enable = %u\n", __func__, enable);
+
+ if (enable) {
+ ret = clk_prepare_enable(tasha->wcd_native_clk);
+ if (ret) {
+ dev_err(tasha->dev, "%s: native clk enable failed\n",
+ __func__);
+ goto err;
+ }
+ if (++tasha->native_clk_users == 1) {
+ snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL,
+ 0x10, 0x10);
+ snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_GATE,
+ 0x04, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x02, 0x02);
+ }
+ } else {
+ if (tasha->native_clk_users &&
+ (--tasha->native_clk_users == 0)) {
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_GATE,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL,
+ 0x80, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL,
+ 0x10, 0x00);
+ }
+ clk_disable_unprepare(tasha->wcd_native_clk);
+ }
+
+ dev_dbg(codec->dev, "%s: native_clk_users: %d\n", __func__,
+ tasha->native_clk_users);
+err:
+ return ret;
+}
+
+static int tasha_codec_get_native_fifo_sync_mask(struct snd_soc_codec *codec,
+ int interp_n)
+{
+ int mask = 0;
+ u16 reg;
+ u8 val1, val2, inp0 = 0;
+ u8 inp1 = 0, inp2 = 0;
+
+ reg = WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0 + (2 * interp_n) - 2;
+
+ val1 = snd_soc_read(codec, reg);
+ val2 = snd_soc_read(codec, reg + 1);
+
+ inp0 = val1 & 0x0F;
+ inp1 = (val1 >> 4) & 0x0F;
+ inp2 = (val2 >> 4) & 0x0F;
+
+ if (IS_VALID_NATIVE_FIFO_PORT(inp0))
+ mask |= (1 << (inp0 - 5));
+ if (IS_VALID_NATIVE_FIFO_PORT(inp1))
+ mask |= (1 << (inp1 - 5));
+ if (IS_VALID_NATIVE_FIFO_PORT(inp2))
+ mask |= (1 << (inp2 - 5));
+
+ dev_dbg(codec->dev, "%s: native fifo mask: 0x%x\n", __func__, mask);
+ if (!mask)
+ dev_err(codec->dev, "native fifo err,int:%d,inp0:%d,inp1:%d,inp2:%d\n",
+ interp_n, inp0, inp1, inp2);
+ return mask;
+}
+
+static int tasha_enable_native_supply(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int mask;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u16 interp_reg;
+
+ dev_dbg(codec->dev, "%s: event: %d, shift:%d\n", __func__, event,
+ w->shift);
+
+ if (w->shift < INTERP_HPHL || w->shift > INTERP_LO2)
+ return -EINVAL;
+
+ interp_reg = WCD9335_CDC_RX1_RX_PATH_CTL + 20 * (w->shift - 1);
+
+ mask = tasha_codec_get_native_fifo_sync_mask(codec, w->shift);
+ if (!mask)
+ return -EINVAL;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Adjust interpolator rate to 44P1_NATIVE */
+ snd_soc_update_bits(codec, interp_reg, 0x0F, 0x09);
+ __tasha_cdc_native_clk_enable(tasha, true);
+ snd_soc_update_bits(codec, WCD9335_DATA_HUB_NATIVE_FIFO_SYNC,
+ mask, mask);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, WCD9335_DATA_HUB_NATIVE_FIFO_SYNC,
+ mask, 0x0);
+ __tasha_cdc_native_clk_enable(tasha, false);
+ /* Adjust interpolator rate to default */
+ snd_soc_update_bits(codec, interp_reg, 0x0F, 0x04);
+ break;
+ }
+
+ return 0;
+}
+
+static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u16 gain_reg;
+ u16 reg;
+ int val;
+ int offset_val = 0;
+
+ dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
+
+ if (!(strcmp(w->name, "RX INT0 INTERP"))) {
+ reg = WCD9335_CDC_RX0_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX0_RX_VOL_CTL;
+ } else if (!(strcmp(w->name, "RX INT1 INTERP"))) {
+ reg = WCD9335_CDC_RX1_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX1_RX_VOL_CTL;
+ } else if (!(strcmp(w->name, "RX INT2 INTERP"))) {
+ reg = WCD9335_CDC_RX2_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX2_RX_VOL_CTL;
+ } else if (!(strcmp(w->name, "RX INT3 INTERP"))) {
+ reg = WCD9335_CDC_RX3_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX3_RX_VOL_CTL;
+ } else if (!(strcmp(w->name, "RX INT4 INTERP"))) {
+ reg = WCD9335_CDC_RX4_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX4_RX_VOL_CTL;
+ } else if (!(strcmp(w->name, "RX INT5 INTERP"))) {
+ reg = WCD9335_CDC_RX5_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX5_RX_VOL_CTL;
+ } else if (!(strcmp(w->name, "RX INT6 INTERP"))) {
+ reg = WCD9335_CDC_RX6_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX6_RX_VOL_CTL;
+ } else if (!(strcmp(w->name, "RX INT7 INTERP"))) {
+ reg = WCD9335_CDC_RX7_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX7_RX_VOL_CTL;
+ } else if (!(strcmp(w->name, "RX INT8 INTERP"))) {
+ reg = WCD9335_CDC_RX8_RX_PATH_CTL;
+ gain_reg = WCD9335_CDC_RX8_RX_VOL_CTL;
+ } else {
+ dev_err(codec->dev, "%s: Interpolator reg not found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tasha_codec_vote_max_bw(codec, true);
+ /* Reset if needed */
+ tasha_codec_enable_prim_interpolator(codec, reg, event);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ tasha_config_compander(codec, w->shift, event);
+ /* apply gain after int clk is enabled */
+ if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
+ (tasha->comp_enabled[COMPANDER_7] ||
+ tasha->comp_enabled[COMPANDER_8]) &&
+ (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD9335_CDC_RX8_RX_VOL_CTL)) {
+ snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX7_RX_PATH_MIX_SEC0,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX8_RX_PATH_MIX_SEC0,
+ 0x01, 0x01);
+ offset_val = -2;
+ }
+ val = snd_soc_read(codec, gain_reg);
+ val += offset_val;
+ snd_soc_write(codec, gain_reg, val);
+ tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tasha_config_compander(codec, w->shift, event);
+ tasha_codec_enable_prim_interpolator(codec, reg, event);
+ if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
+ (tasha->comp_enabled[COMPANDER_7] ||
+ tasha->comp_enabled[COMPANDER_8]) &&
+ (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD9335_CDC_RX8_RX_VOL_CTL)) {
+ snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX7_RX_PATH_MIX_SEC0,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_RX8_RX_PATH_MIX_SEC0,
+ 0x01, 0x00);
+ offset_val = 2;
+ val = snd_soc_read(codec, gain_reg);
+ val += offset_val;
+ snd_soc_write(codec, gain_reg, val);
+ }
+ tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
+ break;
+ };
+
+ return 0;
+}
+
+static int tasha_codec_set_iir_gain(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ dev_dbg(codec->dev, "%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU: /* fall through */
+ case SND_SOC_DAPM_PRE_PMD:
+ if (strnstr(w->name, "IIR0", sizeof("IIR0"))) {
+ snd_soc_write(codec,
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL,
+ snd_soc_read(codec,
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL));
+ snd_soc_write(codec,
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL,
+ snd_soc_read(codec,
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL));
+ snd_soc_write(codec,
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL,
+ snd_soc_read(codec,
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL));
+ snd_soc_write(codec,
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL,
+ snd_soc_read(codec,
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL));
+ } else {
+ snd_soc_write(codec,
+ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL,
+ snd_soc_read(codec,
+ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL));
+ snd_soc_write(codec,
+ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL,
+ snd_soc_read(codec,
+ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL));
+ snd_soc_write(codec,
+ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL,
+ snd_soc_read(codec,
+ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL));
+ }
+ break;
+ }
+ return 0;
+}
+
+static int tasha_codec_enable_on_demand_supply(
+ struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct on_demand_supply *supply;
+
+ if (w->shift >= ON_DEMAND_SUPPLIES_MAX) {
+ dev_err(codec->dev, "%s: error index > MAX Demand supplies",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dev_dbg(codec->dev, "%s: supply: %s event: %d\n",
+ __func__, on_demand_supply_name[w->shift], event);
+
+ supply = &tasha->on_demand_list[w->shift];
+ WARN_ONCE(!supply->supply, "%s isn't defined\n",
+ on_demand_supply_name[w->shift]);
+ if (!supply->supply) {
+ dev_err(codec->dev, "%s: err supply not present ond for %d",
+ __func__, w->shift);
+ goto out;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = regulator_enable(supply->supply);
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to enable %s\n",
+ __func__,
+ on_demand_supply_name[w->shift]);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = regulator_disable(supply->supply);
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to disable %s\n",
+ __func__,
+ on_demand_supply_name[w->shift]);
+ break;
+ default:
+ break;
+ };
+
+out:
+ return ret;
+}
+
+static int tasha_codec_find_amic_input(struct snd_soc_codec *codec,
+ int adc_mux_n)
+{
+ u16 mask, shift, adc_mux_in_reg;
+ u16 amic_mux_sel_reg;
+ bool is_amic;
+
+ if (adc_mux_n < 0 || adc_mux_n > WCD9335_MAX_VALID_ADC_MUX ||
+ adc_mux_n == WCD9335_INVALID_ADC_MUX)
+ return 0;
+
+ /* Check whether adc mux input is AMIC or DMIC */
+ if (adc_mux_n < 4) {
+ adc_mux_in_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 +
+ 2 * adc_mux_n;
+ amic_mux_sel_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+ 2 * adc_mux_n;
+ mask = 0x03;
+ shift = 0;
+ } else {
+ adc_mux_in_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_n - 4;
+ amic_mux_sel_reg = adc_mux_in_reg;
+ mask = 0xC0;
+ shift = 6;
+ }
+ is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask) >> shift)
+ == 1);
+ if (!is_amic)
+ return 0;
+
+ return snd_soc_read(codec, amic_mux_sel_reg) & 0x07;
+}
+
+static void tasha_codec_set_tx_hold(struct snd_soc_codec *codec,
+ u16 amic_reg, bool set)
+{
+ u8 mask = 0x20;
+ u8 val;
+
+ if (amic_reg == WCD9335_ANA_AMIC1 ||
+ amic_reg == WCD9335_ANA_AMIC3 ||
+ amic_reg == WCD9335_ANA_AMIC5)
+ mask = 0x40;
+
+ val = set ? mask : 0x00;
+
+ switch (amic_reg) {
+ case WCD9335_ANA_AMIC1:
+ case WCD9335_ANA_AMIC2:
+ snd_soc_update_bits(codec, WCD9335_ANA_AMIC2, mask, val);
+ break;
+ case WCD9335_ANA_AMIC3:
+ case WCD9335_ANA_AMIC4:
+ snd_soc_update_bits(codec, WCD9335_ANA_AMIC4, mask, val);
+ break;
+ case WCD9335_ANA_AMIC5:
+ case WCD9335_ANA_AMIC6:
+ snd_soc_update_bits(codec, WCD9335_ANA_AMIC6, mask, val);
+ break;
+ default:
+ dev_dbg(codec->dev, "%s: invalid amic: %d\n",
+ __func__, amic_reg);
+ break;
+ }
+}
+
+static int tasha_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int adc_mux_n = w->shift;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int amic_n;
+
+ dev_dbg(codec->dev, "%s: event: %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ amic_n = tasha_codec_find_amic_input(codec, adc_mux_n);
+ if (amic_n) {
+ /*
+ * Prevent ANC Rx pop by leaving Tx FE in HOLD
+ * state until PA is up. Track AMIC being used
+ * so we can release the HOLD later.
+ */
+ set_bit(ANC_MIC_AMIC1 + amic_n - 1,
+ &tasha->status_mask);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static u16 tasha_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, int amic)
+{
+ u16 pwr_level_reg = 0;
+
+ switch (amic) {
+ case 1:
+ case 2:
+ pwr_level_reg = WCD9335_ANA_AMIC1;
+ break;
+
+ case 3:
+ case 4:
+ pwr_level_reg = WCD9335_ANA_AMIC3;
+ break;
+
+ case 5:
+ case 6:
+ pwr_level_reg = WCD9335_ANA_AMIC5;
+ break;
+ default:
+ dev_dbg(codec->dev, "%s: invalid amic: %d\n",
+ __func__, amic);
+ break;
+ }
+
+ return pwr_level_reg;
+}
+
+#define TX_HPF_CUT_OFF_FREQ_MASK 0x60
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+
+static void tasha_tx_hpf_corner_freq_callback(struct work_struct *work)
+{
+ struct delayed_work *hpf_delayed_work;
+ struct hpf_work *hpf_work;
+ struct tasha_priv *tasha;
+ struct snd_soc_codec *codec;
+ u16 dec_cfg_reg, amic_reg;
+ u8 hpf_cut_off_freq;
+ int amic_n;
+
+ hpf_delayed_work = to_delayed_work(work);
+ hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
+ tasha = hpf_work->tasha;
+ codec = tasha->codec;
+ hpf_cut_off_freq = hpf_work->hpf_cut_off_freq;
+
+ dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator;
+
+ dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n",
+ __func__, hpf_work->decimator, hpf_cut_off_freq);
+
+ amic_n = tasha_codec_find_amic_input(codec, hpf_work->decimator);
+ if (amic_n) {
+ amic_reg = WCD9335_ANA_AMIC1 + amic_n - 1;
+ tasha_codec_set_tx_hold(codec, amic_reg, false);
+ }
+ tasha_codec_vote_max_bw(codec, true);
+ snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK,
+ hpf_cut_off_freq << 5);
+ tasha_codec_vote_max_bw(codec, false);
+}
+
+static void tasha_tx_mute_update_callback(struct work_struct *work)
+{
+ struct tx_mute_work *tx_mute_dwork;
+ struct tasha_priv *tasha;
+ struct delayed_work *delayed_work;
+ struct snd_soc_codec *codec;
+ u16 tx_vol_ctl_reg, hpf_gate_reg;
+
+ delayed_work = to_delayed_work(work);
+ tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork);
+ tasha = tx_mute_dwork->tasha;
+ codec = tasha->codec;
+
+ tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL +
+ 16 * tx_mute_dwork->decimator;
+ hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 +
+ 16 * tx_mute_dwork->decimator;
+ snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00);
+}
+
+static int tasha_codec_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int decimator;
+ char *dec_adc_mux_name = NULL;
+ char *widget_name = NULL;
+ char *wname;
+ int ret = 0, amic_n;
+ u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg;
+ u16 tx_gain_ctl_reg;
+ char *dec;
+ u8 hpf_cut_off_freq;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %d\n", __func__, event);
+
+ widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+ if (!widget_name)
+ return -ENOMEM;
+
+ wname = widget_name;
+ dec_adc_mux_name = strsep(&widget_name, " ");
+ if (!dec_adc_mux_name) {
+ dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+ __func__, w->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ dec_adc_mux_name = widget_name;
+
+ dec = strpbrk(dec_adc_mux_name, "012345678");
+ if (!dec) {
+ dev_err(codec->dev, "%s: decimator index not found\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = kstrtouint(dec, 10, &decimator);
+ if (ret < 0) {
+ dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+ __func__, wname);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__,
+ w->name, decimator);
+
+ tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + 16 * decimator;
+ hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 + 16 * decimator;
+ dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * decimator;
+ tx_gain_ctl_reg = WCD9335_CDC_TX0_TX_VOL_CTL + 16 * decimator;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ amic_n = tasha_codec_find_amic_input(codec, decimator);
+ if (amic_n)
+ pwr_level_reg = tasha_codec_get_amic_pwlvl_reg(codec,
+ amic_n);
+
+ if (pwr_level_reg) {
+ switch ((snd_soc_read(codec, pwr_level_reg) &
+ WCD9335_AMIC_PWR_LVL_MASK) >>
+ WCD9335_AMIC_PWR_LVL_SHIFT) {
+ case WCD9335_AMIC_PWR_LEVEL_LP:
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ WCD9335_DEC_PWR_LVL_MASK,
+ WCD9335_DEC_PWR_LVL_LP);
+ break;
+
+ case WCD9335_AMIC_PWR_LEVEL_HP:
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ WCD9335_DEC_PWR_LVL_MASK,
+ WCD9335_DEC_PWR_LVL_HP);
+ break;
+ case WCD9335_AMIC_PWR_LEVEL_DEFAULT:
+ default:
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ WCD9335_DEC_PWR_LVL_MASK,
+ WCD9335_DEC_PWR_LVL_DF);
+ break;
+ }
+ }
+ hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) &
+ TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
+ tasha->tx_hpf_work[decimator].hpf_cut_off_freq =
+ hpf_cut_off_freq;
+
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ)
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ CF_MIN_3DB_150HZ << 5);
+ /* Enable TX PGA Mute */
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x00);
+
+ if (decimator == 0) {
+ snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x83);
+ snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0xA3);
+ snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x83);
+ snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x03);
+ }
+ /* schedule work queue to Remove Mute */
+ schedule_delayed_work(&tasha->tx_mute_dwork[decimator].dwork,
+ msecs_to_jiffies(tx_unmute_delay));
+ if (tasha->tx_hpf_work[decimator].hpf_cut_off_freq !=
+ CF_MIN_3DB_150HZ)
+ schedule_delayed_work(
+ &tasha->tx_hpf_work[decimator].dwork,
+ msecs_to_jiffies(300));
+ /* apply gain after decimator is enabled */
+ snd_soc_write(codec, tx_gain_ctl_reg,
+ snd_soc_read(codec, tx_gain_ctl_reg));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ hpf_cut_off_freq =
+ tasha->tx_hpf_work[decimator].hpf_cut_off_freq;
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
+ if (cancel_delayed_work_sync(
+ &tasha->tx_hpf_work[decimator].dwork)) {
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+ tasha_codec_vote_max_bw(codec, true);
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ hpf_cut_off_freq << 5);
+ tasha_codec_vote_max_bw(codec, false);
+ }
+ }
+ cancel_delayed_work_sync(
+ &tasha->tx_mute_dwork[decimator].dwork);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00);
+ break;
+ };
+out:
+ kfree(wname);
+ return ret;
+}
+
+static u32 tasha_get_dmic_sample_rate(struct snd_soc_codec *codec,
+ unsigned int dmic, struct wcd9xxx_pdata *pdata)
+{
+ u8 tx_stream_fs;
+ u8 adc_mux_index = 0, adc_mux_sel = 0;
+ bool dec_found = false;
+ u16 adc_mux_ctl_reg, tx_fs_reg;
+ u32 dmic_fs;
+
+ while (dec_found == 0 && adc_mux_index < WCD9335_MAX_VALID_ADC_MUX) {
+ if (adc_mux_index < 4) {
+ adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+ (adc_mux_index * 2);
+ adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) &
+ 0x78) >> 3) - 1;
+ } else if (adc_mux_index < 9) {
+ adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ ((adc_mux_index - 4) * 1);
+ adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) &
+ 0x38) >> 3) - 1;
+ } else if (adc_mux_index == 9) {
+ ++adc_mux_index;
+ continue;
+ }
+ if (adc_mux_sel == dmic)
+ dec_found = true;
+ else
+ ++adc_mux_index;
+ }
+
+ if (dec_found == true && adc_mux_index <= 8) {
+ tx_fs_reg = WCD9335_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index);
+ tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F;
+ dmic_fs = tx_stream_fs <= 4 ? WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ :
+ WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+
+ /*
+ * Check for ECPP path selection and DEC1 not connected to
+ * any other audio path to apply ECPP DMIC sample rate
+ */
+ if ((adc_mux_index == 1) &&
+ ((snd_soc_read(codec, WCD9335_CPE_SS_US_EC_MUX_CFG)
+ & 0x0F) == 0x0A) &&
+ ((snd_soc_read(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0)
+ & 0x0C) == 0x00)) {
+ dmic_fs = pdata->ecpp_dmic_sample_rate;
+ }
+ } else {
+ dmic_fs = pdata->dmic_sample_rate;
+ }
+
+ return dmic_fs;
+}
+
+static u8 tasha_get_dmic_clk_val(struct snd_soc_codec *codec,
+ u32 mclk_rate, u32 dmic_clk_rate)
+{
+ u32 div_factor;
+ u8 dmic_ctl_val;
+
+ dev_dbg(codec->dev,
+ "%s: mclk_rate = %d, dmic_sample_rate = %d\n",
+ __func__, mclk_rate, dmic_clk_rate);
+
+ /* Default value to return in case of error */
+ if (mclk_rate == TASHA_MCLK_CLK_9P6MHZ)
+ dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2;
+ else
+ dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3;
+
+ if (dmic_clk_rate == 0) {
+ dev_err(codec->dev,
+ "%s: dmic_sample_rate cannot be 0\n",
+ __func__);
+ goto done;
+ }
+
+ div_factor = mclk_rate / dmic_clk_rate;
+ switch (div_factor) {
+ case 2:
+ dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2;
+ break;
+ case 3:
+ dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3;
+ break;
+ case 4:
+ dmic_ctl_val = WCD9335_DMIC_CLK_DIV_4;
+ break;
+ case 6:
+ dmic_ctl_val = WCD9335_DMIC_CLK_DIV_6;
+ break;
+ case 8:
+ dmic_ctl_val = WCD9335_DMIC_CLK_DIV_8;
+ break;
+ case 16:
+ dmic_ctl_val = WCD9335_DMIC_CLK_DIV_16;
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n",
+ __func__, div_factor, mclk_rate, dmic_clk_rate);
+ break;
+ }
+
+done:
+ return dmic_ctl_val;
+}
+
+static int tasha_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ dev_dbg(codec->dev, "%s: event:%d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tasha_codec_set_tx_hold(codec, w->reg, true);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int tasha_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+ u8 dmic_clk_en = 0x01;
+ u16 dmic_clk_reg;
+ s32 *dmic_clk_cnt;
+ u8 dmic_rate_val, dmic_rate_shift = 1;
+ unsigned int dmic;
+ u32 dmic_sample_rate;
+ int ret;
+ char *wname;
+
+ wname = strpbrk(w->name, "012345");
+ if (!wname) {
+ dev_err(codec->dev, "%s: widget not found\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(wname, 10, &dmic);
+ if (ret < 0) {
+ dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (dmic) {
+ case 0:
+ case 1:
+ dmic_clk_cnt = &(tasha->dmic_0_1_clk_cnt);
+ dmic_clk_reg = WCD9335_CPE_SS_DMIC0_CTL;
+ break;
+ case 2:
+ case 3:
+ dmic_clk_cnt = &(tasha->dmic_2_3_clk_cnt);
+ dmic_clk_reg = WCD9335_CPE_SS_DMIC1_CTL;
+ break;
+ case 4:
+ case 5:
+ dmic_clk_cnt = &(tasha->dmic_4_5_clk_cnt);
+ dmic_clk_reg = WCD9335_CPE_SS_DMIC2_CTL;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid DMIC Selection\n",
+ __func__);
+ return -EINVAL;
+ };
+ dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n",
+ __func__, event, dmic, *dmic_clk_cnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dmic_sample_rate = tasha_get_dmic_sample_rate(codec, dmic,
+ pdata);
+ dmic_rate_val =
+ tasha_get_dmic_clk_val(codec,
+ pdata->mclk_rate,
+ dmic_sample_rate);
+
+ (*dmic_clk_cnt)++;
+ if (*dmic_clk_cnt == 1) {
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ 0x07 << dmic_rate_shift,
+ dmic_rate_val << dmic_rate_shift);
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ dmic_clk_en, dmic_clk_en);
+ }
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dmic_rate_val =
+ tasha_get_dmic_clk_val(codec,
+ pdata->mclk_rate,
+ pdata->mad_dmic_sample_rate);
+ (*dmic_clk_cnt)--;
+ if (*dmic_clk_cnt == 0) {
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ dmic_clk_en, 0);
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ 0x07 << dmic_rate_shift,
+ dmic_rate_val << dmic_rate_shift);
+ }
+ break;
+ };
+
+ return 0;
+}
+
+static int __tasha_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int micb_num;
+
+ dev_dbg(codec->dev, "%s: wname: %s, event: %d\n",
+ __func__, w->name, event);
+
+ if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1")))
+ micb_num = MIC_BIAS_1;
+ else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2")))
+ micb_num = MIC_BIAS_2;
+ else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3")))
+ micb_num = MIC_BIAS_3;
+ else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4")))
+ micb_num = MIC_BIAS_4;
+ else
+ return -EINVAL;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /*
+ * MIC BIAS can also be requested by MBHC,
+ * so use ref count to handle micbias pullup
+ * and enable requests
+ */
+ tasha_micbias_control(codec, micb_num, MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* wait for cnp time */
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tasha_micbias_control(codec, micb_num, MICB_DISABLE, true);
+ break;
+ };
+
+ return 0;
+}
+
+static int tasha_codec_ldo_h_control(struct snd_soc_dapm_widget *w,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ tasha->ldo_h_users++;
+
+ if (tasha->ldo_h_users == 1)
+ snd_soc_update_bits(codec, WCD9335_LDOH_MODE,
+ 0x80, 0x80);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ tasha->ldo_h_users--;
+
+ if (tasha->ldo_h_users < 0)
+ tasha->ldo_h_users = 0;
+
+ if (tasha->ldo_h_users == 0)
+ snd_soc_update_bits(codec, WCD9335_LDOH_MODE,
+ 0x80, 0x00);
+ }
+
+ return 0;
+}
+
+static int tasha_codec_force_enable_ldo_h(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd_resmgr_enable_master_bias(tasha->resmgr);
+ tasha_codec_ldo_h_control(w, event);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tasha_codec_ldo_h_control(w, event);
+ wcd_resmgr_disable_master_bias(tasha->resmgr);
+ break;
+ }
+
+ return 0;
+}
+
+static int tasha_codec_force_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd_resmgr_enable_master_bias(tasha->resmgr);
+ tasha_cdc_mclk_enable(codec, true, true);
+ ret = __tasha_codec_enable_micbias(w, SND_SOC_DAPM_PRE_PMU);
+ /* Wait for 1ms for better cnp */
+ usleep_range(1000, 1100);
+ tasha_cdc_mclk_enable(codec, false, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = __tasha_codec_enable_micbias(w, SND_SOC_DAPM_POST_PMD);
+ wcd_resmgr_disable_master_bias(tasha->resmgr);
+ break;
+ }
+
+ return ret;
+}
+
+static int tasha_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return __tasha_codec_enable_micbias(w, event);
+}
+
+static int tasha_codec_enable_standalone_ldo_h(struct snd_soc_codec *codec,
+ bool enable)
+{
+ int rc;
+
+ if (enable)
+ rc = snd_soc_dapm_force_enable_pin(
+ snd_soc_codec_get_dapm(codec),
+ DAPM_LDO_H_STANDALONE);
+ else
+ rc = snd_soc_dapm_disable_pin(
+ snd_soc_codec_get_dapm(codec),
+ DAPM_LDO_H_STANDALONE);
+
+ if (!rc)
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+ else
+ dev_err(codec->dev, "%s: ldo_h force %s pin failed\n",
+ __func__, (enable ? "enable" : "disable"));
+
+ return rc;
+}
+
+/*
+ * tasha_codec_enable_standalone_micbias - enable micbias standalone
+ * @codec: pointer to codec instance
+ * @micb_num: number of micbias to be enabled
+ * @enable: true to enable micbias or false to disable
+ *
+ * This function is used to enable micbias (1, 2, 3 or 4) during
+ * standalone independent of whether TX use-case is running or not
+ *
+ * Return: error code in case of failure or 0 for success
+ */
+int tasha_codec_enable_standalone_micbias(struct snd_soc_codec *codec,
+ int micb_num,
+ bool enable)
+{
+ const char * const micb_names[] = {
+ DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE,
+ DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE
+ };
+ int micb_index = micb_num - 1;
+ int rc;
+
+ if (!codec) {
+ pr_err("%s: Codec memory is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((micb_index < 0) || (micb_index > TASHA_MAX_MICBIAS - 1)) {
+ dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+ __func__, micb_index);
+ return -EINVAL;
+ }
+
+ if (enable)
+ rc = snd_soc_dapm_force_enable_pin(
+ snd_soc_codec_get_dapm(codec),
+ micb_names[micb_index]);
+ else
+ rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec),
+ micb_names[micb_index]);
+
+ if (!rc)
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+ else
+ dev_err(codec->dev, "%s: micbias%d force %s pin failed\n",
+ __func__, micb_num, (enable ? "enable" : "disable"));
+
+ return rc;
+}
+EXPORT_SYMBOL(tasha_codec_enable_standalone_micbias);
+
+static const char *const tasha_anc_func_text[] = {"OFF", "ON"};
+static const struct soc_enum tasha_anc_func_enum =
+ SOC_ENUM_SINGLE_EXT(2, tasha_anc_func_text);
+
+static const char *const tasha_clkmode_text[] = {"EXTERNAL", "INTERNAL"};
+static SOC_ENUM_SINGLE_EXT_DECL(tasha_clkmode_enum, tasha_clkmode_text);
+
+/* Cutoff frequency for high pass filter */
+static const char * const cf_text[] = {
+ "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ"
+};
+
+static const char * const rx_cf_text[] = {
+ "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ",
+ "CF_NEG_3DB_0P48HZ"
+};
+
+static const struct soc_enum cf_dec0_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX0_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX1_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec2_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX2_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec3_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX3_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec4_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX4_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec5_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX5_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec6_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX6_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec7_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX7_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec8_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX8_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_int0_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int1_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int2_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int3_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int4_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int5_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int5_2_enum, WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int6_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int6_2_enum, WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int7_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct soc_enum cf_int8_1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct snd_soc_dapm_route audio_i2s_map[] = {
+ {"SLIM RX0 MUX", NULL, "RX_I2S_CTL"},
+ {"SLIM RX1 MUX", NULL, "RX_I2S_CTL"},
+ {"SLIM RX2 MUX", NULL, "RX_I2S_CTL"},
+ {"SLIM RX3 MUX", NULL, "RX_I2S_CTL"},
+
+ {"SLIM TX6 MUX", NULL, "TX_I2S_CTL"},
+ {"SLIM TX7 MUX", NULL, "TX_I2S_CTL"},
+ {"SLIM TX8 MUX", NULL, "TX_I2S_CTL"},
+ {"SLIM TX11 MUX", NULL, "TX_I2S_CTL"},
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* MAD */
+ {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"},
+ {"MAD_SEL MUX", "MSM", "MADINPUT"},
+ {"MADONOFF", "Switch", "MAD_SEL MUX"},
+ {"MAD_BROADCAST", "Switch", "MAD_SEL MUX"},
+ {"TX13 INP MUX", "CPE_TX_PP", "MADONOFF"},
+
+ /* CPE HW MAD bypass */
+ {"CPE IN Mixer", "MAD_BYPASS", "SLIM TX1 MUX"},
+
+ {"AIF4_MAD Mixer", "SLIM TX1", "CPE IN Mixer"},
+ {"AIF4_MAD Mixer", "SLIM TX12", "MADONOFF"},
+ {"AIF4_MAD Mixer", "SLIM TX13", "TX13 INP MUX"},
+ {"AIF4 MAD", NULL, "AIF4_MAD Mixer"},
+ {"AIF4 MAD", NULL, "AIF4"},
+
+ {"EC BUF MUX INP", "DEC1", "ADC MUX1"},
+ {"AIF5 CPE", NULL, "EC BUF MUX INP"},
+
+ /* SLIMBUS Connections */
+ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+ {"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+ {"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
+
+ /* VI Feedback */
+ {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"},
+ {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"},
+ {"AIF4 VI", NULL, "AIF4_VI Mixer"},
+
+ /* SLIM_MIXER("AIF1_CAP Mixer"),*/
+ {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"},
+ {"AIF1_CAP Mixer", "SLIM TX13", "TX13 INP MUX"},
+ /* SLIM_MIXER("AIF2_CAP Mixer"),*/
+ {"AIF2_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"},
+ {"AIF2_CAP Mixer", "SLIM TX13", "TX13 INP MUX"},
+ /* SLIM_MIXER("AIF3_CAP Mixer"),*/
+ {"AIF3_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"},
+ {"AIF3_CAP Mixer", "SLIM TX13", "TX13 INP MUX"},
+
+ {"SLIM TX0 MUX", "DEC0", "ADC MUX0"},
+ {"SLIM TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"},
+ {"SLIM TX0 MUX", "DEC0_192", "ADC US MUX0"},
+
+ {"SLIM TX1 MUX", "DEC1", "ADC MUX1"},
+ {"SLIM TX1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"},
+ {"SLIM TX1 MUX", "DEC1_192", "ADC US MUX1"},
+
+ {"SLIM TX2 MUX", "DEC2", "ADC MUX2"},
+ {"SLIM TX2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"},
+ {"SLIM TX2 MUX", "DEC2_192", "ADC US MUX2"},
+
+ {"SLIM TX3 MUX", "DEC3", "ADC MUX3"},
+ {"SLIM TX3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"},
+ {"SLIM TX3 MUX", "DEC3_192", "ADC US MUX3"},
+
+ {"SLIM TX4 MUX", "DEC4", "ADC MUX4"},
+ {"SLIM TX4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"},
+ {"SLIM TX4 MUX", "DEC4_192", "ADC US MUX4"},
+
+ {"SLIM TX5 MUX", "DEC5", "ADC MUX5"},
+ {"SLIM TX5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"},
+ {"SLIM TX5 MUX", "DEC5_192", "ADC US MUX5"},
+
+ {"SLIM TX6 MUX", "DEC6", "ADC MUX6"},
+ {"SLIM TX6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"},
+ {"SLIM TX6 MUX", "DEC6_192", "ADC US MUX6"},
+
+ {"SLIM TX7 MUX", "DEC7", "ADC MUX7"},
+ {"SLIM TX7 MUX", "RX_MIX_TX7", "RX MIX TX7 MUX"},
+ {"SLIM TX7 MUX", "DEC7_192", "ADC US MUX7"},
+
+ {"SLIM TX8 MUX", "DEC8", "ADC MUX8"},
+ {"SLIM TX8 MUX", "RX_MIX_TX8", "RX MIX TX8 MUX"},
+ {"SLIM TX8 MUX", "DEC8_192", "ADC US MUX8"},
+
+ {"SLIM TX9 MUX", "DEC7", "ADC MUX7"},
+ {"SLIM TX9 MUX", "DEC7_192", "ADC US MUX7"},
+ {"SLIM TX10 MUX", "DEC6", "ADC MUX6"},
+ {"SLIM TX10 MUX", "DEC6_192", "ADC US MUX6"},
+
+ {"SLIM TX11 MUX", "DEC_0_5", "SLIM TX11 INP1 MUX"},
+ {"SLIM TX11 MUX", "DEC_9_12", "SLIM TX11 INP1 MUX"},
+ {"SLIM TX11 INP1 MUX", "DEC0", "ADC MUX0"},
+ {"SLIM TX11 INP1 MUX", "DEC1", "ADC MUX1"},
+ {"SLIM TX11 INP1 MUX", "DEC2", "ADC MUX2"},
+ {"SLIM TX11 INP1 MUX", "DEC3", "ADC MUX3"},
+ {"SLIM TX11 INP1 MUX", "DEC4", "ADC MUX4"},
+ {"SLIM TX11 INP1 MUX", "DEC5", "ADC MUX5"},
+ {"SLIM TX11 INP1 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"},
+
+ {"TX13 INP MUX", "MAD_BRDCST", "MAD_BROADCAST"},
+ {"TX13 INP MUX", "CDC_DEC_5", "SLIM TX13 MUX"},
+ {"SLIM TX13 MUX", "DEC5", "ADC MUX5"},
+
+ {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+ {"RX MIX TX0 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+ {"RX MIX TX0 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+ {"RX MIX TX0 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+ {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+ {"RX MIX TX1 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+ {"RX MIX TX1 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+ {"RX MIX TX1 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+ {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+ {"RX MIX TX2 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+ {"RX MIX TX2 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+ {"RX MIX TX2 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+ {"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+ {"RX MIX TX3 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+ {"RX MIX TX3 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+ {"RX MIX TX3 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+ {"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+ {"RX MIX TX4 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+ {"RX MIX TX4 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+ {"RX MIX TX4 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+ {"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+ {"RX MIX TX5 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+ {"RX MIX TX5 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+ {"RX MIX TX5 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+ {"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+ {"RX MIX TX6 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+ {"RX MIX TX6 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+ {"RX MIX TX6 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+ {"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+ {"RX MIX TX7 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+ {"RX MIX TX7 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+ {"RX MIX TX7 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+ {"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+ {"RX MIX TX8 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+ {"RX MIX TX8 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+ {"RX MIX TX8 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+ {"ADC US MUX0", "US_Switch", "ADC MUX0"},
+ {"ADC US MUX1", "US_Switch", "ADC MUX1"},
+ {"ADC US MUX2", "US_Switch", "ADC MUX2"},
+ {"ADC US MUX3", "US_Switch", "ADC MUX3"},
+ {"ADC US MUX4", "US_Switch", "ADC MUX4"},
+ {"ADC US MUX5", "US_Switch", "ADC MUX5"},
+ {"ADC US MUX6", "US_Switch", "ADC MUX6"},
+ {"ADC US MUX7", "US_Switch", "ADC MUX7"},
+ {"ADC US MUX8", "US_Switch", "ADC MUX8"},
+ {"ADC MUX0", "DMIC", "DMIC MUX0"},
+ {"ADC MUX0", "AMIC", "AMIC MUX0"},
+ {"ADC MUX1", "DMIC", "DMIC MUX1"},
+ {"ADC MUX1", "AMIC", "AMIC MUX1"},
+ {"ADC MUX2", "DMIC", "DMIC MUX2"},
+ {"ADC MUX2", "AMIC", "AMIC MUX2"},
+ {"ADC MUX3", "DMIC", "DMIC MUX3"},
+ {"ADC MUX3", "AMIC", "AMIC MUX3"},
+ {"ADC MUX4", "DMIC", "DMIC MUX4"},
+ {"ADC MUX4", "AMIC", "AMIC MUX4"},
+ {"ADC MUX5", "DMIC", "DMIC MUX5"},
+ {"ADC MUX5", "AMIC", "AMIC MUX5"},
+ {"ADC MUX6", "DMIC", "DMIC MUX6"},
+ {"ADC MUX6", "AMIC", "AMIC MUX6"},
+ {"ADC MUX7", "DMIC", "DMIC MUX7"},
+ {"ADC MUX7", "AMIC", "AMIC MUX7"},
+ {"ADC MUX8", "DMIC", "DMIC MUX8"},
+ {"ADC MUX8", "AMIC", "AMIC MUX8"},
+ {"ADC MUX10", "DMIC", "DMIC MUX10"},
+ {"ADC MUX10", "AMIC", "AMIC MUX10"},
+ {"ADC MUX11", "DMIC", "DMIC MUX11"},
+ {"ADC MUX11", "AMIC", "AMIC MUX11"},
+ {"ADC MUX12", "DMIC", "DMIC MUX12"},
+ {"ADC MUX12", "AMIC", "AMIC MUX12"},
+ {"ADC MUX13", "DMIC", "DMIC MUX13"},
+ {"ADC MUX13", "AMIC", "AMIC MUX13"},
+
+ {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"},
+
+ {"DMIC MUX0", "DMIC0", "DMIC0"},
+ {"DMIC MUX0", "DMIC1", "DMIC1"},
+ {"DMIC MUX0", "DMIC2", "DMIC2"},
+ {"DMIC MUX0", "DMIC3", "DMIC3"},
+ {"DMIC MUX0", "DMIC4", "DMIC4"},
+ {"DMIC MUX0", "DMIC5", "DMIC5"},
+ {"AMIC MUX0", "ADC1", "ADC1"},
+ {"AMIC MUX0", "ADC2", "ADC2"},
+ {"AMIC MUX0", "ADC3", "ADC3"},
+ {"AMIC MUX0", "ADC4", "ADC4"},
+ {"AMIC MUX0", "ADC5", "ADC5"},
+ {"AMIC MUX0", "ADC6", "ADC6"},
+
+ {"DMIC MUX1", "DMIC0", "DMIC0"},
+ {"DMIC MUX1", "DMIC1", "DMIC1"},
+ {"DMIC MUX1", "DMIC2", "DMIC2"},
+ {"DMIC MUX1", "DMIC3", "DMIC3"},
+ {"DMIC MUX1", "DMIC4", "DMIC4"},
+ {"DMIC MUX1", "DMIC5", "DMIC5"},
+ {"AMIC MUX1", "ADC1", "ADC1"},
+ {"AMIC MUX1", "ADC2", "ADC2"},
+ {"AMIC MUX1", "ADC3", "ADC3"},
+ {"AMIC MUX1", "ADC4", "ADC4"},
+ {"AMIC MUX1", "ADC5", "ADC5"},
+ {"AMIC MUX1", "ADC6", "ADC6"},
+
+ {"DMIC MUX2", "DMIC0", "DMIC0"},
+ {"DMIC MUX2", "DMIC1", "DMIC1"},
+ {"DMIC MUX2", "DMIC2", "DMIC2"},
+ {"DMIC MUX2", "DMIC3", "DMIC3"},
+ {"DMIC MUX2", "DMIC4", "DMIC4"},
+ {"DMIC MUX2", "DMIC5", "DMIC5"},
+ {"AMIC MUX2", "ADC1", "ADC1"},
+ {"AMIC MUX2", "ADC2", "ADC2"},
+ {"AMIC MUX2", "ADC3", "ADC3"},
+ {"AMIC MUX2", "ADC4", "ADC4"},
+ {"AMIC MUX2", "ADC5", "ADC5"},
+ {"AMIC MUX2", "ADC6", "ADC6"},
+
+ {"DMIC MUX3", "DMIC0", "DMIC0"},
+ {"DMIC MUX3", "DMIC1", "DMIC1"},
+ {"DMIC MUX3", "DMIC2", "DMIC2"},
+ {"DMIC MUX3", "DMIC3", "DMIC3"},
+ {"DMIC MUX3", "DMIC4", "DMIC4"},
+ {"DMIC MUX3", "DMIC5", "DMIC5"},
+ {"AMIC MUX3", "ADC1", "ADC1"},
+ {"AMIC MUX3", "ADC2", "ADC2"},
+ {"AMIC MUX3", "ADC3", "ADC3"},
+ {"AMIC MUX3", "ADC4", "ADC4"},
+ {"AMIC MUX3", "ADC5", "ADC5"},
+ {"AMIC MUX3", "ADC6", "ADC6"},
+
+ {"DMIC MUX4", "DMIC0", "DMIC0"},
+ {"DMIC MUX4", "DMIC1", "DMIC1"},
+ {"DMIC MUX4", "DMIC2", "DMIC2"},
+ {"DMIC MUX4", "DMIC3", "DMIC3"},
+ {"DMIC MUX4", "DMIC4", "DMIC4"},
+ {"DMIC MUX4", "DMIC5", "DMIC5"},
+ {"AMIC MUX4", "ADC1", "ADC1"},
+ {"AMIC MUX4", "ADC2", "ADC2"},
+ {"AMIC MUX4", "ADC3", "ADC3"},
+ {"AMIC MUX4", "ADC4", "ADC4"},
+ {"AMIC MUX4", "ADC5", "ADC5"},
+ {"AMIC MUX4", "ADC6", "ADC6"},
+
+ {"DMIC MUX5", "DMIC0", "DMIC0"},
+ {"DMIC MUX5", "DMIC1", "DMIC1"},
+ {"DMIC MUX5", "DMIC2", "DMIC2"},
+ {"DMIC MUX5", "DMIC3", "DMIC3"},
+ {"DMIC MUX5", "DMIC4", "DMIC4"},
+ {"DMIC MUX5", "DMIC5", "DMIC5"},
+ {"AMIC MUX5", "ADC1", "ADC1"},
+ {"AMIC MUX5", "ADC2", "ADC2"},
+ {"AMIC MUX5", "ADC3", "ADC3"},
+ {"AMIC MUX5", "ADC4", "ADC4"},
+ {"AMIC MUX5", "ADC5", "ADC5"},
+ {"AMIC MUX5", "ADC6", "ADC6"},
+
+ {"DMIC MUX6", "DMIC0", "DMIC0"},
+ {"DMIC MUX6", "DMIC1", "DMIC1"},
+ {"DMIC MUX6", "DMIC2", "DMIC2"},
+ {"DMIC MUX6", "DMIC3", "DMIC3"},
+ {"DMIC MUX6", "DMIC4", "DMIC4"},
+ {"DMIC MUX6", "DMIC5", "DMIC5"},
+ {"AMIC MUX6", "ADC1", "ADC1"},
+ {"AMIC MUX6", "ADC2", "ADC2"},
+ {"AMIC MUX6", "ADC3", "ADC3"},
+ {"AMIC MUX6", "ADC4", "ADC4"},
+ {"AMIC MUX6", "ADC5", "ADC5"},
+ {"AMIC MUX6", "ADC6", "ADC6"},
+
+ {"DMIC MUX7", "DMIC0", "DMIC0"},
+ {"DMIC MUX7", "DMIC1", "DMIC1"},
+ {"DMIC MUX7", "DMIC2", "DMIC2"},
+ {"DMIC MUX7", "DMIC3", "DMIC3"},
+ {"DMIC MUX7", "DMIC4", "DMIC4"},
+ {"DMIC MUX7", "DMIC5", "DMIC5"},
+ {"AMIC MUX7", "ADC1", "ADC1"},
+ {"AMIC MUX7", "ADC2", "ADC2"},
+ {"AMIC MUX7", "ADC3", "ADC3"},
+ {"AMIC MUX7", "ADC4", "ADC4"},
+ {"AMIC MUX7", "ADC5", "ADC5"},
+ {"AMIC MUX7", "ADC6", "ADC6"},
+
+ {"DMIC MUX8", "DMIC0", "DMIC0"},
+ {"DMIC MUX8", "DMIC1", "DMIC1"},
+ {"DMIC MUX8", "DMIC2", "DMIC2"},
+ {"DMIC MUX8", "DMIC3", "DMIC3"},
+ {"DMIC MUX8", "DMIC4", "DMIC4"},
+ {"DMIC MUX8", "DMIC5", "DMIC5"},
+ {"AMIC MUX8", "ADC1", "ADC1"},
+ {"AMIC MUX8", "ADC2", "ADC2"},
+ {"AMIC MUX8", "ADC3", "ADC3"},
+ {"AMIC MUX8", "ADC4", "ADC4"},
+ {"AMIC MUX8", "ADC5", "ADC5"},
+ {"AMIC MUX8", "ADC6", "ADC6"},
+
+ {"DMIC MUX10", "DMIC0", "DMIC0"},
+ {"DMIC MUX10", "DMIC1", "DMIC1"},
+ {"DMIC MUX10", "DMIC2", "DMIC2"},
+ {"DMIC MUX10", "DMIC3", "DMIC3"},
+ {"DMIC MUX10", "DMIC4", "DMIC4"},
+ {"DMIC MUX10", "DMIC5", "DMIC5"},
+ {"AMIC MUX10", "ADC1", "ADC1"},
+ {"AMIC MUX10", "ADC2", "ADC2"},
+ {"AMIC MUX10", "ADC3", "ADC3"},
+ {"AMIC MUX10", "ADC4", "ADC4"},
+ {"AMIC MUX10", "ADC5", "ADC5"},
+ {"AMIC MUX10", "ADC6", "ADC6"},
+
+ {"DMIC MUX11", "DMIC0", "DMIC0"},
+ {"DMIC MUX11", "DMIC1", "DMIC1"},
+ {"DMIC MUX11", "DMIC2", "DMIC2"},
+ {"DMIC MUX11", "DMIC3", "DMIC3"},
+ {"DMIC MUX11", "DMIC4", "DMIC4"},
+ {"DMIC MUX11", "DMIC5", "DMIC5"},
+ {"AMIC MUX11", "ADC1", "ADC1"},
+ {"AMIC MUX11", "ADC2", "ADC2"},
+ {"AMIC MUX11", "ADC3", "ADC3"},
+ {"AMIC MUX11", "ADC4", "ADC4"},
+ {"AMIC MUX11", "ADC5", "ADC5"},
+ {"AMIC MUX11", "ADC6", "ADC6"},
+
+ {"DMIC MUX12", "DMIC0", "DMIC0"},
+ {"DMIC MUX12", "DMIC1", "DMIC1"},
+ {"DMIC MUX12", "DMIC2", "DMIC2"},
+ {"DMIC MUX12", "DMIC3", "DMIC3"},
+ {"DMIC MUX12", "DMIC4", "DMIC4"},
+ {"DMIC MUX12", "DMIC5", "DMIC5"},
+ {"AMIC MUX12", "ADC1", "ADC1"},
+ {"AMIC MUX12", "ADC2", "ADC2"},
+ {"AMIC MUX12", "ADC3", "ADC3"},
+ {"AMIC MUX12", "ADC4", "ADC4"},
+ {"AMIC MUX12", "ADC5", "ADC5"},
+ {"AMIC MUX12", "ADC6", "ADC6"},
+
+ {"DMIC MUX13", "DMIC0", "DMIC0"},
+ {"DMIC MUX13", "DMIC1", "DMIC1"},
+ {"DMIC MUX13", "DMIC2", "DMIC2"},
+ {"DMIC MUX13", "DMIC3", "DMIC3"},
+ {"DMIC MUX13", "DMIC4", "DMIC4"},
+ {"DMIC MUX13", "DMIC5", "DMIC5"},
+ {"AMIC MUX13", "ADC1", "ADC1"},
+ {"AMIC MUX13", "ADC2", "ADC2"},
+ {"AMIC MUX13", "ADC3", "ADC3"},
+ {"AMIC MUX13", "ADC4", "ADC4"},
+ {"AMIC MUX13", "ADC5", "ADC5"},
+ {"AMIC MUX13", "ADC6", "ADC6"},
+ /* ADC Connections */
+ {"ADC1", NULL, "AMIC1"},
+ {"ADC2", NULL, "AMIC2"},
+ {"ADC3", NULL, "AMIC3"},
+ {"ADC4", NULL, "AMIC4"},
+ {"ADC5", NULL, "AMIC5"},
+ {"ADC6", NULL, "AMIC6"},
+
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"},
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"},
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"},
+ {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP0"},
+ {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP1"},
+ {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP2"},
+ {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP0"},
+ {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP1"},
+ {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP2"},
+ {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP0"},
+ {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP1"},
+ {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP2"},
+ {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP0"},
+ {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP1"},
+ {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP2"},
+ {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"},
+ {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP1"},
+ {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP2"},
+ {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP0"},
+ {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP1"},
+ {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP2"},
+
+ {"RX INT0 SEC MIX", NULL, "RX INT0_1 MIX1"},
+ {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"},
+ {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"},
+ {"RX INT0 INTERP", NULL, "RX INT0 MIX2"},
+ {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 INTERP"},
+ {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"},
+ {"RX INT0 DAC", NULL, "RX_BIAS"},
+ {"EAR PA", NULL, "RX INT0 DAC"},
+ {"EAR", NULL, "EAR PA"},
+
+ {"SPL SRC0 MUX", "SRC_IN_HPHL", "RX INT1_1 MIX1"},
+ {"RX INT1 SPLINE MIX", NULL, "RX INT1_1 MIX1"},
+ {"RX INT1 SPLINE MIX", "HPHL Switch", "SPL SRC0 MUX"},
+ {"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"},
+ {"RX INT1 SPLINE MIX", NULL, "RX INT1_1 NATIVE MUX"},
+ {"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"},
+ {"RX INT1 SEC MIX", NULL, "RX INT1 SPLINE MIX"},
+ {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"},
+ {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"},
+ {"RX INT1 INTERP", NULL, "RX INT1 MIX2"},
+ {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 INTERP"},
+ {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"},
+ {"RX INT1 DAC", NULL, "RX_BIAS"},
+ {"HPHL PA", NULL, "RX INT1 DAC"},
+ {"HPHL", NULL, "HPHL PA"},
+
+ {"SPL SRC1 MUX", "SRC_IN_HPHR", "RX INT2_1 MIX1"},
+ {"RX INT2 SPLINE MIX", NULL, "RX INT2_1 MIX1"},
+ {"RX INT2 SPLINE MIX", "HPHR Switch", "SPL SRC1 MUX"},
+ {"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"},
+ {"RX INT2 SPLINE MIX", NULL, "RX INT2_1 NATIVE MUX"},
+ {"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"},
+ {"RX INT2 SEC MIX", NULL, "RX INT2 SPLINE MIX"},
+ {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"},
+ {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"},
+ {"RX INT2 INTERP", NULL, "RX INT2 MIX2"},
+ {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 INTERP"},
+ {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"},
+ {"RX INT2 DAC", NULL, "RX_BIAS"},
+ {"HPHR PA", NULL, "RX INT2 DAC"},
+ {"HPHR", NULL, "HPHR PA"},
+
+ {"SPL SRC0 MUX", "SRC_IN_LO1", "RX INT3_1 MIX1"},
+ {"RX INT3 SPLINE MIX", NULL, "RX INT3_1 MIX1"},
+ {"RX INT3 SPLINE MIX", "LO1 Switch", "SPL SRC0 MUX"},
+ {"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"},
+ {"RX INT3 SPLINE MIX", NULL, "RX INT3_1 NATIVE MUX"},
+ {"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"},
+ {"RX INT3 SEC MIX", NULL, "RX INT3 SPLINE MIX"},
+ {"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"},
+ {"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"},
+ {"RX INT3 INTERP", NULL, "RX INT3 MIX2"},
+ {"RX INT3 DAC", NULL, "RX INT3 INTERP"},
+ {"RX INT3 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT1 PA", NULL, "RX INT3 DAC"},
+ {"LINEOUT1", NULL, "LINEOUT1 PA"},
+
+ {"SPL SRC1 MUX", "SRC_IN_LO2", "RX INT4_1 MIX1"},
+ {"RX INT4 SPLINE MIX", NULL, "RX INT4_1 MIX1"},
+ {"RX INT4 SPLINE MIX", "LO2 Switch", "SPL SRC1 MUX"},
+ {"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"},
+ {"RX INT4 SPLINE MIX", NULL, "RX INT4_1 NATIVE MUX"},
+ {"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"},
+ {"RX INT4 SEC MIX", NULL, "RX INT4 SPLINE MIX"},
+ {"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"},
+ {"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"},
+ {"RX INT4 INTERP", NULL, "RX INT4 MIX2"},
+ {"RX INT4 DAC", NULL, "RX INT4 INTERP"},
+ {"RX INT4 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT2 PA", NULL, "RX INT4 DAC"},
+ {"LINEOUT2", NULL, "LINEOUT2 PA"},
+
+ {"SPL SRC2 MUX", "SRC_IN_LO3", "RX INT5_1 MIX1"},
+ {"RX INT5 SPLINE MIX", NULL, "RX INT5_1 MIX1"},
+ {"RX INT5 SPLINE MIX", "LO3 Switch", "SPL SRC2 MUX"},
+ {"RX INT5 SEC MIX", NULL, "RX INT5 SPLINE MIX"},
+ {"RX INT5 MIX2", NULL, "RX INT5 SEC MIX"},
+ {"RX INT5 INTERP", NULL, "RX INT5 MIX2"},
+
+ {"RX INT5 VBAT", "LO3 VBAT Enable", "RX INT5 INTERP"},
+ {"RX INT5 DAC", NULL, "RX INT5 VBAT"},
+
+ {"RX INT5 DAC", NULL, "RX INT5 INTERP"},
+ {"RX INT5 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT3 PA", NULL, "RX INT5 DAC"},
+ {"LINEOUT3", NULL, "LINEOUT3 PA"},
+
+ {"SPL SRC3 MUX", "SRC_IN_LO4", "RX INT6_1 MIX1"},
+ {"RX INT6 SPLINE MIX", NULL, "RX INT6_1 MIX1"},
+ {"RX INT6 SPLINE MIX", "LO4 Switch", "SPL SRC3 MUX"},
+ {"RX INT6 SEC MIX", NULL, "RX INT6 SPLINE MIX"},
+ {"RX INT6 MIX2", NULL, "RX INT6 SEC MIX"},
+ {"RX INT6 INTERP", NULL, "RX INT6 MIX2"},
+
+ {"RX INT6 VBAT", "LO4 VBAT Enable", "RX INT6 INTERP"},
+ {"RX INT6 DAC", NULL, "RX INT6 VBAT"},
+
+ {"RX INT6 DAC", NULL, "RX INT6 INTERP"},
+ {"RX INT6 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT4 PA", NULL, "RX INT6 DAC"},
+ {"LINEOUT4", NULL, "LINEOUT4 PA"},
+
+ {"SPL SRC2 MUX", "SRC_IN_SPKRL", "RX INT7_1 MIX1"},
+ {"RX INT7 SPLINE MIX", NULL, "RX INT7_1 MIX1"},
+ {"RX INT7 SPLINE MIX", "SPKRL Switch", "SPL SRC2 MUX"},
+ {"RX INT7 SEC MIX", NULL, "RX INT7 SPLINE MIX"},
+ {"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"},
+ {"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"},
+
+ {"RX INT7 INTERP", NULL, "RX INT7 MIX2"},
+
+ {"RX INT7 VBAT", "SPKRL VBAT Enable", "RX INT7 INTERP"},
+ {"RX INT7 CHAIN", NULL, "RX INT7 VBAT"},
+
+ {"RX INT7 CHAIN", NULL, "RX INT7 INTERP"},
+ {"RX INT7 CHAIN", NULL, "RX_BIAS"},
+ {"SPK1 OUT", NULL, "RX INT7 CHAIN"},
+
+ {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"},
+ {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"},
+ {"SPK1 OUT", NULL, "ANC SPK1 PA"},
+
+ {"SPL SRC3 MUX", "SRC_IN_SPKRR", "RX INT8_1 MIX1"},
+ {"RX INT8 SPLINE MIX", NULL, "RX INT8_1 MIX1"},
+ {"RX INT8 SPLINE MIX", "SPKRR Switch", "SPL SRC3 MUX"},
+ {"RX INT8 SEC MIX", NULL, "RX INT8 SPLINE MIX"},
+ {"RX INT8 INTERP", NULL, "RX INT8 SEC MIX"},
+
+ {"RX INT8 VBAT", "SPKRR VBAT Enable", "RX INT8 INTERP"},
+ {"RX INT8 CHAIN", NULL, "RX INT8 VBAT"},
+
+ {"RX INT8 CHAIN", NULL, "RX INT8 INTERP"},
+ {"RX INT8 CHAIN", NULL, "RX_BIAS"},
+ {"SPK2 OUT", NULL, "RX INT8 CHAIN"},
+
+ {"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"},
+ {"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"},
+ {"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"},
+ {"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"},
+ {"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"},
+ {"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"},
+
+ {"ANC HPHL Enable", "Switch", "ADC MUX10"},
+ {"ANC HPHL Enable", "Switch", "ADC MUX11"},
+ {"RX INT1 MIX2", NULL, "ANC HPHL Enable"},
+
+ {"ANC HPHR Enable", "Switch", "ADC MUX12"},
+ {"ANC HPHR Enable", "Switch", "ADC MUX13"},
+ {"RX INT2 MIX2", NULL, "ANC HPHR Enable"},
+
+ {"ANC EAR Enable", "Switch", "ADC MUX10"},
+ {"ANC EAR Enable", "Switch", "ADC MUX11"},
+ {"RX INT0 MIX2", NULL, "ANC EAR Enable"},
+
+ {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"},
+ {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"},
+ {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"},
+
+ {"ANC LINEOUT1 Enable", "Switch", "ADC MUX10"},
+ {"ANC LINEOUT1 Enable", "Switch", "ADC MUX11"},
+ {"RX INT3 MIX2", NULL, "ANC LINEOUT1 Enable"},
+
+ {"ANC LINEOUT2 Enable", "Switch", "ADC MUX12"},
+ {"ANC LINEOUT2 Enable", "Switch", "ADC MUX13"},
+ {"RX INT4 MIX2", NULL, "ANC LINEOUT2 Enable"},
+
+ {"ANC EAR PA", NULL, "RX INT0 DAC"},
+ {"ANC EAR", NULL, "ANC EAR PA"},
+ {"ANC HPHL PA", NULL, "RX INT1 DAC"},
+ {"ANC HPHL", NULL, "ANC HPHL PA"},
+ {"ANC HPHR PA", NULL, "RX INT2 DAC"},
+ {"ANC HPHR", NULL, "ANC HPHR PA"},
+ {"ANC LINEOUT1 PA", NULL, "RX INT3 DAC"},
+ {"ANC LINEOUT1", NULL, "ANC LINEOUT1 PA"},
+ {"ANC LINEOUT2 PA", NULL, "RX INT4 DAC"},
+ {"ANC LINEOUT2", NULL, "ANC LINEOUT2 PA"},
+
+ /* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+ {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+ /* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+ {"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+ /* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+ {"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+ /* SLIM_MUX("AIF4_PB", "AIF4 PB"),*/
+ {"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"},
+
+ /* SLIM_MUX("AIF_MIX1_PB", "AIF MIX1 PB"),*/
+ {"SLIM RX0 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+ {"SLIM RX1 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+ {"SLIM RX2 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+ {"SLIM RX3 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+ {"SLIM RX4 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+ {"SLIM RX5 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+ {"SLIM RX6 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+ {"SLIM RX7 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+
+ {"SLIM RX0", NULL, "SLIM RX0 MUX"},
+ {"SLIM RX1", NULL, "SLIM RX1 MUX"},
+ {"SLIM RX2", NULL, "SLIM RX2 MUX"},
+ {"SLIM RX3", NULL, "SLIM RX3 MUX"},
+ {"SLIM RX4", NULL, "SLIM RX4 MUX"},
+ {"SLIM RX5", NULL, "SLIM RX5 MUX"},
+ {"SLIM RX6", NULL, "SLIM RX6 MUX"},
+ {"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
+ {"RX INT0_1 MIX1 INP0", "RX0", "SLIM RX0"},
+ {"RX INT0_1 MIX1 INP0", "RX1", "SLIM RX1"},
+ {"RX INT0_1 MIX1 INP0", "RX2", "SLIM RX2"},
+ {"RX INT0_1 MIX1 INP0", "RX3", "SLIM RX3"},
+ {"RX INT0_1 MIX1 INP0", "RX4", "SLIM RX4"},
+ {"RX INT0_1 MIX1 INP0", "RX5", "SLIM RX5"},
+ {"RX INT0_1 MIX1 INP0", "RX6", "SLIM RX6"},
+ {"RX INT0_1 MIX1 INP0", "RX7", "SLIM RX7"},
+ {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT0_1 MIX1 INP1", "RX0", "SLIM RX0"},
+ {"RX INT0_1 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX INT0_1 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX INT0_1 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX INT0_1 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX INT0_1 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX INT0_1 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX INT0_1 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT0_1 MIX1 INP2", "RX0", "SLIM RX0"},
+ {"RX INT0_1 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX INT0_1 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX INT0_1 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX INT0_1 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX INT0_1 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX INT0_1 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX INT0_1 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ /* MIXing path INT0 */
+ {"RX INT0_2 MUX", "RX0", "SLIM RX0"},
+ {"RX INT0_2 MUX", "RX1", "SLIM RX1"},
+ {"RX INT0_2 MUX", "RX2", "SLIM RX2"},
+ {"RX INT0_2 MUX", "RX3", "SLIM RX3"},
+ {"RX INT0_2 MUX", "RX4", "SLIM RX4"},
+ {"RX INT0_2 MUX", "RX5", "SLIM RX5"},
+ {"RX INT0_2 MUX", "RX6", "SLIM RX6"},
+ {"RX INT0_2 MUX", "RX7", "SLIM RX7"},
+ {"RX INT0 SEC MIX", NULL, "RX INT0_2 MUX"},
+
+ /* MIXing path INT1 */
+ {"RX INT1_2 MUX", "RX0", "SLIM RX0"},
+ {"RX INT1_2 MUX", "RX1", "SLIM RX1"},
+ {"RX INT1_2 MUX", "RX2", "SLIM RX2"},
+ {"RX INT1_2 MUX", "RX3", "SLIM RX3"},
+ {"RX INT1_2 MUX", "RX4", "SLIM RX4"},
+ {"RX INT1_2 MUX", "RX5", "SLIM RX5"},
+ {"RX INT1_2 MUX", "RX6", "SLIM RX6"},
+ {"RX INT1_2 MUX", "RX7", "SLIM RX7"},
+ {"RX INT1 SEC MIX", NULL, "RX INT1_2 MUX"},
+
+ /* MIXing path INT2 */
+ {"RX INT2_2 MUX", "RX0", "SLIM RX0"},
+ {"RX INT2_2 MUX", "RX1", "SLIM RX1"},
+ {"RX INT2_2 MUX", "RX2", "SLIM RX2"},
+ {"RX INT2_2 MUX", "RX3", "SLIM RX3"},
+ {"RX INT2_2 MUX", "RX4", "SLIM RX4"},
+ {"RX INT2_2 MUX", "RX5", "SLIM RX5"},
+ {"RX INT2_2 MUX", "RX6", "SLIM RX6"},
+ {"RX INT2_2 MUX", "RX7", "SLIM RX7"},
+ {"RX INT2 SEC MIX", NULL, "RX INT2_2 MUX"},
+
+ /* MIXing path INT3 */
+ {"RX INT3_2 MUX", "RX0", "SLIM RX0"},
+ {"RX INT3_2 MUX", "RX1", "SLIM RX1"},
+ {"RX INT3_2 MUX", "RX2", "SLIM RX2"},
+ {"RX INT3_2 MUX", "RX3", "SLIM RX3"},
+ {"RX INT3_2 MUX", "RX4", "SLIM RX4"},
+ {"RX INT3_2 MUX", "RX5", "SLIM RX5"},
+ {"RX INT3_2 MUX", "RX6", "SLIM RX6"},
+ {"RX INT3_2 MUX", "RX7", "SLIM RX7"},
+ {"RX INT3 SEC MIX", NULL, "RX INT3_2 MUX"},
+
+ /* MIXing path INT4 */
+ {"RX INT4_2 MUX", "RX0", "SLIM RX0"},
+ {"RX INT4_2 MUX", "RX1", "SLIM RX1"},
+ {"RX INT4_2 MUX", "RX2", "SLIM RX2"},
+ {"RX INT4_2 MUX", "RX3", "SLIM RX3"},
+ {"RX INT4_2 MUX", "RX4", "SLIM RX4"},
+ {"RX INT4_2 MUX", "RX5", "SLIM RX5"},
+ {"RX INT4_2 MUX", "RX6", "SLIM RX6"},
+ {"RX INT4_2 MUX", "RX7", "SLIM RX7"},
+ {"RX INT4 SEC MIX", NULL, "RX INT4_2 MUX"},
+
+ /* MIXing path INT5 */
+ {"RX INT5_2 MUX", "RX0", "SLIM RX0"},
+ {"RX INT5_2 MUX", "RX1", "SLIM RX1"},
+ {"RX INT5_2 MUX", "RX2", "SLIM RX2"},
+ {"RX INT5_2 MUX", "RX3", "SLIM RX3"},
+ {"RX INT5_2 MUX", "RX4", "SLIM RX4"},
+ {"RX INT5_2 MUX", "RX5", "SLIM RX5"},
+ {"RX INT5_2 MUX", "RX6", "SLIM RX6"},
+ {"RX INT5_2 MUX", "RX7", "SLIM RX7"},
+ {"RX INT5 SEC MIX", NULL, "RX INT5_2 MUX"},
+
+ /* MIXing path INT6 */
+ {"RX INT6_2 MUX", "RX0", "SLIM RX0"},
+ {"RX INT6_2 MUX", "RX1", "SLIM RX1"},
+ {"RX INT6_2 MUX", "RX2", "SLIM RX2"},
+ {"RX INT6_2 MUX", "RX3", "SLIM RX3"},
+ {"RX INT6_2 MUX", "RX4", "SLIM RX4"},
+ {"RX INT6_2 MUX", "RX5", "SLIM RX5"},
+ {"RX INT6_2 MUX", "RX6", "SLIM RX6"},
+ {"RX INT6_2 MUX", "RX7", "SLIM RX7"},
+ {"RX INT6 SEC MIX", NULL, "RX INT6_2 MUX"},
+
+ /* MIXing path INT7 */
+ {"RX INT7_2 MUX", "RX0", "SLIM RX0"},
+ {"RX INT7_2 MUX", "RX1", "SLIM RX1"},
+ {"RX INT7_2 MUX", "RX2", "SLIM RX2"},
+ {"RX INT7_2 MUX", "RX3", "SLIM RX3"},
+ {"RX INT7_2 MUX", "RX4", "SLIM RX4"},
+ {"RX INT7_2 MUX", "RX5", "SLIM RX5"},
+ {"RX INT7_2 MUX", "RX6", "SLIM RX6"},
+ {"RX INT7_2 MUX", "RX7", "SLIM RX7"},
+ {"RX INT7 SEC MIX", NULL, "RX INT7_2 MUX"},
+
+ /* MIXing path INT8 */
+ {"RX INT8_2 MUX", "RX0", "SLIM RX0"},
+ {"RX INT8_2 MUX", "RX1", "SLIM RX1"},
+ {"RX INT8_2 MUX", "RX2", "SLIM RX2"},
+ {"RX INT8_2 MUX", "RX3", "SLIM RX3"},
+ {"RX INT8_2 MUX", "RX4", "SLIM RX4"},
+ {"RX INT8_2 MUX", "RX5", "SLIM RX5"},
+ {"RX INT8_2 MUX", "RX6", "SLIM RX6"},
+ {"RX INT8_2 MUX", "RX7", "SLIM RX7"},
+ {"RX INT8 SEC MIX", NULL, "RX INT8_2 MUX"},
+
+ {"RX INT1_1 MIX1 INP0", "RX0", "SLIM RX0"},
+ {"RX INT1_1 MIX1 INP0", "RX1", "SLIM RX1"},
+ {"RX INT1_1 MIX1 INP0", "RX2", "SLIM RX2"},
+ {"RX INT1_1 MIX1 INP0", "RX3", "SLIM RX3"},
+ {"RX INT1_1 MIX1 INP0", "RX4", "SLIM RX4"},
+ {"RX INT1_1 MIX1 INP0", "RX5", "SLIM RX5"},
+ {"RX INT1_1 MIX1 INP0", "RX6", "SLIM RX6"},
+ {"RX INT1_1 MIX1 INP0", "RX7", "SLIM RX7"},
+ {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT1_1 MIX1 INP1", "RX0", "SLIM RX0"},
+ {"RX INT1_1 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX INT1_1 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX INT1_1 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX INT1_1 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX INT1_1 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX INT1_1 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX INT1_1 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT1_1 MIX1 INP2", "RX0", "SLIM RX0"},
+ {"RX INT1_1 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX INT1_1 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX INT1_1 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX INT1_1 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX INT1_1 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX INT1_1 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX INT1_1 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP0", "RX0", "SLIM RX0"},
+ {"RX INT2_1 MIX1 INP0", "RX1", "SLIM RX1"},
+ {"RX INT2_1 MIX1 INP0", "RX2", "SLIM RX2"},
+ {"RX INT2_1 MIX1 INP0", "RX3", "SLIM RX3"},
+ {"RX INT2_1 MIX1 INP0", "RX4", "SLIM RX4"},
+ {"RX INT2_1 MIX1 INP0", "RX5", "SLIM RX5"},
+ {"RX INT2_1 MIX1 INP0", "RX6", "SLIM RX6"},
+ {"RX INT2_1 MIX1 INP0", "RX7", "SLIM RX7"},
+ {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP1", "RX0", "SLIM RX0"},
+ {"RX INT2_1 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX INT2_1 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX INT2_1 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX INT2_1 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX INT2_1 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX INT2_1 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX INT2_1 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP2", "RX0", "SLIM RX0"},
+ {"RX INT2_1 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX INT2_1 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX INT2_1 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX INT2_1 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX INT2_1 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX INT2_1 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX INT2_1 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT3_1 MIX1 INP0", "RX0", "SLIM RX0"},
+ {"RX INT3_1 MIX1 INP0", "RX1", "SLIM RX1"},
+ {"RX INT3_1 MIX1 INP0", "RX2", "SLIM RX2"},
+ {"RX INT3_1 MIX1 INP0", "RX3", "SLIM RX3"},
+ {"RX INT3_1 MIX1 INP0", "RX4", "SLIM RX4"},
+ {"RX INT3_1 MIX1 INP0", "RX5", "SLIM RX5"},
+ {"RX INT3_1 MIX1 INP0", "RX6", "SLIM RX6"},
+ {"RX INT3_1 MIX1 INP0", "RX7", "SLIM RX7"},
+ {"RX INT3_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT3_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT3_1 MIX1 INP1", "RX0", "SLIM RX0"},
+ {"RX INT3_1 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX INT3_1 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX INT3_1 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX INT3_1 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX INT3_1 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX INT3_1 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX INT3_1 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX INT3_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT3_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT3_1 MIX1 INP2", "RX0", "SLIM RX0"},
+ {"RX INT3_1 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX INT3_1 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX INT3_1 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX INT3_1 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX INT3_1 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX INT3_1 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX INT3_1 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX INT3_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT3_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT4_1 MIX1 INP0", "RX0", "SLIM RX0"},
+ {"RX INT4_1 MIX1 INP0", "RX1", "SLIM RX1"},
+ {"RX INT4_1 MIX1 INP0", "RX2", "SLIM RX2"},
+ {"RX INT4_1 MIX1 INP0", "RX3", "SLIM RX3"},
+ {"RX INT4_1 MIX1 INP0", "RX4", "SLIM RX4"},
+ {"RX INT4_1 MIX1 INP0", "RX5", "SLIM RX5"},
+ {"RX INT4_1 MIX1 INP0", "RX6", "SLIM RX6"},
+ {"RX INT4_1 MIX1 INP0", "RX7", "SLIM RX7"},
+ {"RX INT4_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT4_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT4_1 MIX1 INP1", "RX0", "SLIM RX0"},
+ {"RX INT4_1 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX INT4_1 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX INT4_1 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX INT4_1 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX INT4_1 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX INT4_1 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX INT4_1 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX INT4_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT4_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT4_1 MIX1 INP2", "RX0", "SLIM RX0"},
+ {"RX INT4_1 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX INT4_1 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX INT4_1 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX INT4_1 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX INT4_1 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX INT4_1 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX INT4_1 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX INT4_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT4_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT5_1 MIX1 INP0", "RX0", "SLIM RX0"},
+ {"RX INT5_1 MIX1 INP0", "RX1", "SLIM RX1"},
+ {"RX INT5_1 MIX1 INP0", "RX2", "SLIM RX2"},
+ {"RX INT5_1 MIX1 INP0", "RX3", "SLIM RX3"},
+ {"RX INT5_1 MIX1 INP0", "RX4", "SLIM RX4"},
+ {"RX INT5_1 MIX1 INP0", "RX5", "SLIM RX5"},
+ {"RX INT5_1 MIX1 INP0", "RX6", "SLIM RX6"},
+ {"RX INT5_1 MIX1 INP0", "RX7", "SLIM RX7"},
+ {"RX INT5_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT5_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT5_1 MIX1 INP1", "RX0", "SLIM RX0"},
+ {"RX INT5_1 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX INT5_1 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX INT5_1 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX INT5_1 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX INT5_1 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX INT5_1 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX INT5_1 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX INT5_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT5_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT5_1 MIX1 INP2", "RX0", "SLIM RX0"},
+ {"RX INT5_1 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX INT5_1 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX INT5_1 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX INT5_1 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX INT5_1 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX INT5_1 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX INT5_1 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX INT5_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT5_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT6_1 MIX1 INP0", "RX0", "SLIM RX0"},
+ {"RX INT6_1 MIX1 INP0", "RX1", "SLIM RX1"},
+ {"RX INT6_1 MIX1 INP0", "RX2", "SLIM RX2"},
+ {"RX INT6_1 MIX1 INP0", "RX3", "SLIM RX3"},
+ {"RX INT6_1 MIX1 INP0", "RX4", "SLIM RX4"},
+ {"RX INT6_1 MIX1 INP0", "RX5", "SLIM RX5"},
+ {"RX INT6_1 MIX1 INP0", "RX6", "SLIM RX6"},
+ {"RX INT6_1 MIX1 INP0", "RX7", "SLIM RX7"},
+ {"RX INT6_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT6_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT6_1 MIX1 INP1", "RX0", "SLIM RX0"},
+ {"RX INT6_1 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX INT6_1 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX INT6_1 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX INT6_1 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX INT6_1 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX INT6_1 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX INT6_1 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX INT6_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT6_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT6_1 MIX1 INP2", "RX0", "SLIM RX0"},
+ {"RX INT6_1 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX INT6_1 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX INT6_1 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX INT6_1 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX INT6_1 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX INT6_1 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX INT6_1 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX INT6_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT6_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT7_1 MIX1 INP0", "RX0", "SLIM RX0"},
+ {"RX INT7_1 MIX1 INP0", "RX1", "SLIM RX1"},
+ {"RX INT7_1 MIX1 INP0", "RX2", "SLIM RX2"},
+ {"RX INT7_1 MIX1 INP0", "RX3", "SLIM RX3"},
+ {"RX INT7_1 MIX1 INP0", "RX4", "SLIM RX4"},
+ {"RX INT7_1 MIX1 INP0", "RX5", "SLIM RX5"},
+ {"RX INT7_1 MIX1 INP0", "RX6", "SLIM RX6"},
+ {"RX INT7_1 MIX1 INP0", "RX7", "SLIM RX7"},
+ {"RX INT7_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT7_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT7_1 MIX1 INP1", "RX0", "SLIM RX0"},
+ {"RX INT7_1 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX INT7_1 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX INT7_1 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX INT7_1 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX INT7_1 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX INT7_1 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX INT7_1 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX INT7_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT7_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT7_1 MIX1 INP2", "RX0", "SLIM RX0"},
+ {"RX INT7_1 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX INT7_1 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX INT7_1 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX INT7_1 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX INT7_1 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX INT7_1 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX INT7_1 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX INT7_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT7_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT8_1 MIX1 INP0", "RX0", "SLIM RX0"},
+ {"RX INT8_1 MIX1 INP0", "RX1", "SLIM RX1"},
+ {"RX INT8_1 MIX1 INP0", "RX2", "SLIM RX2"},
+ {"RX INT8_1 MIX1 INP0", "RX3", "SLIM RX3"},
+ {"RX INT8_1 MIX1 INP0", "RX4", "SLIM RX4"},
+ {"RX INT8_1 MIX1 INP0", "RX5", "SLIM RX5"},
+ {"RX INT8_1 MIX1 INP0", "RX6", "SLIM RX6"},
+ {"RX INT8_1 MIX1 INP0", "RX7", "SLIM RX7"},
+ {"RX INT8_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT8_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT8_1 MIX1 INP1", "RX0", "SLIM RX0"},
+ {"RX INT8_1 MIX1 INP1", "RX1", "SLIM RX1"},
+ {"RX INT8_1 MIX1 INP1", "RX2", "SLIM RX2"},
+ {"RX INT8_1 MIX1 INP1", "RX3", "SLIM RX3"},
+ {"RX INT8_1 MIX1 INP1", "RX4", "SLIM RX4"},
+ {"RX INT8_1 MIX1 INP1", "RX5", "SLIM RX5"},
+ {"RX INT8_1 MIX1 INP1", "RX6", "SLIM RX6"},
+ {"RX INT8_1 MIX1 INP1", "RX7", "SLIM RX7"},
+ {"RX INT8_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT8_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT8_1 MIX1 INP2", "RX0", "SLIM RX0"},
+ {"RX INT8_1 MIX1 INP2", "RX1", "SLIM RX1"},
+ {"RX INT8_1 MIX1 INP2", "RX2", "SLIM RX2"},
+ {"RX INT8_1 MIX1 INP2", "RX3", "SLIM RX3"},
+ {"RX INT8_1 MIX1 INP2", "RX4", "SLIM RX4"},
+ {"RX INT8_1 MIX1 INP2", "RX5", "SLIM RX5"},
+ {"RX INT8_1 MIX1 INP2", "RX6", "SLIM RX6"},
+ {"RX INT8_1 MIX1 INP2", "RX7", "SLIM RX7"},
+ {"RX INT8_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT8_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ /* SRC0, SRC1 inputs to Sidetone RX Mixer
+ * on RX0, RX1, RX2, RX3, RX4 and RX7 chains
+ */
+ {"IIR0", NULL, "IIR0 INP0 MUX"},
+ {"IIR0 INP0 MUX", "DEC0", "ADC MUX0"},
+ {"IIR0 INP0 MUX", "DEC1", "ADC MUX1"},
+ {"IIR0 INP0 MUX", "DEC2", "ADC MUX2"},
+ {"IIR0 INP0 MUX", "DEC3", "ADC MUX3"},
+ {"IIR0 INP0 MUX", "DEC4", "ADC MUX4"},
+ {"IIR0 INP0 MUX", "DEC5", "ADC MUX5"},
+ {"IIR0 INP0 MUX", "DEC6", "ADC MUX6"},
+ {"IIR0 INP0 MUX", "DEC7", "ADC MUX7"},
+ {"IIR0 INP0 MUX", "DEC8", "ADC MUX8"},
+ {"IIR0 INP0 MUX", "RX0", "SLIM RX0"},
+ {"IIR0 INP0 MUX", "RX1", "SLIM RX1"},
+ {"IIR0 INP0 MUX", "RX2", "SLIM RX2"},
+ {"IIR0 INP0 MUX", "RX3", "SLIM RX3"},
+ {"IIR0 INP0 MUX", "RX4", "SLIM RX4"},
+ {"IIR0 INP0 MUX", "RX5", "SLIM RX5"},
+ {"IIR0 INP0 MUX", "RX6", "SLIM RX6"},
+ {"IIR0 INP0 MUX", "RX7", "SLIM RX7"},
+ {"IIR0", NULL, "IIR0 INP1 MUX"},
+ {"IIR0 INP1 MUX", "DEC0", "ADC MUX0"},
+ {"IIR0 INP1 MUX", "DEC1", "ADC MUX1"},
+ {"IIR0 INP1 MUX", "DEC2", "ADC MUX2"},
+ {"IIR0 INP1 MUX", "DEC3", "ADC MUX3"},
+ {"IIR0 INP1 MUX", "DEC4", "ADC MUX4"},
+ {"IIR0 INP1 MUX", "DEC5", "ADC MUX5"},
+ {"IIR0 INP1 MUX", "DEC6", "ADC MUX6"},
+ {"IIR0 INP1 MUX", "DEC7", "ADC MUX7"},
+ {"IIR0 INP1 MUX", "DEC8", "ADC MUX8"},
+ {"IIR0 INP1 MUX", "RX0", "SLIM RX0"},
+ {"IIR0 INP1 MUX", "RX1", "SLIM RX1"},
+ {"IIR0 INP1 MUX", "RX2", "SLIM RX2"},
+ {"IIR0 INP1 MUX", "RX3", "SLIM RX3"},
+ {"IIR0 INP1 MUX", "RX4", "SLIM RX4"},
+ {"IIR0 INP1 MUX", "RX5", "SLIM RX5"},
+ {"IIR0 INP1 MUX", "RX6", "SLIM RX6"},
+ {"IIR0 INP1 MUX", "RX7", "SLIM RX7"},
+ {"IIR0", NULL, "IIR0 INP2 MUX"},
+ {"IIR0 INP2 MUX", "DEC0", "ADC MUX0"},
+ {"IIR0 INP2 MUX", "DEC1", "ADC MUX1"},
+ {"IIR0 INP2 MUX", "DEC2", "ADC MUX2"},
+ {"IIR0 INP2 MUX", "DEC3", "ADC MUX3"},
+ {"IIR0 INP2 MUX", "DEC4", "ADC MUX4"},
+ {"IIR0 INP2 MUX", "DEC5", "ADC MUX5"},
+ {"IIR0 INP2 MUX", "DEC6", "ADC MUX6"},
+ {"IIR0 INP2 MUX", "DEC7", "ADC MUX7"},
+ {"IIR0 INP2 MUX", "DEC8", "ADC MUX8"},
+ {"IIR0 INP2 MUX", "RX0", "SLIM RX0"},
+ {"IIR0 INP2 MUX", "RX1", "SLIM RX1"},
+ {"IIR0 INP2 MUX", "RX2", "SLIM RX2"},
+ {"IIR0 INP2 MUX", "RX3", "SLIM RX3"},
+ {"IIR0 INP2 MUX", "RX4", "SLIM RX4"},
+ {"IIR0 INP2 MUX", "RX5", "SLIM RX5"},
+ {"IIR0 INP2 MUX", "RX6", "SLIM RX6"},
+ {"IIR0 INP2 MUX", "RX7", "SLIM RX7"},
+ {"IIR0", NULL, "IIR0 INP3 MUX"},
+ {"IIR0 INP3 MUX", "DEC0", "ADC MUX0"},
+ {"IIR0 INP3 MUX", "DEC1", "ADC MUX1"},
+ {"IIR0 INP3 MUX", "DEC2", "ADC MUX2"},
+ {"IIR0 INP3 MUX", "DEC3", "ADC MUX3"},
+ {"IIR0 INP3 MUX", "DEC4", "ADC MUX4"},
+ {"IIR0 INP3 MUX", "DEC5", "ADC MUX5"},
+ {"IIR0 INP3 MUX", "DEC6", "ADC MUX6"},
+ {"IIR0 INP3 MUX", "DEC7", "ADC MUX7"},
+ {"IIR0 INP3 MUX", "DEC8", "ADC MUX8"},
+ {"IIR0 INP3 MUX", "RX0", "SLIM RX0"},
+ {"IIR0 INP3 MUX", "RX1", "SLIM RX1"},
+ {"IIR0 INP3 MUX", "RX2", "SLIM RX2"},
+ {"IIR0 INP3 MUX", "RX3", "SLIM RX3"},
+ {"IIR0 INP3 MUX", "RX4", "SLIM RX4"},
+ {"IIR0 INP3 MUX", "RX5", "SLIM RX5"},
+ {"IIR0 INP3 MUX", "RX6", "SLIM RX6"},
+ {"IIR0 INP3 MUX", "RX7", "SLIM RX7"},
+
+ {"IIR1", NULL, "IIR1 INP0 MUX"},
+ {"IIR1 INP0 MUX", "DEC0", "ADC MUX0"},
+ {"IIR1 INP0 MUX", "DEC1", "ADC MUX1"},
+ {"IIR1 INP0 MUX", "DEC2", "ADC MUX2"},
+ {"IIR1 INP0 MUX", "DEC3", "ADC MUX3"},
+ {"IIR1 INP0 MUX", "DEC4", "ADC MUX4"},
+ {"IIR1 INP0 MUX", "DEC5", "ADC MUX5"},
+ {"IIR1 INP0 MUX", "DEC6", "ADC MUX6"},
+ {"IIR1 INP0 MUX", "DEC7", "ADC MUX7"},
+ {"IIR1 INP0 MUX", "DEC8", "ADC MUX8"},
+ {"IIR1 INP0 MUX", "RX0", "SLIM RX0"},
+ {"IIR1 INP0 MUX", "RX1", "SLIM RX1"},
+ {"IIR1 INP0 MUX", "RX2", "SLIM RX2"},
+ {"IIR1 INP0 MUX", "RX3", "SLIM RX3"},
+ {"IIR1 INP0 MUX", "RX4", "SLIM RX4"},
+ {"IIR1 INP0 MUX", "RX5", "SLIM RX5"},
+ {"IIR1 INP0 MUX", "RX6", "SLIM RX6"},
+ {"IIR1 INP0 MUX", "RX7", "SLIM RX7"},
+ {"IIR1", NULL, "IIR1 INP1 MUX"},
+ {"IIR1 INP1 MUX", "DEC0", "ADC MUX0"},
+ {"IIR1 INP1 MUX", "DEC1", "ADC MUX1"},
+ {"IIR1 INP1 MUX", "DEC2", "ADC MUX2"},
+ {"IIR1 INP1 MUX", "DEC3", "ADC MUX3"},
+ {"IIR1 INP1 MUX", "DEC4", "ADC MUX4"},
+ {"IIR1 INP1 MUX", "DEC5", "ADC MUX5"},
+ {"IIR1 INP1 MUX", "DEC6", "ADC MUX6"},
+ {"IIR1 INP1 MUX", "DEC7", "ADC MUX7"},
+ {"IIR1 INP1 MUX", "DEC8", "ADC MUX8"},
+ {"IIR1 INP1 MUX", "RX0", "SLIM RX0"},
+ {"IIR1 INP1 MUX", "RX1", "SLIM RX1"},
+ {"IIR1 INP1 MUX", "RX2", "SLIM RX2"},
+ {"IIR1 INP1 MUX", "RX3", "SLIM RX3"},
+ {"IIR1 INP1 MUX", "RX4", "SLIM RX4"},
+ {"IIR1 INP1 MUX", "RX5", "SLIM RX5"},
+ {"IIR1 INP1 MUX", "RX6", "SLIM RX6"},
+ {"IIR1 INP1 MUX", "RX7", "SLIM RX7"},
+ {"IIR1", NULL, "IIR1 INP2 MUX"},
+ {"IIR1 INP2 MUX", "DEC0", "ADC MUX0"},
+ {"IIR1 INP2 MUX", "DEC1", "ADC MUX1"},
+ {"IIR1 INP2 MUX", "DEC2", "ADC MUX2"},
+ {"IIR1 INP2 MUX", "DEC3", "ADC MUX3"},
+ {"IIR1 INP2 MUX", "DEC4", "ADC MUX4"},
+ {"IIR1 INP2 MUX", "DEC5", "ADC MUX5"},
+ {"IIR1 INP2 MUX", "DEC6", "ADC MUX6"},
+ {"IIR1 INP2 MUX", "DEC7", "ADC MUX7"},
+ {"IIR1 INP2 MUX", "DEC8", "ADC MUX8"},
+ {"IIR1 INP2 MUX", "RX0", "SLIM RX0"},
+ {"IIR1 INP2 MUX", "RX1", "SLIM RX1"},
+ {"IIR1 INP2 MUX", "RX2", "SLIM RX2"},
+ {"IIR1 INP2 MUX", "RX3", "SLIM RX3"},
+ {"IIR1 INP2 MUX", "RX4", "SLIM RX4"},
+ {"IIR1 INP2 MUX", "RX5", "SLIM RX5"},
+ {"IIR1 INP2 MUX", "RX6", "SLIM RX6"},
+ {"IIR1 INP2 MUX", "RX7", "SLIM RX7"},
+ {"IIR1", NULL, "IIR1 INP3 MUX"},
+ {"IIR1 INP3 MUX", "DEC0", "ADC MUX0"},
+ {"IIR1 INP3 MUX", "DEC1", "ADC MUX1"},
+ {"IIR1 INP3 MUX", "DEC2", "ADC MUX2"},
+ {"IIR1 INP3 MUX", "DEC3", "ADC MUX3"},
+ {"IIR1 INP3 MUX", "DEC4", "ADC MUX4"},
+ {"IIR1 INP3 MUX", "DEC5", "ADC MUX5"},
+ {"IIR1 INP3 MUX", "DEC6", "ADC MUX6"},
+ {"IIR1 INP3 MUX", "DEC7", "ADC MUX7"},
+ {"IIR1 INP3 MUX", "DEC8", "ADC MUX8"},
+ {"IIR1 INP3 MUX", "RX0", "SLIM RX0"},
+ {"IIR1 INP3 MUX", "RX1", "SLIM RX1"},
+ {"IIR1 INP3 MUX", "RX2", "SLIM RX2"},
+ {"IIR1 INP3 MUX", "RX3", "SLIM RX3"},
+ {"IIR1 INP3 MUX", "RX4", "SLIM RX4"},
+ {"IIR1 INP3 MUX", "RX5", "SLIM RX5"},
+ {"IIR1 INP3 MUX", "RX6", "SLIM RX6"},
+ {"IIR1 INP3 MUX", "RX7", "SLIM RX7"},
+
+ {"SRC0", NULL, "IIR0"},
+ {"SRC1", NULL, "IIR1"},
+ {"RX INT0 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT0 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT1 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT1 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT2 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT2 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT3 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT3 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT4 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT4 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT7 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT7 MIX2 INP", "SRC1", "SRC1"},
+};
+
+static int tasha_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ u16 amic_reg;
+
+ if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE"))
+ amic_reg = WCD9335_ANA_AMIC1;
+ if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE"))
+ amic_reg = WCD9335_ANA_AMIC3;
+ if (!strcmp(kcontrol->id.name, "AMIC_5_6 PWR MODE"))
+ amic_reg = WCD9335_ANA_AMIC5;
+
+ ucontrol->value.integer.value[0] =
+ (snd_soc_read(codec, amic_reg) & WCD9335_AMIC_PWR_LVL_MASK) >>
+ WCD9335_AMIC_PWR_LVL_SHIFT;
+
+ return 0;
+}
+
+static int tasha_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ u32 mode_val;
+ u16 amic_reg;
+
+ mode_val = ucontrol->value.enumerated.item[0];
+
+ dev_dbg(codec->dev, "%s: mode: %d\n",
+ __func__, mode_val);
+
+ if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE"))
+ amic_reg = WCD9335_ANA_AMIC1;
+ if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE"))
+ amic_reg = WCD9335_ANA_AMIC3;
+ if (!strcmp(kcontrol->id.name, "AMIC_5_6 PWR MODE"))
+ amic_reg = WCD9335_ANA_AMIC5;
+
+ snd_soc_update_bits(codec, amic_reg, WCD9335_AMIC_PWR_LVL_MASK,
+ mode_val << WCD9335_AMIC_PWR_LVL_SHIFT);
+
+ return 0;
+}
+
+static int tasha_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tasha->hph_mode;
+ return 0;
+}
+
+static int tasha_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u32 mode_val;
+
+ mode_val = ucontrol->value.enumerated.item[0];
+
+ dev_dbg(codec->dev, "%s: mode: %d\n",
+ __func__, mode_val);
+
+ if (mode_val == 0) {
+ dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H HiFi\n",
+ __func__);
+ mode_val = CLS_H_HIFI;
+ }
+ tasha->hph_mode = mode_val;
+ return 0;
+}
+
+static const char *const tasha_conn_mad_text[] = {
+ "NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6",
+ "NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4",
+ "DMIC5", "NOTUSED3", "NOTUSED4"
+};
+
+static const struct soc_enum tasha_conn_mad_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_conn_mad_text),
+ tasha_conn_mad_text);
+
+static int tasha_enable_ldo_h_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ u8 val = 0;
+
+ if (codec)
+ val = snd_soc_read(codec, WCD9335_LDOH_MODE) & 0x80;
+
+ ucontrol->value.integer.value[0] = !!val;
+
+ return 0;
+}
+
+static int tasha_enable_ldo_h_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int value = ucontrol->value.integer.value[0];
+ bool enable;
+
+ enable = !!value;
+ if (codec)
+ tasha_codec_enable_standalone_ldo_h(codec, enable);
+
+ return 0;
+}
+
+static int tasha_mad_input_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 tasha_mad_input;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ tasha_mad_input = snd_soc_read(codec,
+ WCD9335_SOC_MAD_INP_SEL) & 0x0F;
+ ucontrol->value.integer.value[0] = tasha_mad_input;
+
+ dev_dbg(codec->dev,
+ "%s: tasha_mad_input = %s\n", __func__,
+ tasha_conn_mad_text[tasha_mad_input]);
+ return 0;
+}
+
+static int tasha_mad_input_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 tasha_mad_input;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_card *card = codec->component.card;
+ char mad_amic_input_widget[6];
+ const char *mad_input_widget;
+ const char *source_widget = NULL;
+ u32 adc, i, mic_bias_found = 0;
+ int ret = 0;
+ char *mad_input;
+
+ tasha_mad_input = ucontrol->value.integer.value[0];
+
+ if (tasha_mad_input >= ARRAY_SIZE(tasha_conn_mad_text)) {
+ dev_err(codec->dev,
+ "%s: tasha_mad_input = %d out of bounds\n",
+ __func__, tasha_mad_input);
+ return -EINVAL;
+ }
+
+ if (!strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED1") ||
+ !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED2") ||
+ !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED3") ||
+ !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED4")) {
+ dev_err(codec->dev,
+ "%s: Unsupported tasha_mad_input = %s\n",
+ __func__, tasha_conn_mad_text[tasha_mad_input]);
+ return -EINVAL;
+ }
+
+ if (strnstr(tasha_conn_mad_text[tasha_mad_input],
+ "ADC", sizeof("ADC"))) {
+ mad_input = strpbrk(tasha_conn_mad_text[tasha_mad_input],
+ "123456");
+ if (!mad_input) {
+ dev_err(codec->dev, "%s: Invalid MAD input %s\n",
+ __func__,
+ tasha_conn_mad_text[tasha_mad_input]);
+ return -EINVAL;
+ }
+ ret = kstrtouint(mad_input, 10, &adc);
+ if ((ret < 0) || (adc > 6)) {
+ dev_err(codec->dev,
+ "%s: Invalid ADC = %s\n", __func__,
+ tasha_conn_mad_text[tasha_mad_input]);
+ ret = -EINVAL;
+ }
+
+ snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc);
+
+ mad_input_widget = mad_amic_input_widget;
+ } else {
+ /* DMIC type input widget*/
+ mad_input_widget = tasha_conn_mad_text[tasha_mad_input];
+ }
+
+ dev_dbg(codec->dev,
+ "%s: tasha input widget = %s\n", __func__,
+ mad_input_widget);
+
+ for (i = 0; i < card->num_of_dapm_routes; i++) {
+ if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) {
+ source_widget = card->of_dapm_routes[i].source;
+ if (!source_widget) {
+ dev_err(codec->dev,
+ "%s: invalid source widget\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (strnstr(source_widget,
+ "MIC BIAS1", sizeof("MIC BIAS1"))) {
+ mic_bias_found = 1;
+ break;
+ } else if (strnstr(source_widget,
+ "MIC BIAS2", sizeof("MIC BIAS2"))) {
+ mic_bias_found = 2;
+ break;
+ } else if (strnstr(source_widget,
+ "MIC BIAS3", sizeof("MIC BIAS3"))) {
+ mic_bias_found = 3;
+ break;
+ } else if (strnstr(source_widget,
+ "MIC BIAS4", sizeof("MIC BIAS4"))) {
+ mic_bias_found = 4;
+ break;
+ }
+ }
+ }
+
+ if (!mic_bias_found) {
+ dev_err(codec->dev,
+ "%s: mic bias source not found for input = %s\n",
+ __func__, mad_input_widget);
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev,
+ "%s: mic_bias found = %d\n", __func__,
+ mic_bias_found);
+
+ snd_soc_update_bits(codec, WCD9335_SOC_MAD_INP_SEL,
+ 0x0F, tasha_mad_input);
+ snd_soc_update_bits(codec, WCD9335_ANA_MAD_SETUP,
+ 0x07, mic_bias_found);
+
+ return 0;
+}
+
+static int tasha_pinctl_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ u16 ctl_reg;
+ u8 reg_val, pinctl_position;
+
+ pinctl_position = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ switch (pinctl_position >> 3) {
+ case 0:
+ ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_0;
+ break;
+ case 1:
+ ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_1;
+ break;
+ case 2:
+ ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_2;
+ break;
+ case 3:
+ ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_3;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid pinctl position = %d\n",
+ __func__, pinctl_position);
+ return -EINVAL;
+ }
+
+ reg_val = snd_soc_read(codec, ctl_reg);
+ reg_val = (reg_val >> (pinctl_position & 0x07)) & 0x1;
+ ucontrol->value.integer.value[0] = reg_val;
+
+ return 0;
+}
+
+static int tasha_pinctl_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u16 ctl_reg, cfg_reg;
+ u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask;
+
+ /* 1- high or low; 0- high Z */
+ pinctl_mode = ucontrol->value.integer.value[0];
+ pinctl_position = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ switch (pinctl_position >> 3) {
+ case 0:
+ ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_0;
+ break;
+ case 1:
+ ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_1;
+ break;
+ case 2:
+ ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_2;
+ break;
+ case 3:
+ ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_3;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid pinctl position = %d\n",
+ __func__, pinctl_position);
+ return -EINVAL;
+ }
+
+ ctl_val = pinctl_mode << (pinctl_position & 0x07);
+ mask = 1 << (pinctl_position & 0x07);
+ snd_soc_update_bits(codec, ctl_reg, mask, ctl_val);
+
+ cfg_reg = WCD9335_TLMM_BIST_MODE_PINCFG + pinctl_position;
+ if (!pinctl_mode) {
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ cfg_val = 0x4;
+ else
+ cfg_val = 0xC;
+ } else {
+ cfg_val = 0;
+ }
+ snd_soc_update_bits(codec, cfg_reg, 0x07, cfg_val);
+
+ dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n",
+ __func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val);
+
+ return 0;
+}
+
+static void wcd_vbat_adc_out_config_2_0(struct wcd_vbat *vbat,
+ struct snd_soc_codec *codec)
+{
+ u8 val1, val2;
+
+ /*
+ * Measure dcp1 by using "ALT" branch of band gap
+ * voltage(Vbg) and use it in FAST mode
+ */
+ snd_soc_update_bits(codec, WCD9335_BIAS_CTL, 0x82, 0x82);
+ snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x10);
+ snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD9335_ANA_VBADC, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9335_VBADC_SUBBLOCK_EN, 0x20, 0x00);
+
+ snd_soc_update_bits(codec, WCD9335_VBADC_FE_CTRL, 0x20, 0x20);
+ /* Wait 100 usec after calibration select as Vbg */
+ usleep_range(100, 110);
+
+ snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x40);
+ val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB);
+ val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB);
+ snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x00);
+
+ vbat->dcp1 = (((val1 & 0xFF) << 3) | (val2 & 0x07));
+
+ snd_soc_update_bits(codec, WCD9335_BIAS_CTL, 0x40, 0x40);
+ /* Wait 100 usec after selecting Vbg as 1.05V */
+ usleep_range(100, 110);
+
+ snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x40);
+ val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB);
+ val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB);
+ snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x00);
+
+ vbat->dcp2 = (((val1 & 0xFF) << 3) | (val2 & 0x07));
+
+ dev_dbg(codec->dev, "%s: dcp1:0x%x, dcp2:0x%x\n",
+ __func__, vbat->dcp1, vbat->dcp2);
+
+ snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28);
+ /* Wait 100 usec after selecting Vbg as 0.85V */
+ usleep_range(100, 110);
+
+ snd_soc_update_bits(codec, WCD9335_VBADC_FE_CTRL, 0x20, 0x00);
+ snd_soc_update_bits(codec, WCD9335_VBADC_SUBBLOCK_EN, 0x20, 0x20);
+ snd_soc_update_bits(codec, WCD9335_ANA_VBADC, 0x80, 0x00);
+
+ snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01, 0x00);
+}
+
+static void wcd_vbat_adc_out_config_1_x(struct wcd_vbat *vbat,
+ struct snd_soc_codec *codec)
+{
+ u8 val1, val2;
+
+ /*
+ * Measure dcp1 by applying band gap voltage(Vbg)
+ * of 0.85V
+ */
+ snd_soc_write(codec, WCD9335_ANA_BIAS, 0x20);
+ snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28);
+ snd_soc_write(codec, WCD9335_BIAS_VBG_FINE_ADJ, 0x05);
+ snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0);
+ /* Wait 2 sec after enabling band gap bias */
+ usleep_range(2000000, 2000100);
+
+ snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x82);
+ snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x87);
+ snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x10);
+ snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0D);
+ snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01);
+
+ snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80);
+ snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xDE);
+ snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x3C);
+ /* Wait 1 msec after calibration select as Vbg */
+ usleep_range(1000, 1100);
+
+ snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0);
+ val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB);
+ val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB);
+ snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80);
+
+ vbat->dcp1 = (((val1 & 0xFF) << 3) | (val2 & 0x07));
+
+ /*
+ * Measure dcp2 by applying band gap voltage(Vbg)
+ * of 1.05V
+ */
+ snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80);
+ snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0);
+ snd_soc_write(codec, WCD9335_BIAS_CTL, 0x68);
+ /* Wait 2 msec after selecting Vbg as 1.05V */
+ usleep_range(2000, 2100);
+
+ snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80);
+ /* Wait 1 sec after enabling band gap bias */
+ usleep_range(1000000, 1000100);
+
+ snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0);
+ val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB);
+ val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB);
+ snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80);
+
+ vbat->dcp2 = (((val1 & 0xFF) << 3) | (val2 & 0x07));
+
+ dev_dbg(codec->dev, "%s: dcp1:0x%x, dcp2:0x%x\n",
+ __func__, vbat->dcp1, vbat->dcp2);
+
+ /* Reset the Vbat ADC configuration */
+ snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80);
+ snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0);
+
+ snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28);
+ /* Wait 2 msec after selecting Vbg as 0.85V */
+ usleep_range(2000, 2100);
+
+ snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0);
+ /* Wait 1 sec after enabling band gap bias */
+ usleep_range(1000000, 1000100);
+
+ snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x1C);
+ snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xFE);
+ snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80);
+ snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00);
+
+ snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x00);
+ snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x00);
+ snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0A);
+}
+
+static void wcd_vbat_adc_out_config(struct wcd_vbat *vbat,
+ struct snd_soc_codec *codec)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ if (!vbat->adc_config) {
+ tasha_cdc_mclk_enable(codec, true, false);
+
+ if (TASHA_IS_2_0(wcd9xxx))
+ wcd_vbat_adc_out_config_2_0(vbat, codec);
+ else
+ wcd_vbat_adc_out_config_1_x(vbat, codec);
+
+ tasha_cdc_mclk_enable(codec, false, false);
+ vbat->adc_config = true;
+ }
+}
+
+static int tasha_update_vbat_reg_config(struct snd_soc_codec *codec)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct firmware_cal *hwdep_cal = NULL;
+ struct vbat_monitor_reg *vbat_reg_ptr = NULL;
+ const void *data;
+ size_t cal_size, vbat_size_remaining;
+ int ret = 0, i;
+ u32 vbat_writes_size = 0;
+ u16 reg;
+ u8 mask, val, old_val;
+
+ hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_VBAT_CAL);
+ if (hwdep_cal) {
+ data = hwdep_cal->data;
+ cal_size = hwdep_cal->size;
+ dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+ __func__);
+ } else {
+ dev_err(codec->dev, "%s: Vbat cal not received\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (cal_size < sizeof(*vbat_reg_ptr)) {
+ dev_err(codec->dev,
+ "%s: Incorrect size %zd for Vbat Cal, expected %zd\n",
+ __func__, cal_size, sizeof(*vbat_reg_ptr));
+ ret = -EINVAL;
+ goto done;
+ }
+
+ vbat_reg_ptr = (struct vbat_monitor_reg *) (data);
+
+ if (!vbat_reg_ptr) {
+ dev_err(codec->dev,
+ "%s: Invalid calibration data for Vbat\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ vbat_writes_size = vbat_reg_ptr->size;
+ vbat_size_remaining = cal_size - sizeof(u32);
+ dev_dbg(codec->dev, "%s: vbat_writes_sz: %d, vbat_sz_remaining: %zd\n",
+ __func__, vbat_writes_size, vbat_size_remaining);
+
+ if ((vbat_writes_size * TASHA_PACKED_REG_SIZE)
+ > vbat_size_remaining) {
+ pr_err("%s: Incorrect Vbat calibration data\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ for (i = 0 ; i < vbat_writes_size; i++) {
+ TASHA_CODEC_UNPACK_ENTRY(vbat_reg_ptr->writes[i],
+ reg, mask, val);
+ old_val = snd_soc_read(codec, reg);
+ snd_soc_write(codec, reg, (old_val & ~mask) | (val & mask));
+ }
+
+done:
+ return ret;
+}
+
+static int tasha_vbat_adc_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ wcd_vbat_adc_out_config(&tasha->vbat, codec);
+
+ ucontrol->value.integer.value[0] = tasha->vbat.dcp1;
+ ucontrol->value.integer.value[1] = tasha->vbat.dcp2;
+
+ dev_dbg(codec->dev,
+ "%s: Vbat ADC output values, Dcp1 : %lu, Dcp2: %lu\n",
+ __func__, ucontrol->value.integer.value[0],
+ ucontrol->value.integer.value[1]);
+
+ return 0;
+}
+
+static const char * const tasha_vbat_gsm_mode_text[] = {
+ "OFF", "ON"};
+
+static const struct soc_enum tasha_vbat_gsm_mode_enum =
+ SOC_ENUM_SINGLE_EXT(2, tasha_vbat_gsm_mode_text);
+
+static int tasha_vbat_gsm_mode_func_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ ((snd_soc_read(codec, WCD9335_CDC_VBAT_VBAT_CFG) & 0x04) ?
+ 1 : 0);
+
+ dev_dbg(codec->dev, "%s: value: %lu\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int tasha_vbat_gsm_mode_func_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ dev_dbg(codec->dev, "%s: value: %lu\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ /* Set Vbat register configuration for GSM mode bit based on value */
+ if (ucontrol->value.integer.value[0])
+ snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG,
+ 0x04, 0x04);
+ else
+ snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG,
+ 0x04, 0x00);
+
+ return 0;
+}
+
+static int tasha_codec_vbat_enable_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u16 vbat_path_ctl, vbat_cfg, vbat_path_cfg;
+
+ vbat_path_ctl = WCD9335_CDC_VBAT_VBAT_PATH_CTL;
+ vbat_cfg = WCD9335_CDC_VBAT_VBAT_CFG;
+ vbat_path_cfg = WCD9335_CDC_RX8_RX_PATH_CFG1;
+
+ if (!strcmp(w->name, "RX INT8 VBAT"))
+ vbat_path_cfg = WCD9335_CDC_RX8_RX_PATH_CFG1;
+ else if (!strcmp(w->name, "RX INT7 VBAT"))
+ vbat_path_cfg = WCD9335_CDC_RX7_RX_PATH_CFG1;
+ else if (!strcmp(w->name, "RX INT6 VBAT"))
+ vbat_path_cfg = WCD9335_CDC_RX6_RX_PATH_CFG1;
+ else if (!strcmp(w->name, "RX INT5 VBAT"))
+ vbat_path_cfg = WCD9335_CDC_RX5_RX_PATH_CFG1;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = tasha_update_vbat_reg_config(codec);
+ if (ret) {
+ dev_dbg(codec->dev,
+ "%s : VBAT isn't calibrated, So not enabling it\n",
+ __func__);
+ return 0;
+ }
+ snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80);
+ snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x02);
+ snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x10);
+ snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x01);
+ tasha->vbat.is_enabled = true;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (tasha->vbat.is_enabled) {
+ snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x00);
+ snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x00);
+ snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x00);
+ snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00);
+ tasha->vbat.is_enabled = false;
+ }
+ break;
+ };
+
+ return ret;
+}
+
+static const char * const rx_hph_mode_mux_text[] = {
+ "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI"
+};
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
+ rx_hph_mode_mux_text);
+
+static const char * const amic_pwr_lvl_text[] = {
+ "LOW_PWR", "DEFAULT", "HIGH_PERF"
+};
+
+static const struct soc_enum amic_pwr_lvl_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(amic_pwr_lvl_text),
+ amic_pwr_lvl_text);
+
+static const struct snd_kcontrol_new tasha_snd_controls[] = {
+ SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD9335_CDC_RX0_RX_VOL_CTL,
+ 0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+ SOC_SINGLE_SX_TLV("RX1 Digital Volume", WCD9335_CDC_RX1_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX2 Digital Volume", WCD9335_CDC_RX2_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX3 Digital Volume", WCD9335_CDC_RX3_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX4 Digital Volume", WCD9335_CDC_RX4_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX5 Digital Volume", WCD9335_CDC_RX5_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX6 Digital Volume", WCD9335_CDC_RX6_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX7 Digital Volume", WCD9335_CDC_RX7_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX8 Digital Volume", WCD9335_CDC_RX8_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+
+ SOC_SINGLE_SX_TLV("RX0 Mix Digital Volume",
+ WCD9335_CDC_RX0_RX_VOL_MIX_CTL,
+ 0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+ SOC_SINGLE_SX_TLV("RX1 Mix Digital Volume",
+ WCD9335_CDC_RX1_RX_VOL_MIX_CTL,
+ 0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+ SOC_SINGLE_SX_TLV("RX2 Mix Digital Volume",
+ WCD9335_CDC_RX2_RX_VOL_MIX_CTL,
+ 0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+ SOC_SINGLE_SX_TLV("RX3 Mix Digital Volume",
+ WCD9335_CDC_RX3_RX_VOL_MIX_CTL,
+ 0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+ SOC_SINGLE_SX_TLV("RX4 Mix Digital Volume",
+ WCD9335_CDC_RX4_RX_VOL_MIX_CTL,
+ 0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+ SOC_SINGLE_SX_TLV("RX5 Mix Digital Volume",
+ WCD9335_CDC_RX5_RX_VOL_MIX_CTL,
+ 0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+ SOC_SINGLE_SX_TLV("RX6 Mix Digital Volume",
+ WCD9335_CDC_RX6_RX_VOL_MIX_CTL,
+ 0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+ SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume",
+ WCD9335_CDC_RX7_RX_VOL_MIX_CTL,
+ 0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+ SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume",
+ WCD9335_CDC_RX8_RX_VOL_MIX_CTL,
+ 0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+
+ SOC_SINGLE_SX_TLV("DEC0 Volume", WCD9335_CDC_TX0_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC1 Volume", WCD9335_CDC_TX1_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC2 Volume", WCD9335_CDC_TX2_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC3 Volume", WCD9335_CDC_TX3_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC4 Volume", WCD9335_CDC_TX4_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC5 Volume", WCD9335_CDC_TX5_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC6 Volume", WCD9335_CDC_TX6_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC7 Volume", WCD9335_CDC_TX7_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC8 Volume", WCD9335_CDC_TX8_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+
+ SOC_SINGLE_SX_TLV("IIR0 INP0 Volume",
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84,
+ 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR0 INP1 Volume",
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84,
+ 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR0 INP2 Volume",
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84,
+ 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR0 INP3 Volume",
+ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84,
+ 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP0 Volume",
+ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84,
+ 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP1 Volume",
+ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84,
+ 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP2 Volume",
+ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84,
+ 40, digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP3 Volume",
+ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84,
+ 40, digital_gain),
+
+ SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tasha_get_anc_slot,
+ tasha_put_anc_slot),
+ SOC_ENUM_EXT("ANC Function", tasha_anc_func_enum, tasha_get_anc_func,
+ tasha_put_anc_func),
+
+ SOC_ENUM_EXT("CLK MODE", tasha_clkmode_enum, tasha_get_clkmode,
+ tasha_put_clkmode),
+
+ SOC_ENUM("TX0 HPF cut off", cf_dec0_enum),
+ SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
+ SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
+ SOC_ENUM("TX3 HPF cut off", cf_dec3_enum),
+ SOC_ENUM("TX4 HPF cut off", cf_dec4_enum),
+ SOC_ENUM("TX5 HPF cut off", cf_dec5_enum),
+ SOC_ENUM("TX6 HPF cut off", cf_dec6_enum),
+ SOC_ENUM("TX7 HPF cut off", cf_dec7_enum),
+ SOC_ENUM("TX8 HPF cut off", cf_dec8_enum),
+
+ SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum),
+ SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum),
+ SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum),
+ SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum),
+ SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum),
+ SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum),
+ SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum),
+ SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum),
+ SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum),
+ SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum),
+ SOC_ENUM("RX INT5_1 HPF cut off", cf_int5_1_enum),
+ SOC_ENUM("RX INT5_2 HPF cut off", cf_int5_2_enum),
+ SOC_ENUM("RX INT6_1 HPF cut off", cf_int6_1_enum),
+ SOC_ENUM("RX INT6_2 HPF cut off", cf_int6_2_enum),
+ SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum),
+ SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum),
+ SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum),
+ SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum),
+
+ SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0,
+ tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0,
+ tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0,
+ tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0,
+ tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0,
+ tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0,
+ tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0,
+ tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0,
+ tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0,
+ tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0,
+ tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+
+ SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5,
+ tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5,
+ tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5,
+ tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5,
+ tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5,
+ tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5,
+ tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5,
+ tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5,
+ tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5,
+ tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5,
+ tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+
+ SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0,
+ tasha_get_compander, tasha_set_compander),
+ SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
+ tasha_get_compander, tasha_set_compander),
+ SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0,
+ tasha_get_compander, tasha_set_compander),
+ SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0,
+ tasha_get_compander, tasha_set_compander),
+ SOC_SINGLE_EXT("COMP5 Switch", SND_SOC_NOPM, COMPANDER_5, 1, 0,
+ tasha_get_compander, tasha_set_compander),
+ SOC_SINGLE_EXT("COMP6 Switch", SND_SOC_NOPM, COMPANDER_6, 1, 0,
+ tasha_get_compander, tasha_set_compander),
+ SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0,
+ tasha_get_compander, tasha_set_compander),
+ SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0,
+ tasha_get_compander, tasha_set_compander),
+
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+ tasha_rx_hph_mode_get, tasha_rx_hph_mode_put),
+
+ SOC_ENUM_EXT("MAD Input", tasha_conn_mad_enum,
+ tasha_mad_input_get, tasha_mad_input_put),
+ SOC_SINGLE_EXT("LDO_H Enable", SND_SOC_NOPM, 0, 1, 0,
+ tasha_enable_ldo_h_get, tasha_enable_ldo_h_put),
+
+ SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0,
+ tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+
+ SOC_SINGLE_EXT("DMIC1_DATA_PIN_MODE", SND_SOC_NOPM, 18, 1, 0,
+ tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+
+ SOC_SINGLE_EXT("DMIC2_CLK_PIN_MODE", SND_SOC_NOPM, 19, 1, 0,
+ tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+
+ SOC_SINGLE_EXT("DMIC2_DATA_PIN_MODE", SND_SOC_NOPM, 20, 1, 0,
+ tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+
+ SOC_SINGLE_EXT("DMIC3_CLK_PIN_MODE", SND_SOC_NOPM, 21, 1, 0,
+ tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+
+ SOC_SINGLE_EXT("DMIC3_DATA_PIN_MODE", SND_SOC_NOPM, 22, 1, 0,
+ tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+ SOC_ENUM_EXT("AMIC_1_2 PWR MODE", amic_pwr_lvl_enum,
+ tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put),
+ SOC_ENUM_EXT("AMIC_3_4 PWR MODE", amic_pwr_lvl_enum,
+ tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put),
+ SOC_ENUM_EXT("AMIC_5_6 PWR MODE", amic_pwr_lvl_enum,
+ tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put),
+
+ SOC_SINGLE_MULTI_EXT("Vbat ADC data", SND_SOC_NOPM, 0, 0xFFFF, 0, 2,
+ tasha_vbat_adc_data_get, NULL),
+
+ SOC_ENUM_EXT("GSM mode Enable", tasha_vbat_gsm_mode_enum,
+ tasha_vbat_gsm_mode_func_get,
+ tasha_vbat_gsm_mode_func_put),
+};
+
+static int tasha_put_dec_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ u16 mic_sel_reg;
+ u8 mic_sel;
+
+ val = ucontrol->value.enumerated.item[0];
+ if (val > e->items - 1)
+ return -EINVAL;
+
+ dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__,
+ widget->name, val);
+
+ switch (e->reg) {
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1:
+ mic_sel_reg = WCD9335_CDC_TX0_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1:
+ mic_sel_reg = WCD9335_CDC_TX1_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1:
+ mic_sel_reg = WCD9335_CDC_TX2_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1:
+ mic_sel_reg = WCD9335_CDC_TX3_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0:
+ mic_sel_reg = WCD9335_CDC_TX4_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0:
+ mic_sel_reg = WCD9335_CDC_TX5_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0:
+ mic_sel_reg = WCD9335_CDC_TX6_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0:
+ mic_sel_reg = WCD9335_CDC_TX7_TX_PATH_CFG0;
+ break;
+ case WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0:
+ mic_sel_reg = WCD9335_CDC_TX8_TX_PATH_CFG0;
+ break;
+ default:
+ dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n",
+ __func__, e->reg);
+ return -EINVAL;
+ }
+
+ /* ADC: 0, DMIC: 1 */
+ mic_sel = val ? 0x0 : 0x1;
+ snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, mic_sel << 7);
+
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static int tasha_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ unsigned short look_ahead_dly_reg = WCD9335_CDC_RX0_RX_PATH_CFG0;
+
+ val = ucontrol->value.enumerated.item[0];
+ if (val >= e->items)
+ return -EINVAL;
+
+ dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__,
+ widget->name, val);
+
+ if (e->reg == WCD9335_CDC_RX0_RX_PATH_SEC0)
+ look_ahead_dly_reg = WCD9335_CDC_RX0_RX_PATH_CFG0;
+ else if (e->reg == WCD9335_CDC_RX1_RX_PATH_SEC0)
+ look_ahead_dly_reg = WCD9335_CDC_RX1_RX_PATH_CFG0;
+ else if (e->reg == WCD9335_CDC_RX2_RX_PATH_SEC0)
+ look_ahead_dly_reg = WCD9335_CDC_RX2_RX_PATH_CFG0;
+
+ /* Set Look Ahead Delay */
+ snd_soc_update_bits(codec, look_ahead_dly_reg,
+ 0x08, (val ? 0x08 : 0x00));
+ /* Set DEM INP Select */
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static int tasha_ear_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 ear_pa_gain;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ ear_pa_gain = snd_soc_read(codec, WCD9335_ANA_EAR);
+
+ ear_pa_gain = (ear_pa_gain & 0x70) >> 4;
+
+ ucontrol->value.integer.value[0] = ear_pa_gain;
+
+ dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__,
+ ear_pa_gain);
+
+ return 0;
+}
+
+static int tasha_ear_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 ear_pa_gain;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ ear_pa_gain = ucontrol->value.integer.value[0] << 4;
+
+ snd_soc_update_bits(codec, WCD9335_ANA_EAR, 0x70, ear_pa_gain);
+ return 0;
+}
+
+static int tasha_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tasha->ear_spkr_gain;
+
+ dev_dbg(codec->dev, "%s: ear_spkr_gain = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int tasha_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ tasha->ear_spkr_gain = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int tasha_config_compander(struct snd_soc_codec *codec, int interp_n,
+ int event)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int comp;
+ u16 comp_ctl0_reg, rx_path_cfg0_reg;
+
+ /* EAR does not have compander */
+ if (!interp_n)
+ return 0;
+
+ comp = interp_n - 1;
+ dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n",
+ __func__, event, comp + 1, tasha->comp_enabled[comp]);
+
+ if (!tasha->comp_enabled[comp])
+ return 0;
+
+ comp_ctl0_reg = WCD9335_CDC_COMPANDER1_CTL0 + (comp * 8);
+ rx_path_cfg0_reg = WCD9335_CDC_RX1_RX_PATH_CFG0 + (comp * 20);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Compander Clock */
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00);
+ snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04);
+ snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00);
+ }
+
+ return 0;
+}
+
+static int tasha_codec_config_mad(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ int idx;
+ const struct firmware *fw;
+ struct firmware_cal *hwdep_cal = NULL;
+ struct wcd_mad_audio_cal *mad_cal = NULL;
+ const void *data;
+ const char *filename = TASHA_MAD_AUDIO_FIRMWARE_PATH;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ size_t cal_size;
+
+ hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_MAD_CAL);
+ if (hwdep_cal) {
+ data = hwdep_cal->data;
+ cal_size = hwdep_cal->size;
+ dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+ __func__);
+ } else {
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (ret || !fw) {
+ dev_err(codec->dev,
+ "%s: MAD firmware acquire failed, err = %d\n",
+ __func__, ret);
+ return -ENODEV;
+ }
+ data = fw->data;
+ cal_size = fw->size;
+ dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+ __func__);
+ }
+
+ if (cal_size < sizeof(*mad_cal)) {
+ dev_err(codec->dev,
+ "%s: Incorrect size %zd for MAD Cal, expected %zd\n",
+ __func__, cal_size, sizeof(*mad_cal));
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ mad_cal = (struct wcd_mad_audio_cal *) (data);
+ if (!mad_cal) {
+ dev_err(codec->dev,
+ "%s: Invalid calibration data\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ snd_soc_write(codec, WCD9335_SOC_MAD_MAIN_CTL_2,
+ mad_cal->microphone_info.cycle_time);
+ snd_soc_update_bits(codec, WCD9335_SOC_MAD_MAIN_CTL_1, 0xFF << 3,
+ ((uint16_t)mad_cal->microphone_info.settle_time)
+ << 3);
+
+ /* Audio */
+ snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_8,
+ mad_cal->audio_info.rms_omit_samples);
+ snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_CTL_1,
+ 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4);
+ snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03 << 2,
+ mad_cal->audio_info.detection_mechanism << 2);
+ snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_7,
+ mad_cal->audio_info.rms_diff_threshold & 0x3F);
+ snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_5,
+ mad_cal->audio_info.rms_threshold_lsb);
+ snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_6,
+ mad_cal->audio_info.rms_threshold_msb);
+
+ for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients);
+ idx++) {
+ snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR,
+ 0x3F, idx);
+ snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL,
+ mad_cal->audio_info.iir_coefficients[idx]);
+ dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x",
+ __func__, idx,
+ mad_cal->audio_info.iir_coefficients[idx]);
+ }
+
+ /* Beacon */
+ snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_8,
+ mad_cal->beacon_info.rms_omit_samples);
+ snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_CTL_1,
+ 0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4);
+ snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_CTL_2, 0x03 << 2,
+ mad_cal->beacon_info.detection_mechanism << 2);
+ snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_7,
+ mad_cal->beacon_info.rms_diff_threshold & 0x1F);
+ snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_5,
+ mad_cal->beacon_info.rms_threshold_lsb);
+ snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_6,
+ mad_cal->beacon_info.rms_threshold_msb);
+
+ for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients);
+ idx++) {
+ snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR,
+ 0x3F, idx);
+ snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL,
+ mad_cal->beacon_info.iir_coefficients[idx]);
+ dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x",
+ __func__, idx,
+ mad_cal->beacon_info.iir_coefficients[idx]);
+ }
+
+ /* Ultrasound */
+ snd_soc_update_bits(codec, WCD9335_SOC_MAD_ULTR_CTL_1,
+ 0x07 << 4,
+ mad_cal->ultrasound_info.rms_comp_time << 4);
+ snd_soc_update_bits(codec, WCD9335_SOC_MAD_ULTR_CTL_2, 0x03 << 2,
+ mad_cal->ultrasound_info.detection_mechanism << 2);
+ snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_7,
+ mad_cal->ultrasound_info.rms_diff_threshold & 0x1F);
+ snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_5,
+ mad_cal->ultrasound_info.rms_threshold_lsb);
+ snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_6,
+ mad_cal->ultrasound_info.rms_threshold_msb);
+
+done:
+ if (!hwdep_cal)
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int tasha_codec_enable_mad(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int ret = 0;
+
+ dev_dbg(codec->dev,
+ "%s: event = %d\n", __func__, event);
+
+ /* Return if CPE INPUT is DEC1 */
+ if (snd_soc_read(codec, WCD9335_CPE_SS_SVA_CFG) & 0x01)
+ return ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+
+ /* Turn on MAD clk */
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL,
+ 0x01, 0x01);
+
+ /* Undo reset for MAD */
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL,
+ 0x02, 0x00);
+ ret = tasha_codec_config_mad(codec);
+ if (ret)
+ dev_err(codec->dev,
+ "%s: Failed to config MAD, err = %d\n",
+ __func__, ret);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Reset the MAD block */
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL,
+ 0x02, 0x02);
+ /* Turn off MAD clk */
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL,
+ 0x01, 0x00);
+ break;
+ }
+
+ return ret;
+}
+
+static int tasha_codec_configure_cpe_input(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ dev_dbg(codec->dev,
+ "%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Configure CPE input as DEC1 */
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_SVA_CFG,
+ 0x01, 0x01);
+
+ /* Configure DEC1 Tx out with sample rate as 16K */
+ snd_soc_update_bits(codec, WCD9335_CDC_TX1_TX_PATH_CTL,
+ 0x0F, 0x01);
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Reset DEC1 Tx out sample rate */
+ snd_soc_update_bits(codec, WCD9335_CDC_TX1_TX_PATH_CTL,
+ 0x0F, 0x04);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_SVA_CFG,
+ 0x01, 0x00);
+
+ break;
+ }
+
+ return 0;
+}
+
+
+static int tasha_codec_aif4_mixer_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+
+ if (test_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ dev_dbg(codec->dev, "%s: AIF4 switch value = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int tasha_codec_aif4_mixer_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_dapm_update *update = NULL;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: AIF4 switch value = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ if (ucontrol->value.integer.value[0]) {
+ snd_soc_dapm_mixer_update_power(widget->dapm,
+ kcontrol, 1, update);
+ set_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask);
+ } else {
+ snd_soc_dapm_mixer_update_power(widget->dapm,
+ kcontrol, 0, update);
+ clear_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask);
+ }
+
+ return 1;
+}
+
+static const char * const tasha_ear_pa_gain_text[] = {
+ "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB",
+ "G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB"
+};
+
+static const char * const tasha_ear_spkr_pa_gain_text[] = {
+ "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", "G_4_DB",
+ "G_5_DB", "G_6_DB"
+};
+
+static const struct soc_enum tasha_ear_pa_gain_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_pa_gain_text),
+ tasha_ear_pa_gain_text);
+
+static const struct soc_enum tasha_ear_spkr_pa_gain_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_spkr_pa_gain_text),
+ tasha_ear_spkr_pa_gain_text);
+
+static const struct snd_kcontrol_new tasha_analog_gain_controls[] = {
+ SOC_ENUM_EXT("EAR PA Gain", tasha_ear_pa_gain_enum,
+ tasha_ear_pa_gain_get, tasha_ear_pa_gain_put),
+
+ SOC_ENUM_EXT("EAR SPKR PA Gain", tasha_ear_spkr_pa_gain_enum,
+ tasha_ear_spkr_pa_gain_get, tasha_ear_spkr_pa_gain_put),
+
+ SOC_SINGLE_TLV("HPHL Volume", WCD9335_HPH_L_EN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", WCD9335_HPH_R_EN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("LINEOUT1 Volume", WCD9335_DIFF_LO_LO1_COMPANDER,
+ 3, 16, 1, line_gain),
+ SOC_SINGLE_TLV("LINEOUT2 Volume", WCD9335_DIFF_LO_LO2_COMPANDER,
+ 3, 16, 1, line_gain),
+ SOC_SINGLE_TLV("LINEOUT3 Volume", WCD9335_SE_LO_LO3_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("LINEOUT4 Volume", WCD9335_SE_LO_LO4_GAIN, 0, 20, 1,
+ line_gain),
+
+ SOC_SINGLE_TLV("ADC1 Volume", WCD9335_ANA_AMIC1, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", WCD9335_ANA_AMIC2, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", WCD9335_ANA_AMIC3, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC4 Volume", WCD9335_ANA_AMIC4, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC5 Volume", WCD9335_ANA_AMIC5, 0, 20, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC6 Volume", WCD9335_ANA_AMIC6, 0, 20, 0,
+ analog_gain),
+};
+
+static const char * const spl_src0_mux_text[] = {
+ "ZERO", "SRC_IN_HPHL", "SRC_IN_LO1",
+};
+
+static const char * const spl_src1_mux_text[] = {
+ "ZERO", "SRC_IN_HPHR", "SRC_IN_LO2",
+};
+
+static const char * const spl_src2_mux_text[] = {
+ "ZERO", "SRC_IN_LO3", "SRC_IN_SPKRL",
+};
+
+static const char * const spl_src3_mux_text[] = {
+ "ZERO", "SRC_IN_LO4", "SRC_IN_SPKRR",
+};
+
+static const char * const rx_int0_7_mix_mux_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+ "RX6", "RX7", "PROXIMITY"
+};
+
+static const char * const rx_int_mix_mux_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+ "RX6", "RX7"
+};
+
+static const char * const rx_prim_mix_text[] = {
+ "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2",
+ "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const rx_sidetone_mix_text[] = {
+ "ZERO", "SRC0", "SRC1", "SRC_SUM"
+};
+
+static const char * const sb_tx0_mux_text[] = {
+ "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192"
+};
+
+static const char * const sb_tx1_mux_text[] = {
+ "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192"
+};
+
+static const char * const sb_tx2_mux_text[] = {
+ "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192"
+};
+
+static const char * const sb_tx3_mux_text[] = {
+ "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192"
+};
+
+static const char * const sb_tx4_mux_text[] = {
+ "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192"
+};
+
+static const char * const sb_tx5_mux_text[] = {
+ "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192"
+};
+
+static const char * const sb_tx6_mux_text[] = {
+ "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192"
+};
+
+static const char * const sb_tx7_mux_text[] = {
+ "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192"
+};
+
+static const char * const sb_tx8_mux_text[] = {
+ "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192"
+};
+
+static const char * const sb_tx9_mux_text[] = {
+ "ZERO", "DEC7", "DEC7_192"
+};
+
+static const char * const sb_tx10_mux_text[] = {
+ "ZERO", "DEC6", "DEC6_192"
+};
+
+static const char * const sb_tx11_mux_text[] = {
+ "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST"
+};
+
+static const char * const sb_tx11_inp1_mux_text[] = {
+ "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4",
+ "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12"
+};
+
+static const char * const sb_tx13_mux_text[] = {
+ "ZERO", "DEC5", "DEC5_192"
+};
+
+static const char * const tx13_inp_mux_text[] = {
+ "CDC_DEC_5", "MAD_BRDCST", "CPE_TX_PP"
+};
+
+static const char * const iir_inp_mux_text[] = {
+ "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6",
+ "DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const rx_int_dem_inp_mux_text[] = {
+ "NORMAL_DSM_OUT", "CLSH_DSM_OUT",
+};
+
+static const char * const rx_int0_interp_mux_text[] = {
+ "ZERO", "RX INT0 MIX2",
+};
+
+static const char * const rx_int1_interp_mux_text[] = {
+ "ZERO", "RX INT1 MIX2",
+};
+
+static const char * const rx_int2_interp_mux_text[] = {
+ "ZERO", "RX INT2 MIX2",
+};
+
+static const char * const rx_int3_interp_mux_text[] = {
+ "ZERO", "RX INT3 MIX2",
+};
+
+static const char * const rx_int4_interp_mux_text[] = {
+ "ZERO", "RX INT4 MIX2",
+};
+
+static const char * const rx_int5_interp_mux_text[] = {
+ "ZERO", "RX INT5 MIX2",
+};
+
+static const char * const rx_int6_interp_mux_text[] = {
+ "ZERO", "RX INT6 MIX2",
+};
+
+static const char * const rx_int7_interp_mux_text[] = {
+ "ZERO", "RX INT7 MIX2",
+};
+
+static const char * const rx_int8_interp_mux_text[] = {
+ "ZERO", "RX INT8 SEC MIX"
+};
+
+static const char * const mad_sel_text[] = {
+ "SPE", "MSM"
+};
+
+static const char * const adc_mux_text[] = {
+ "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2"
+};
+
+static const char * const dmic_mux_text[] = {
+ "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5",
+ "SMIC0", "SMIC1", "SMIC2", "SMIC3"
+};
+
+static const char * const dmic_mux_alt_text[] = {
+ "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5",
+};
+
+static const char * const amic_mux_text[] = {
+ "ZERO", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6"
+};
+
+static const char * const rx_echo_mux_text[] = {
+ "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4",
+ "RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8", "RX_MIX_VBAT5",
+ "RX_MIX_VBAT6", "RX_MIX_VBAT7", "RX_MIX_VBAT8"
+};
+
+static const char * const anc0_fb_mux_text[] = {
+ "ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR",
+ "ANC_IN_LO1"
+};
+
+static const char * const anc1_fb_mux_text[] = {
+ "ZERO", "ANC_IN_HPHR", "ANC_IN_LO2"
+};
+
+static const char * const native_mux_text[] = {
+ "OFF", "ON",
+};
+
+static const struct soc_enum spl_src0_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 0, 3,
+ spl_src0_mux_text);
+
+static const struct soc_enum spl_src1_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 2, 3,
+ spl_src1_mux_text);
+
+static const struct soc_enum spl_src2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 4, 3,
+ spl_src2_mux_text);
+
+static const struct soc_enum spl_src3_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 6, 3,
+ spl_src3_mux_text);
+
+static const struct soc_enum rx_int0_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, 10,
+ rx_int0_7_mix_mux_text);
+
+static const struct soc_enum rx_int1_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int2_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int3_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int4_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int5_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int6_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int7_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, 10,
+ rx_int0_7_mix_mux_text);
+
+static const struct soc_enum rx_int8_2_mux_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9,
+ rx_int_mix_mux_text);
+
+static const struct soc_enum int1_1_native_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+ native_mux_text);
+
+static const struct soc_enum int2_1_native_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+ native_mux_text);
+
+static const struct soc_enum int3_1_native_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+ native_mux_text);
+
+static const struct soc_enum int4_1_native_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+ native_mux_text);
+
+static const struct soc_enum rx_int0_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int5_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int5_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int5_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int6_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int6_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int6_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, 13,
+ rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_sidetone_mix_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int1_sidetone_mix_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int2_sidetone_mix_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int3_sidetone_mix_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int4_sidetone_mix_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int7_sidetone_mix_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, 4,
+ rx_sidetone_mix_text);
+
+static const struct soc_enum tx_adc_mux0_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux1_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux2_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux3_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux4_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux5_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux6_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux7_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux8_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux10_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux11_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux12_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_adc_mux13_chain_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 6, 4,
+ adc_mux_text);
+
+static const struct soc_enum tx_dmic_mux0_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, 11,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, 11,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux2_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, 11,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux3_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, 11,
+ dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux4_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux5_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux6_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux7_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux8_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux10_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux11_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux12_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux13_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 3, 7,
+ dmic_mux_alt_text);
+
+static const struct soc_enum tx_amic_mux0_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux1_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux2_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux3_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux4_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux5_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux6_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux7_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux8_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux10_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux11_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux12_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum tx_amic_mux13_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0, 7,
+ amic_mux_text);
+
+static const struct soc_enum sb_tx0_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 0, 4,
+ sb_tx0_mux_text);
+
+static const struct soc_enum sb_tx1_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 2, 4,
+ sb_tx1_mux_text);
+
+static const struct soc_enum sb_tx2_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 4, 4,
+ sb_tx2_mux_text);
+
+static const struct soc_enum sb_tx3_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 6, 4,
+ sb_tx3_mux_text);
+
+static const struct soc_enum sb_tx4_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 0, 4,
+ sb_tx4_mux_text);
+
+static const struct soc_enum sb_tx5_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 2, 4,
+ sb_tx5_mux_text);
+
+static const struct soc_enum sb_tx6_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 4, 4,
+ sb_tx6_mux_text);
+
+static const struct soc_enum sb_tx7_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 6, 4,
+ sb_tx7_mux_text);
+
+static const struct soc_enum sb_tx8_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 0, 4,
+ sb_tx8_mux_text);
+
+static const struct soc_enum sb_tx9_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 2, 3,
+ sb_tx9_mux_text);
+
+static const struct soc_enum sb_tx10_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 4, 3,
+ sb_tx10_mux_text);
+
+static const struct soc_enum sb_tx11_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG, 0, 4,
+ sb_tx11_mux_text);
+
+static const struct soc_enum sb_tx11_inp1_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 0, 10,
+ sb_tx11_inp1_mux_text);
+
+static const struct soc_enum sb_tx13_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 4, 3,
+ sb_tx13_mux_text);
+
+static const struct soc_enum tx13_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, 0, 3,
+ tx13_inp_mux_text);
+
+static const struct soc_enum rx_mix_tx0_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 0, 14,
+ rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx1_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 4, 14,
+ rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx2_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 0, 14,
+ rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx3_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 4, 14,
+ rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx4_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 0, 14,
+ rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx5_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 4, 14,
+ rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx6_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 0, 14,
+ rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx7_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 4, 14,
+ rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx8_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, 14,
+ rx_echo_mux_text);
+
+static const struct soc_enum iir0_inp0_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0, 18,
+ iir_inp_mux_text);
+
+static const struct soc_enum iir0_inp1_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0, 18,
+ iir_inp_mux_text);
+
+static const struct soc_enum iir0_inp2_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0, 18,
+ iir_inp_mux_text);
+
+static const struct soc_enum iir0_inp3_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0, 18,
+ iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp0_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0, 18,
+ iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp1_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0, 18,
+ iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp2_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0, 18,
+ iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp3_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0, 18,
+ iir_inp_mux_text);
+
+static const struct soc_enum rx_int0_dem_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_SEC0, 0,
+ ARRAY_SIZE(rx_int_dem_inp_mux_text),
+ rx_int_dem_inp_mux_text);
+
+static const struct soc_enum rx_int1_dem_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_SEC0, 0,
+ ARRAY_SIZE(rx_int_dem_inp_mux_text),
+ rx_int_dem_inp_mux_text);
+
+static const struct soc_enum rx_int2_dem_inp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_SEC0, 0,
+ ARRAY_SIZE(rx_int_dem_inp_mux_text),
+ rx_int_dem_inp_mux_text);
+
+static const struct soc_enum rx_int0_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CTL, 5, 2,
+ rx_int0_interp_mux_text);
+
+static const struct soc_enum rx_int1_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CTL, 5, 2,
+ rx_int1_interp_mux_text);
+
+static const struct soc_enum rx_int2_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CTL, 5, 2,
+ rx_int2_interp_mux_text);
+
+static const struct soc_enum rx_int3_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CTL, 5, 2,
+ rx_int3_interp_mux_text);
+
+static const struct soc_enum rx_int4_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CTL, 5, 2,
+ rx_int4_interp_mux_text);
+
+static const struct soc_enum rx_int5_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CTL, 5, 2,
+ rx_int5_interp_mux_text);
+
+static const struct soc_enum rx_int6_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CTL, 5, 2,
+ rx_int6_interp_mux_text);
+
+static const struct soc_enum rx_int7_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CTL, 5, 2,
+ rx_int7_interp_mux_text);
+
+static const struct soc_enum rx_int8_interp_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CTL, 5, 2,
+ rx_int8_interp_mux_text);
+
+static const struct soc_enum mad_sel_enum =
+ SOC_ENUM_SINGLE(WCD9335_CPE_SS_CFG, 0, 2, mad_sel_text);
+
+static const struct soc_enum anc0_fb_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 0, 5,
+ anc0_fb_mux_text);
+
+static const struct soc_enum anc1_fb_mux_enum =
+ SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 3, 3,
+ anc1_fb_mux_text);
+
+static const struct snd_kcontrol_new rx_int0_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("RX INT0 DEM MUX Mux", rx_int0_dem_inp_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new rx_int1_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("RX INT1 DEM MUX Mux", rx_int1_dem_inp_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new rx_int2_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("RX INT2 DEM MUX Mux", rx_int2_dem_inp_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new spl_src0_mux =
+ SOC_DAPM_ENUM("SPL SRC0 MUX Mux", spl_src0_mux_chain_enum);
+
+static const struct snd_kcontrol_new spl_src1_mux =
+ SOC_DAPM_ENUM("SPL SRC1 MUX Mux", spl_src1_mux_chain_enum);
+
+static const struct snd_kcontrol_new spl_src2_mux =
+ SOC_DAPM_ENUM("SPL SRC2 MUX Mux", spl_src2_mux_chain_enum);
+
+static const struct snd_kcontrol_new spl_src3_mux =
+ SOC_DAPM_ENUM("SPL SRC3 MUX Mux", spl_src3_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_2_mux =
+ SOC_DAPM_ENUM("RX INT0_2 MUX Mux", rx_int0_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_2_mux =
+ SOC_DAPM_ENUM("RX INT1_2 MUX Mux", rx_int1_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_2_mux =
+ SOC_DAPM_ENUM("RX INT2_2 MUX Mux", rx_int2_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_2_mux =
+ SOC_DAPM_ENUM("RX INT3_2 MUX Mux", rx_int3_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_2_mux =
+ SOC_DAPM_ENUM("RX INT4_2 MUX Mux", rx_int4_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_2_mux =
+ SOC_DAPM_ENUM("RX INT5_2 MUX Mux", rx_int5_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_2_mux =
+ SOC_DAPM_ENUM("RX INT6_2 MUX Mux", rx_int6_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_2_mux =
+ SOC_DAPM_ENUM("RX INT7_2 MUX Mux", rx_int7_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_2_mux =
+ SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new int1_1_native_mux =
+ SOC_DAPM_ENUM("RX INT1_1 NATIVE MUX Mux", int1_1_native_enum);
+
+static const struct snd_kcontrol_new int2_1_native_mux =
+ SOC_DAPM_ENUM("RX INT2_1 NATIVE MUX Mux", int2_1_native_enum);
+
+static const struct snd_kcontrol_new int3_1_native_mux =
+ SOC_DAPM_ENUM("RX INT3_1 NATIVE MUX Mux", int3_1_native_enum);
+
+static const struct snd_kcontrol_new int4_1_native_mux =
+ SOC_DAPM_ENUM("RX INT4_1 NATIVE MUX Mux", int4_1_native_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT0_1 MIX1 INP1 Mux", rx_int0_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT0_1 MIX1 INP2 Mux", rx_int0_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT1_1 MIX1 INP0 Mux", rx_int1_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT1_1 MIX1 INP1 Mux", rx_int1_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT1_1 MIX1 INP2 Mux", rx_int1_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT2_1 MIX1 INP0 Mux", rx_int2_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT2_1 MIX1 INP1 Mux", rx_int2_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT2_1 MIX1 INP2 Mux", rx_int2_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT3_1 MIX1 INP0 Mux", rx_int3_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT3_1 MIX1 INP1 Mux", rx_int3_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT3_1 MIX1 INP2 Mux", rx_int3_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT4_1 MIX1 INP0 Mux", rx_int4_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT4_1 MIX1 INP1 Mux", rx_int4_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT4_1 MIX1 INP2 Mux", rx_int4_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT5_1 MIX1 INP0 Mux", rx_int5_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT5_1 MIX1 INP1 Mux", rx_int5_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT5_1 MIX1 INP2 Mux", rx_int5_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT6_1 MIX1 INP0 Mux", rx_int6_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT6_1 MIX1 INP1 Mux", rx_int6_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT6_1 MIX1 INP2 Mux", rx_int6_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT7_1 MIX1 INP0 Mux", rx_int7_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT7_1 MIX1 INP1 Mux", rx_int7_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT7_1 MIX1 INP2 Mux", rx_int7_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp0_mux =
+ SOC_DAPM_ENUM("RX INT8_1 MIX1 INP0 Mux", rx_int8_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp1_mux =
+ SOC_DAPM_ENUM("RX INT8_1 MIX1 INP1 Mux", rx_int8_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp2_mux =
+ SOC_DAPM_ENUM("RX INT8_1 MIX1 INP2 Mux", rx_int8_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT0 MIX2 INP Mux", rx_int0_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT1 MIX2 INP Mux", rx_int1_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT2 MIX2 INP Mux", rx_int2_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT3 MIX2 INP Mux", rx_int3_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT4 MIX2 INP Mux", rx_int4_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_mix2_inp_mux =
+ SOC_DAPM_ENUM("RX INT7 MIX2 INP Mux", rx_int7_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux0 =
+ SOC_DAPM_ENUM_EXT("ADC MUX0 Mux", tx_adc_mux0_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux1 =
+ SOC_DAPM_ENUM_EXT("ADC MUX1 Mux", tx_adc_mux1_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux2 =
+ SOC_DAPM_ENUM_EXT("ADC MUX2 Mux", tx_adc_mux2_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux3 =
+ SOC_DAPM_ENUM_EXT("ADC MUX3 Mux", tx_adc_mux3_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux4 =
+ SOC_DAPM_ENUM_EXT("ADC MUX4 Mux", tx_adc_mux4_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux5 =
+ SOC_DAPM_ENUM_EXT("ADC MUX5 Mux", tx_adc_mux5_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux6 =
+ SOC_DAPM_ENUM_EXT("ADC MUX6 Mux", tx_adc_mux6_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux7 =
+ SOC_DAPM_ENUM_EXT("ADC MUX7 Mux", tx_adc_mux7_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux8 =
+ SOC_DAPM_ENUM_EXT("ADC MUX8 Mux", tx_adc_mux8_chain_enum,
+ snd_soc_dapm_get_enum_double,
+ tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux10 =
+ SOC_DAPM_ENUM("ADC MUX10 Mux", tx_adc_mux10_chain_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux11 =
+ SOC_DAPM_ENUM("ADC MUX11 Mux", tx_adc_mux11_chain_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux12 =
+ SOC_DAPM_ENUM("ADC MUX12 Mux", tx_adc_mux12_chain_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux13 =
+ SOC_DAPM_ENUM("ADC MUX13 Mux", tx_adc_mux13_chain_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux0 =
+ SOC_DAPM_ENUM("DMIC MUX0 Mux", tx_dmic_mux0_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux1 =
+ SOC_DAPM_ENUM("DMIC MUX1 Mux", tx_dmic_mux1_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux2 =
+ SOC_DAPM_ENUM("DMIC MUX2 Mux", tx_dmic_mux2_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux3 =
+ SOC_DAPM_ENUM("DMIC MUX3 Mux", tx_dmic_mux3_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux4 =
+ SOC_DAPM_ENUM("DMIC MUX4 Mux", tx_dmic_mux4_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux5 =
+ SOC_DAPM_ENUM("DMIC MUX5 Mux", tx_dmic_mux5_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux6 =
+ SOC_DAPM_ENUM("DMIC MUX6 Mux", tx_dmic_mux6_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux7 =
+ SOC_DAPM_ENUM("DMIC MUX7 Mux", tx_dmic_mux7_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux8 =
+ SOC_DAPM_ENUM("DMIC MUX8 Mux", tx_dmic_mux8_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux10 =
+ SOC_DAPM_ENUM("DMIC MUX10 Mux", tx_dmic_mux10_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux11 =
+ SOC_DAPM_ENUM("DMIC MUX11 Mux", tx_dmic_mux11_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux12 =
+ SOC_DAPM_ENUM("DMIC MUX12 Mux", tx_dmic_mux12_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux13 =
+ SOC_DAPM_ENUM("DMIC MUX13 Mux", tx_dmic_mux13_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux0 =
+ SOC_DAPM_ENUM("AMIC MUX0 Mux", tx_amic_mux0_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux1 =
+ SOC_DAPM_ENUM("AMIC MUX1 Mux", tx_amic_mux1_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux2 =
+ SOC_DAPM_ENUM("AMIC MUX2 Mux", tx_amic_mux2_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux3 =
+ SOC_DAPM_ENUM("AMIC MUX3 Mux", tx_amic_mux3_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux4 =
+ SOC_DAPM_ENUM("AMIC MUX4 Mux", tx_amic_mux4_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux5 =
+ SOC_DAPM_ENUM("AMIC MUX5 Mux", tx_amic_mux5_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux6 =
+ SOC_DAPM_ENUM("AMIC MUX6 Mux", tx_amic_mux6_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux7 =
+ SOC_DAPM_ENUM("AMIC MUX7 Mux", tx_amic_mux7_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux8 =
+ SOC_DAPM_ENUM("AMIC MUX8 Mux", tx_amic_mux8_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux10 =
+ SOC_DAPM_ENUM("AMIC MUX10 Mux", tx_amic_mux10_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux11 =
+ SOC_DAPM_ENUM("AMIC MUX11 Mux", tx_amic_mux11_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux12 =
+ SOC_DAPM_ENUM("AMIC MUX12 Mux", tx_amic_mux12_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux13 =
+ SOC_DAPM_ENUM("AMIC MUX13 Mux", tx_amic_mux13_enum);
+
+static const struct snd_kcontrol_new sb_tx0_mux =
+ SOC_DAPM_ENUM("SLIM TX0 MUX Mux", sb_tx0_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx1_mux =
+ SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx2_mux =
+ SOC_DAPM_ENUM("SLIM TX2 MUX Mux", sb_tx2_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx3_mux =
+ SOC_DAPM_ENUM("SLIM TX3 MUX Mux", sb_tx3_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx4_mux =
+ SOC_DAPM_ENUM("SLIM TX4 MUX Mux", sb_tx4_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx5_mux =
+ SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx6_mux =
+ SOC_DAPM_ENUM("SLIM TX6 MUX Mux", sb_tx6_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx7_mux =
+ SOC_DAPM_ENUM("SLIM TX7 MUX Mux", sb_tx7_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx8_mux =
+ SOC_DAPM_ENUM("SLIM TX8 MUX Mux", sb_tx8_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx9_mux =
+ SOC_DAPM_ENUM("SLIM TX9 MUX Mux", sb_tx9_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx10_mux =
+ SOC_DAPM_ENUM("SLIM TX10 MUX Mux", sb_tx10_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx11_mux =
+ SOC_DAPM_ENUM("SLIM TX11 MUX Mux", sb_tx11_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx11_inp1_mux =
+ SOC_DAPM_ENUM("SLIM TX11 INP1 MUX Mux", sb_tx11_inp1_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx13_mux =
+ SOC_DAPM_ENUM("SLIM TX13 MUX Mux", sb_tx13_mux_enum);
+
+static const struct snd_kcontrol_new tx13_inp_mux =
+ SOC_DAPM_ENUM("TX13 INP MUX Mux", tx13_inp_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx0_mux =
+ SOC_DAPM_ENUM("RX MIX TX0 MUX Mux", rx_mix_tx0_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx1_mux =
+ SOC_DAPM_ENUM("RX MIX TX1 MUX Mux", rx_mix_tx1_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx2_mux =
+ SOC_DAPM_ENUM("RX MIX TX2 MUX Mux", rx_mix_tx2_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx3_mux =
+ SOC_DAPM_ENUM("RX MIX TX3 MUX Mux", rx_mix_tx3_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx4_mux =
+ SOC_DAPM_ENUM("RX MIX TX4 MUX Mux", rx_mix_tx4_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx5_mux =
+ SOC_DAPM_ENUM("RX MIX TX5 MUX Mux", rx_mix_tx5_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx6_mux =
+ SOC_DAPM_ENUM("RX MIX TX6 MUX Mux", rx_mix_tx6_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx7_mux =
+ SOC_DAPM_ENUM("RX MIX TX7 MUX Mux", rx_mix_tx7_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx8_mux =
+ SOC_DAPM_ENUM("RX MIX TX8 MUX Mux", rx_mix_tx8_mux_enum);
+
+static const struct snd_kcontrol_new iir0_inp0_mux =
+ SOC_DAPM_ENUM("IIR0 INP0 Mux", iir0_inp0_mux_enum);
+
+static const struct snd_kcontrol_new iir0_inp1_mux =
+ SOC_DAPM_ENUM("IIR0 INP1 Mux", iir0_inp1_mux_enum);
+
+static const struct snd_kcontrol_new iir0_inp2_mux =
+ SOC_DAPM_ENUM("IIR0 INP2 Mux", iir0_inp2_mux_enum);
+
+static const struct snd_kcontrol_new iir0_inp3_mux =
+ SOC_DAPM_ENUM("IIR0 INP3 Mux", iir0_inp3_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp0_mux =
+ SOC_DAPM_ENUM("IIR1 INP0 Mux", iir1_inp0_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp1_mux =
+ SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp2_mux =
+ SOC_DAPM_ENUM("IIR1 INP2 Mux", iir1_inp2_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp3_mux =
+ SOC_DAPM_ENUM("IIR1 INP3 Mux", iir1_inp3_mux_enum);
+
+static const struct snd_kcontrol_new rx_int0_interp_mux =
+ SOC_DAPM_ENUM("RX INT0 INTERP Mux", rx_int0_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int1_interp_mux =
+ SOC_DAPM_ENUM("RX INT1 INTERP Mux", rx_int1_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int2_interp_mux =
+ SOC_DAPM_ENUM("RX INT2 INTERP Mux", rx_int2_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int3_interp_mux =
+ SOC_DAPM_ENUM("RX INT3 INTERP Mux", rx_int3_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int4_interp_mux =
+ SOC_DAPM_ENUM("RX INT4 INTERP Mux", rx_int4_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int5_interp_mux =
+ SOC_DAPM_ENUM("RX INT5 INTERP Mux", rx_int5_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int6_interp_mux =
+ SOC_DAPM_ENUM("RX INT6 INTERP Mux", rx_int6_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int7_interp_mux =
+ SOC_DAPM_ENUM("RX INT7 INTERP Mux", rx_int7_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int8_interp_mux =
+ SOC_DAPM_ENUM("RX INT8 INTERP Mux", rx_int8_interp_mux_enum);
+
+static const struct snd_kcontrol_new mad_sel_mux =
+ SOC_DAPM_ENUM("MAD_SEL MUX Mux", mad_sel_enum);
+
+static const struct snd_kcontrol_new aif4_mad_switch =
+ SOC_DAPM_SINGLE("Switch", WCD9335_CPE_SS_CFG, 5, 1, 0);
+
+static const struct snd_kcontrol_new mad_brdcst_switch =
+ SOC_DAPM_SINGLE("Switch", WCD9335_CPE_SS_CFG, 6, 1, 0);
+
+static const struct snd_kcontrol_new aif4_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, tasha_codec_aif4_mixer_switch_get,
+ tasha_codec_aif4_mixer_switch_put);
+
+static const struct snd_kcontrol_new anc_hphl_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_hphr_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_ear_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_ear_spkr_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_lineout1_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_lineout2_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_spkr_pa_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux0_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux1_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux2_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux3_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux4_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux5_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux6_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux7_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux8_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc0_fb_mux =
+ SOC_DAPM_ENUM("ANC0 FB MUX Mux", anc0_fb_mux_enum);
+
+static const struct snd_kcontrol_new anc1_fb_mux =
+ SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum);
+
+static int tasha_codec_ec_buf_mux_enable(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ dev_dbg(codec->dev, "%s: event = %d name = %s\n",
+ __func__, event, w->name);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3B);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x08);
+ snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0,
+ 0x08, 0x08);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0,
+ 0x08, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x00);
+ snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x00);
+ break;
+ }
+
+ return 0;
+};
+
+static const char * const ec_buf_mux_text[] = {
+ "ZERO", "RXMIXEC", "SB_RX0", "SB_RX1", "SB_RX2", "SB_RX3",
+ "I2S_RX_SD0_L", "I2S_RX_SD0_R", "I2S_RX_SD1_L", "I2S_RX_SD1_R",
+ "DEC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(ec_buf_mux_enum, WCD9335_CPE_SS_US_EC_MUX_CFG,
+ 0, ec_buf_mux_text);
+
+static const struct snd_kcontrol_new ec_buf_mux =
+ SOC_DAPM_ENUM("EC BUF Mux", ec_buf_mux_enum);
+
+static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("ANC EAR"),
+ SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+ AIF1_PB, 0, tasha_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+ AIF2_PB, 0, tasha_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+ AIF3_PB, 0, tasha_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM,
+ AIF4_PB, 0, tasha_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF MIX1 PB", "AIF Mix Playback", 0,
+ SND_SOC_NOPM, AIF_MIX1_PB, 0,
+ tasha_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, TASHA_RX0, 0,
+ &slim_rx_mux[TASHA_RX0]),
+ SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TASHA_RX1, 0,
+ &slim_rx_mux[TASHA_RX1]),
+ SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TASHA_RX2, 0,
+ &slim_rx_mux[TASHA_RX2]),
+ SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TASHA_RX3, 0,
+ &slim_rx_mux[TASHA_RX3]),
+ SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TASHA_RX4, 0,
+ &slim_rx_mux[TASHA_RX4]),
+ SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TASHA_RX5, 0,
+ &slim_rx_mux[TASHA_RX5]),
+ SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TASHA_RX6, 0,
+ &slim_rx_mux[TASHA_RX6]),
+ SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TASHA_RX7, 0,
+ &slim_rx_mux[TASHA_RX7]),
+
+ SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX_E("SPL SRC0 MUX", SND_SOC_NOPM, SPLINE_SRC0, 0,
+ &spl_src0_mux, tasha_codec_enable_spline_resampler,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("SPL SRC1 MUX", SND_SOC_NOPM, SPLINE_SRC1, 0,
+ &spl_src1_mux, tasha_codec_enable_spline_resampler,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("SPL SRC2 MUX", SND_SOC_NOPM, SPLINE_SRC2, 0,
+ &spl_src2_mux, tasha_codec_enable_spline_resampler,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("SPL SRC3 MUX", SND_SOC_NOPM, SPLINE_SRC3, 0,
+ &spl_src3_mux, tasha_codec_enable_spline_resampler,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", WCD9335_CDC_RX0_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int0_2_mux, tasha_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", WCD9335_CDC_RX1_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int1_2_mux, tasha_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", WCD9335_CDC_RX2_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int2_2_mux, tasha_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", WCD9335_CDC_RX3_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int3_2_mux, tasha_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", WCD9335_CDC_RX4_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int4_2_mux, tasha_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT5_2 MUX", WCD9335_CDC_RX5_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int5_2_mux, tasha_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT6_2 MUX", WCD9335_CDC_RX6_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int6_2_mux, tasha_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", WCD9335_CDC_RX7_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int7_2_mux, tasha_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", WCD9335_CDC_RX8_RX_PATH_MIX_CTL,
+ 5, 0, &rx_int8_2_mux, tasha_codec_enable_mix_path,
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int0_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int0_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int0_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int1_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int1_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int1_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int2_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int2_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int2_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int3_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int3_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int3_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int4_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int4_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int4_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int5_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int5_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int5_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int6_1_mix_inp0_mux),
+ SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int6_1_mix_inp1_mux),
+ SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int6_1_mix_inp2_mux),
+ SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp0_mux, tasha_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp1_mux, tasha_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp2_mux, tasha_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp0_mux, tasha_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp1_mux, tasha_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp2_mux, tasha_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+ rx_int1_spline_mix_switch,
+ ARRAY_SIZE(rx_int1_spline_mix_switch)),
+ SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+ rx_int2_spline_mix_switch,
+ ARRAY_SIZE(rx_int2_spline_mix_switch)),
+ SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT3 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+ rx_int3_spline_mix_switch,
+ ARRAY_SIZE(rx_int3_spline_mix_switch)),
+ SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT4 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+ rx_int4_spline_mix_switch,
+ ARRAY_SIZE(rx_int4_spline_mix_switch)),
+ SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT5_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT5 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+ rx_int5_spline_mix_switch,
+ ARRAY_SIZE(rx_int5_spline_mix_switch)),
+ SND_SOC_DAPM_MIXER("RX INT5 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("RX INT6_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT6 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+ rx_int6_spline_mix_switch,
+ ARRAY_SIZE(rx_int6_spline_mix_switch)),
+ SND_SOC_DAPM_MIXER("RX INT6 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT7 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+ rx_int7_spline_mix_switch,
+ ARRAY_SIZE(rx_int7_spline_mix_switch)),
+
+ SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT8 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+ rx_int8_spline_mix_switch,
+ ARRAY_SIZE(rx_int8_spline_mix_switch)),
+
+ SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT5 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT6 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, tasha_codec_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, tasha_codec_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER_E("RX INT5 VBAT", SND_SOC_NOPM, 0, 0,
+ rx_int5_vbat_mix_switch,
+ ARRAY_SIZE(rx_int5_vbat_mix_switch),
+ tasha_codec_vbat_enable_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX INT6 VBAT", SND_SOC_NOPM, 0, 0,
+ rx_int6_vbat_mix_switch,
+ ARRAY_SIZE(rx_int6_vbat_mix_switch),
+ tasha_codec_vbat_enable_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX INT7 VBAT", SND_SOC_NOPM, 0, 0,
+ rx_int7_vbat_mix_switch,
+ ARRAY_SIZE(rx_int7_vbat_mix_switch),
+ tasha_codec_vbat_enable_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX INT8 VBAT", SND_SOC_NOPM, 0, 0,
+ rx_int8_vbat_mix_switch,
+ ARRAY_SIZE(rx_int8_vbat_mix_switch),
+ tasha_codec_vbat_enable_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("RX INT0 MIX2 INP", WCD9335_CDC_RX0_RX_PATH_CFG1, 4,
+ 0, &rx_int0_mix2_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT1 MIX2 INP", WCD9335_CDC_RX1_RX_PATH_CFG1, 4,
+ 0, &rx_int1_mix2_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT2 MIX2 INP", WCD9335_CDC_RX2_RX_PATH_CFG1, 4,
+ 0, &rx_int2_mix2_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT3 MIX2 INP", WCD9335_CDC_RX3_RX_PATH_CFG1, 4,
+ 0, &rx_int3_mix2_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT4 MIX2 INP", WCD9335_CDC_RX4_RX_PATH_CFG1, 4,
+ 0, &rx_int4_mix2_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT7 MIX2 INP", WCD9335_CDC_RX7_RX_PATH_CFG1, 4,
+ 0, &rx_int7_mix2_inp_mux),
+
+ SND_SOC_DAPM_MUX("SLIM TX0 MUX", SND_SOC_NOPM, TASHA_TX0, 0,
+ &sb_tx0_mux),
+ SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TASHA_TX1, 0,
+ &sb_tx1_mux),
+ SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TASHA_TX2, 0,
+ &sb_tx2_mux),
+ SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TASHA_TX3, 0,
+ &sb_tx3_mux),
+ SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TASHA_TX4, 0,
+ &sb_tx4_mux),
+ SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TASHA_TX5, 0,
+ &sb_tx5_mux),
+ SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TASHA_TX6, 0,
+ &sb_tx6_mux),
+ SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TASHA_TX7, 0,
+ &sb_tx7_mux),
+ SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TASHA_TX8, 0,
+ &sb_tx8_mux),
+ SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TASHA_TX9, 0,
+ &sb_tx9_mux),
+ SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TASHA_TX10, 0,
+ &sb_tx10_mux),
+ SND_SOC_DAPM_MUX("SLIM TX11 MUX", SND_SOC_NOPM, TASHA_TX11, 0,
+ &sb_tx11_mux),
+ SND_SOC_DAPM_MUX("SLIM TX11 INP1 MUX", SND_SOC_NOPM, TASHA_TX11, 0,
+ &sb_tx11_inp1_mux),
+ SND_SOC_DAPM_MUX("SLIM TX13 MUX", SND_SOC_NOPM, TASHA_TX13, 0,
+ &sb_tx13_mux),
+ SND_SOC_DAPM_MUX("TX13 INP MUX", SND_SOC_NOPM, 0, 0,
+ &tx13_inp_mux),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX0", WCD9335_CDC_TX0_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux0, tasha_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX1", WCD9335_CDC_TX1_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux1, tasha_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX2", WCD9335_CDC_TX2_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux2, tasha_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX3", WCD9335_CDC_TX3_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux3, tasha_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX4", WCD9335_CDC_TX4_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux4, tasha_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX5", WCD9335_CDC_TX5_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux5, tasha_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX6", WCD9335_CDC_TX6_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux6, tasha_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX7", WCD9335_CDC_TX7_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux7, tasha_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX8", WCD9335_CDC_TX8_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux8, tasha_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX10", SND_SOC_NOPM, 10, 0,
+ &tx_adc_mux10, tasha_codec_tx_adc_cfg,
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX11", SND_SOC_NOPM, 11, 0,
+ &tx_adc_mux11, tasha_codec_tx_adc_cfg,
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX12", SND_SOC_NOPM, 12, 0,
+ &tx_adc_mux12, tasha_codec_tx_adc_cfg,
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX13", SND_SOC_NOPM, 13, 0,
+ &tx_adc_mux13, tasha_codec_tx_adc_cfg,
+ SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX("DMIC MUX0", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux0),
+ SND_SOC_DAPM_MUX("DMIC MUX1", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux1),
+ SND_SOC_DAPM_MUX("DMIC MUX2", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux2),
+ SND_SOC_DAPM_MUX("DMIC MUX3", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux3),
+ SND_SOC_DAPM_MUX("DMIC MUX4", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux4),
+ SND_SOC_DAPM_MUX("DMIC MUX5", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux5),
+ SND_SOC_DAPM_MUX("DMIC MUX6", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux6),
+ SND_SOC_DAPM_MUX("DMIC MUX7", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux7),
+ SND_SOC_DAPM_MUX("DMIC MUX8", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux8),
+ SND_SOC_DAPM_MUX("DMIC MUX10", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux10),
+ SND_SOC_DAPM_MUX("DMIC MUX11", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux11),
+ SND_SOC_DAPM_MUX("DMIC MUX12", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux12),
+ SND_SOC_DAPM_MUX("DMIC MUX13", SND_SOC_NOPM, 0, 0,
+ &tx_dmic_mux13),
+
+ SND_SOC_DAPM_MUX("AMIC MUX0", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux0),
+ SND_SOC_DAPM_MUX("AMIC MUX1", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux1),
+ SND_SOC_DAPM_MUX("AMIC MUX2", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux2),
+ SND_SOC_DAPM_MUX("AMIC MUX3", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux3),
+ SND_SOC_DAPM_MUX("AMIC MUX4", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux4),
+ SND_SOC_DAPM_MUX("AMIC MUX5", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux5),
+ SND_SOC_DAPM_MUX("AMIC MUX6", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux6),
+ SND_SOC_DAPM_MUX("AMIC MUX7", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux7),
+ SND_SOC_DAPM_MUX("AMIC MUX8", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux8),
+ SND_SOC_DAPM_MUX("AMIC MUX10", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux10),
+ SND_SOC_DAPM_MUX("AMIC MUX11", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux11),
+ SND_SOC_DAPM_MUX("AMIC MUX12", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux12),
+ SND_SOC_DAPM_MUX("AMIC MUX13", SND_SOC_NOPM, 0, 0,
+ &tx_amic_mux13),
+
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD9335_ANA_AMIC1, 7, 0,
+ tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD9335_ANA_AMIC2, 7, 0,
+ tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD9335_ANA_AMIC3, 7, 0,
+ tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD9335_ANA_AMIC4, 7, 0,
+ tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC5", NULL, WCD9335_ANA_AMIC5, 7, 0,
+ tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC6", NULL, WCD9335_ANA_AMIC6, 7, 0,
+ tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY("RX INT1 NATIVE SUPPLY", SND_SOC_NOPM,
+ INTERP_HPHL, 0, tasha_enable_native_supply,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("RX INT2 NATIVE SUPPLY", SND_SOC_NOPM,
+ INTERP_HPHR, 0, tasha_enable_native_supply,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("RX INT3 NATIVE SUPPLY", SND_SOC_NOPM,
+ INTERP_LO1, 0, tasha_enable_native_supply,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("RX INT4 NATIVE SUPPLY", SND_SOC_NOPM,
+ INTERP_LO2, 0, tasha_enable_native_supply,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS2", SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS3", SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS4", SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0,
+ tasha_codec_force_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_STANDALONE, SND_SOC_NOPM, 0, 0,
+ tasha_codec_force_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_STANDALONE, SND_SOC_NOPM, 0, 0,
+ tasha_codec_force_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS4_STANDALONE, SND_SOC_NOPM, 0, 0,
+ tasha_codec_force_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY(DAPM_LDO_H_STANDALONE, SND_SOC_NOPM, 0, 0,
+ tasha_codec_force_enable_ldo_h,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("ANC0 FB MUX", SND_SOC_NOPM, 0, 0, &anc0_fb_mux),
+ SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
+
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("AMIC4"),
+ SND_SOC_DAPM_INPUT("AMIC5"),
+ SND_SOC_DAPM_INPUT("AMIC6"),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+ AIF1_CAP, 0, tasha_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+ AIF2_CAP, 0, tasha_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+ AIF3_CAP, 0, tasha_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM,
+ AIF4_VIFEED, 0, tasha_codec_enable_slimvi_feedback,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0,
+ aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+ aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+ aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+ aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)),
+
+ SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0,
+ aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)),
+
+ SND_SOC_DAPM_INPUT("VIINPUT"),
+
+ SND_SOC_DAPM_AIF_OUT("AIF5 CPE", "AIF5 CPE TX", 0, SND_SOC_NOPM,
+ AIF5_CPE_TX, 0),
+
+ SND_SOC_DAPM_MUX_E("EC BUF MUX INP", SND_SOC_NOPM, 0, 0, &ec_buf_mux,
+ tasha_codec_ec_buf_mux_enable,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Digital Mic Inputs */
+ SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("IIR0 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp0_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp1_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp2_mux),
+ SND_SOC_DAPM_MUX("IIR0 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp3_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp0_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux),
+ SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux),
+
+ SND_SOC_DAPM_MIXER_E("IIR0", WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL,
+ 4, 0, NULL, 0, tasha_codec_set_iir_gain,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER_E("IIR1", WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL,
+ 4, 0, NULL, 0, tasha_codec_set_iir_gain,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER("SRC0", WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SRC1", WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("CPE IN Mixer", SND_SOC_NOPM, 0, 0,
+ cpe_in_mix_switch,
+ ARRAY_SIZE(cpe_in_mix_switch),
+ tasha_codec_configure_cpe_input,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("RX INT1_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+ &int1_1_native_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+ &int2_1_native_mux),
+ SND_SOC_DAPM_MUX("RX INT3_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+ &int3_1_native_mux),
+ SND_SOC_DAPM_MUX("RX INT4_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+ &int4_1_native_mux),
+ SND_SOC_DAPM_MUX("RX MIX TX0 MUX", SND_SOC_NOPM, 0, 0,
+ &rx_mix_tx0_mux),
+ SND_SOC_DAPM_MUX("RX MIX TX1 MUX", SND_SOC_NOPM, 0, 0,
+ &rx_mix_tx1_mux),
+ SND_SOC_DAPM_MUX("RX MIX TX2 MUX", SND_SOC_NOPM, 0, 0,
+ &rx_mix_tx2_mux),
+ SND_SOC_DAPM_MUX("RX MIX TX3 MUX", SND_SOC_NOPM, 0, 0,
+ &rx_mix_tx3_mux),
+ SND_SOC_DAPM_MUX("RX MIX TX4 MUX", SND_SOC_NOPM, 0, 0,
+ &rx_mix_tx4_mux),
+ SND_SOC_DAPM_MUX("RX MIX TX5 MUX", SND_SOC_NOPM, 0, 0,
+ &rx_mix_tx5_mux),
+ SND_SOC_DAPM_MUX("RX MIX TX6 MUX", SND_SOC_NOPM, 0, 0,
+ &rx_mix_tx6_mux),
+ SND_SOC_DAPM_MUX("RX MIX TX7 MUX", SND_SOC_NOPM, 0, 0,
+ &rx_mix_tx7_mux),
+ SND_SOC_DAPM_MUX("RX MIX TX8 MUX", SND_SOC_NOPM, 0, 0,
+ &rx_mix_tx8_mux),
+
+ SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int0_dem_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int1_dem_inp_mux),
+ SND_SOC_DAPM_MUX("RX INT2 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int2_dem_inp_mux),
+
+ SND_SOC_DAPM_MUX_E("RX INT0 INTERP", SND_SOC_NOPM,
+ INTERP_EAR, 0, &rx_int0_interp_mux,
+ tasha_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1 INTERP", SND_SOC_NOPM,
+ INTERP_HPHL, 0, &rx_int1_interp_mux,
+ tasha_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2 INTERP", SND_SOC_NOPM,
+ INTERP_HPHR, 0, &rx_int2_interp_mux,
+ tasha_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT3 INTERP", SND_SOC_NOPM,
+ INTERP_LO1, 0, &rx_int3_interp_mux,
+ tasha_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT4 INTERP", SND_SOC_NOPM,
+ INTERP_LO2, 0, &rx_int4_interp_mux,
+ tasha_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT5 INTERP", SND_SOC_NOPM,
+ INTERP_LO3, 0, &rx_int5_interp_mux,
+ tasha_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT6 INTERP", SND_SOC_NOPM,
+ INTERP_LO4, 0, &rx_int6_interp_mux,
+ tasha_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7 INTERP", SND_SOC_NOPM,
+ INTERP_SPKR1, 0, &rx_int7_interp_mux,
+ tasha_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8 INTERP", SND_SOC_NOPM,
+ INTERP_SPKR2, 0, &rx_int8_interp_mux,
+ tasha_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tasha_codec_ear_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tasha_codec_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tasha_codec_hphr_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tasha_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tasha_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT5 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tasha_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT6 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tasha_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tasha_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tasha_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0,
+ tasha_codec_enable_ear_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD9335_ANA_LO_1_2, 7, 0, NULL, 0,
+ tasha_codec_enable_lineout_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD9335_ANA_LO_1_2, 6, 0, NULL, 0,
+ tasha_codec_enable_lineout_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT3 PA", WCD9335_ANA_LO_3_4, 7, 0, NULL, 0,
+ tasha_codec_enable_lineout_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT4 PA", WCD9335_ANA_LO_3_4, 6, 0, NULL, 0,
+ tasha_codec_enable_lineout_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0,
+ tasha_codec_enable_ear_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tasha_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tasha_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC LINEOUT1 PA", WCD9335_ANA_LO_1_2,
+ 7, 0, NULL, 0,
+ tasha_codec_enable_lineout_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC LINEOUT2 PA", WCD9335_ANA_LO_1_2,
+ 6, 0, NULL, 0,
+ tasha_codec_enable_lineout_pa,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tasha_codec_enable_spk_anc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("HPHL"),
+ SND_SOC_DAPM_OUTPUT("HPHR"),
+ SND_SOC_DAPM_OUTPUT("ANC HPHL"),
+ SND_SOC_DAPM_OUTPUT("ANC HPHR"),
+ SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("SPK1 OUT"),
+ SND_SOC_DAPM_OUTPUT("SPK2 OUT"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT3"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT4"),
+ SND_SOC_DAPM_OUTPUT("ANC LINEOUT1"),
+ SND_SOC_DAPM_OUTPUT("ANC LINEOUT2"),
+ SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM,
+ ON_DEMAND_MICBIAS, 0,
+ tasha_codec_enable_on_demand_supply,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD9335_CDC_TX0_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux0_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD9335_CDC_TX1_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux1_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX2", WCD9335_CDC_TX2_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux2_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX3", WCD9335_CDC_TX3_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux3_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX4", WCD9335_CDC_TX4_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux4_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX5", WCD9335_CDC_TX5_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux5_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX6", WCD9335_CDC_TX6_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux6_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX7", WCD9335_CDC_TX7_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux7_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD9335_CDC_TX8_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux8_switch),
+ /* MAD related widgets */
+ SND_SOC_DAPM_AIF_OUT_E("AIF4 MAD", "AIF4 MAD TX", 0,
+ SND_SOC_NOPM, 0, 0,
+ tasha_codec_enable_mad,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("MAD_SEL MUX", SND_SOC_NOPM, 0, 0,
+ &mad_sel_mux),
+ SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"),
+ SND_SOC_DAPM_INPUT("MADINPUT"),
+ SND_SOC_DAPM_SWITCH("MADONOFF", SND_SOC_NOPM, 0, 0,
+ &aif4_mad_switch),
+ SND_SOC_DAPM_SWITCH("MAD_BROADCAST", SND_SOC_NOPM, 0, 0,
+ &mad_brdcst_switch),
+ SND_SOC_DAPM_SWITCH("AIF4", SND_SOC_NOPM, 0, 0,
+ &aif4_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("ANC HPHL Enable", SND_SOC_NOPM, 0, 0,
+ &anc_hphl_switch),
+ SND_SOC_DAPM_SWITCH("ANC HPHR Enable", SND_SOC_NOPM, 0, 0,
+ &anc_hphr_switch),
+ SND_SOC_DAPM_SWITCH("ANC EAR Enable", SND_SOC_NOPM, 0, 0,
+ &anc_ear_switch),
+ SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0,
+ &anc_ear_spkr_switch),
+ SND_SOC_DAPM_SWITCH("ANC LINEOUT1 Enable", SND_SOC_NOPM, 0, 0,
+ &anc_lineout1_switch),
+ SND_SOC_DAPM_SWITCH("ANC LINEOUT2 Enable", SND_SOC_NOPM, 0, 0,
+ &anc_lineout2_switch),
+ SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0,
+ &anc_spkr_pa_switch),
+};
+
+static int tasha_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(dai->codec);
+ u32 i = 0;
+ struct wcd9xxx_ch *ch;
+
+ switch (dai->id) {
+ case AIF1_PB:
+ case AIF2_PB:
+ case AIF3_PB:
+ case AIF4_PB:
+ case AIF_MIX1_PB:
+ if (!rx_slot || !rx_num) {
+ pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n",
+ __func__, rx_slot, rx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &tasha_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: slot_num %u ch->ch_num %d\n",
+ __func__, i, ch->ch_num);
+ rx_slot[i++] = ch->ch_num;
+ }
+ pr_debug("%s: rx_num %d\n", __func__, i);
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ case AIF4_MAD_TX:
+ case AIF4_VIFEED:
+ if (!tx_slot || !tx_num) {
+ pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n",
+ __func__, tx_slot, tx_num);
+ return -EINVAL;
+ }
+ list_for_each_entry(ch, &tasha_p->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: slot_num %u ch->ch_num %d\n",
+ __func__, i, ch->ch_num);
+ tx_slot[i++] = ch->ch_num;
+ }
+ pr_debug("%s: tx_num %d\n", __func__, i);
+ *tx_num = i;
+ break;
+
+ default:
+ pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+ break;
+ }
+
+ return 0;
+}
+
+static int tasha_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct tasha_priv *tasha;
+ struct wcd9xxx *core;
+ struct wcd9xxx_codec_dai_data *dai_data = NULL;
+
+ if (!dai) {
+ pr_err("%s: dai is empty\n", __func__);
+ return -EINVAL;
+ }
+ tasha = snd_soc_codec_get_drvdata(dai->codec);
+ core = dev_get_drvdata(dai->codec->dev->parent);
+
+ if (!tx_slot || !rx_slot) {
+ pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n",
+ __func__, tx_slot, rx_slot);
+ return -EINVAL;
+ }
+ pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+ "tasha->intf_type %d\n",
+ __func__, dai->name, dai->id, tx_num, rx_num,
+ tasha->intf_type);
+
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ wcd9xxx_init_slimslave(core, core->slim->laddr,
+ tx_num, tx_slot, rx_num, rx_slot);
+ /* Reserve TX12/TX13 for MAD data channel */
+ dai_data = &tasha->dai[AIF4_MAD_TX];
+ if (dai_data) {
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
+ list_add_tail(&core->tx_chs[TASHA_TX13].list,
+ &dai_data->wcd9xxx_ch_list);
+ else
+ list_add_tail(&core->tx_chs[TASHA_TX12].list,
+ &dai_data->wcd9xxx_ch_list);
+ }
+ }
+ return 0;
+}
+
+static int tasha_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s(): substream = %s stream = %d\n" , __func__,
+ substream->name, substream->stream);
+
+ return 0;
+}
+
+static void tasha_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec);
+
+ pr_debug("%s(): substream = %s stream = %d\n" , __func__,
+ substream->name, substream->stream);
+
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C)
+ return;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tasha_codec_vote_max_bw(dai->codec, false);
+}
+
+static int tasha_set_decimator_rate(struct snd_soc_dai *dai,
+ u8 tx_fs_rate_reg_val, u32 sample_rate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u32 tx_port = 0;
+ u8 shift = 0, shift_val = 0, tx_mux_sel = 0;
+ int decimator = -1;
+ u16 tx_port_reg = 0, tx_fs_reg = 0;
+
+ list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) {
+ tx_port = ch->port;
+ dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d",
+ __func__, dai->id, tx_port);
+
+ if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) {
+ dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n",
+ __func__, tx_port, dai->id);
+ return -EINVAL;
+ }
+ /* Find the SB TX MUX input - which decimator is connected */
+ if (tx_port < 4) {
+ tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0;
+ shift = (tx_port << 1);
+ shift_val = 0x03;
+ } else if ((tx_port >= 4) && (tx_port < 8)) {
+ tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1;
+ shift = ((tx_port - 4) << 1);
+ shift_val = 0x03;
+ } else if ((tx_port >= 8) && (tx_port < 11)) {
+ tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2;
+ shift = ((tx_port - 8) << 1);
+ shift_val = 0x03;
+ } else if (tx_port == 11) {
+ tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3;
+ shift = 0;
+ shift_val = 0x0F;
+ } else if (tx_port == 13) {
+ tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3;
+ shift = 4;
+ shift_val = 0x03;
+ }
+ tx_mux_sel = snd_soc_read(codec, tx_port_reg) &
+ (shift_val << shift);
+ tx_mux_sel = tx_mux_sel >> shift;
+
+ if (tx_port <= 8) {
+ if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3))
+ decimator = tx_port;
+ } else if (tx_port <= 10) {
+ if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+ decimator = ((tx_port == 9) ? 7 : 6);
+ } else if (tx_port == 11) {
+ if ((tx_mux_sel >= 1) && (tx_mux_sel < 7))
+ decimator = tx_mux_sel - 1;
+ } else if (tx_port == 13) {
+ if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+ decimator = 5;
+ }
+
+ if (decimator >= 0) {
+ tx_fs_reg = WCD9335_CDC_TX0_TX_PATH_CTL +
+ 16 * decimator;
+ dev_dbg(codec->dev, "%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
+ __func__, decimator, tx_port, sample_rate);
+ snd_soc_update_bits(codec, tx_fs_reg, 0x0F,
+ tx_fs_rate_reg_val);
+ } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) {
+ /* Check if the TX Mux input is RX MIX TXn */
+ dev_dbg(codec->dev, "%s: RX_MIX_TX%u going to SLIM TX%u\n",
+ __func__, tx_port, tx_port);
+ } else {
+ dev_err(codec->dev, "%s: ERROR: Invalid decimator: %d\n",
+ __func__, decimator);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int tasha_set_mix_interpolator_rate(struct snd_soc_dai *dai,
+ u8 int_mix_fs_rate_reg_val,
+ u32 sample_rate)
+{
+ u8 int_2_inp;
+ u32 j;
+ u16 int_mux_cfg1, int_fs_reg;
+ u8 int_mux_cfg1_val;
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) {
+ int_2_inp = ch->port + INTn_2_INP_SEL_RX0 -
+ TASHA_RX_PORT_START_NUMBER;
+ if ((int_2_inp < INTn_2_INP_SEL_RX0) ||
+ (int_2_inp > INTn_2_INP_SEL_RX7)) {
+ pr_err("%s: Invalid RX%u port, Dai ID is %d\n",
+ __func__,
+ (ch->port - TASHA_RX_PORT_START_NUMBER),
+ dai->id);
+ return -EINVAL;
+ }
+
+ int_mux_cfg1 = WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1;
+ for (j = 0; j < TASHA_NUM_INTERPOLATORS; j++) {
+ int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) &
+ 0x0F;
+ if (int_mux_cfg1_val == int_2_inp) {
+ int_fs_reg = WCD9335_CDC_RX0_RX_PATH_MIX_CTL +
+ 20 * j;
+ pr_debug("%s: AIF_MIX_PB DAI(%d) connected to INT%u_2\n",
+ __func__, dai->id, j);
+ pr_debug("%s: set INT%u_2 sample rate to %u\n",
+ __func__, j, sample_rate);
+ snd_soc_update_bits(codec, int_fs_reg,
+ 0x0F, int_mix_fs_rate_reg_val);
+ }
+ int_mux_cfg1 += 2;
+ }
+ }
+ return 0;
+}
+
+static int tasha_set_prim_interpolator_rate(struct snd_soc_dai *dai,
+ u8 int_prim_fs_rate_reg_val,
+ u32 sample_rate)
+{
+ u8 int_1_mix1_inp;
+ u32 j;
+ u16 int_mux_cfg0, int_mux_cfg1;
+ u16 int_fs_reg;
+ u8 int_mux_cfg0_val, int_mux_cfg1_val;
+ u8 inp0_sel, inp1_sel, inp2_sel;
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) {
+ int_1_mix1_inp = ch->port + INTn_1_MIX_INP_SEL_RX0 -
+ TASHA_RX_PORT_START_NUMBER;
+ if ((int_1_mix1_inp < INTn_1_MIX_INP_SEL_RX0) ||
+ (int_1_mix1_inp > INTn_1_MIX_INP_SEL_RX7)) {
+ pr_err("%s: Invalid RX%u port, Dai ID is %d\n",
+ __func__,
+ (ch->port - TASHA_RX_PORT_START_NUMBER),
+ dai->id);
+ return -EINVAL;
+ }
+
+ int_mux_cfg0 = WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0;
+
+ /*
+ * Loop through all interpolator MUX inputs and find out
+ * to which interpolator input, the slim rx port
+ * is connected
+ */
+ for (j = 0; j < TASHA_NUM_INTERPOLATORS; j++) {
+ int_mux_cfg1 = int_mux_cfg0 + 1;
+
+ int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0);
+ int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1);
+ inp0_sel = int_mux_cfg0_val & 0x0F;
+ inp1_sel = (int_mux_cfg0_val >> 4) & 0x0F;
+ inp2_sel = (int_mux_cfg1_val >> 4) & 0x0F;
+ if ((inp0_sel == int_1_mix1_inp) ||
+ (inp1_sel == int_1_mix1_inp) ||
+ (inp2_sel == int_1_mix1_inp)) {
+ int_fs_reg = WCD9335_CDC_RX0_RX_PATH_CTL +
+ 20 * j;
+ pr_debug("%s: AIF_PB DAI(%d) connected to INT%u_1\n",
+ __func__, dai->id, j);
+ pr_debug("%s: set INT%u_1 sample rate to %u\n",
+ __func__, j, sample_rate);
+ /* sample_rate is in Hz */
+ if ((j == 0) && (sample_rate == 44100)) {
+ pr_info("%s: Cannot set 44.1KHz on INT0\n",
+ __func__);
+ } else
+ snd_soc_update_bits(codec, int_fs_reg,
+ 0x0F, int_prim_fs_rate_reg_val);
+ }
+ int_mux_cfg0 += 2;
+ }
+ }
+
+ return 0;
+}
+
+
+static int tasha_set_interpolator_rate(struct snd_soc_dai *dai,
+ u32 sample_rate)
+{
+ int rate_val = 0;
+ int i, ret;
+
+ /* set mixing path rate */
+ for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) {
+ if (sample_rate ==
+ int_mix_sample_rate_val[i].sample_rate) {
+ rate_val =
+ int_mix_sample_rate_val[i].rate_val;
+ break;
+ }
+ }
+ if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) ||
+ (rate_val < 0))
+ goto prim_rate;
+ ret = tasha_set_mix_interpolator_rate(dai,
+ (u8) rate_val, sample_rate);
+prim_rate:
+ /* set primary path sample rate */
+ for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) {
+ if (sample_rate ==
+ int_prim_sample_rate_val[i].sample_rate) {
+ rate_val =
+ int_prim_sample_rate_val[i].rate_val;
+ break;
+ }
+ }
+ if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) ||
+ (rate_val < 0))
+ return -EINVAL;
+ ret = tasha_set_prim_interpolator_rate(dai,
+ (u8) rate_val, sample_rate);
+ return ret;
+}
+
+static int tasha_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s(): substream = %s stream = %d\n" , __func__,
+ substream->name, substream->stream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tasha_codec_vote_max_bw(dai->codec, false);
+ return 0;
+}
+
+static int tasha_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec);
+ int ret;
+ int tx_fs_rate = -EINVAL;
+ int rx_fs_rate = -EINVAL;
+ int i2s_bit_mode;
+ struct snd_soc_codec *codec = dai->codec;
+
+ pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
+ dai->name, dai->id, params_rate(params),
+ params_channels(params));
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = tasha_set_interpolator_rate(dai, params_rate(params));
+ if (ret) {
+ pr_err("%s: cannot set sample rate: %u\n",
+ __func__, params_rate(params));
+ return ret;
+ }
+ switch (params_width(params)) {
+ case 16:
+ tasha->dai[dai->id].bit_width = 16;
+ i2s_bit_mode = 0x01;
+ break;
+ case 24:
+ tasha->dai[dai->id].bit_width = 24;
+ i2s_bit_mode = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+ tasha->dai[dai->id].rate = params_rate(params);
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ switch (params_rate(params)) {
+ case 8000:
+ rx_fs_rate = 0;
+ break;
+ case 16000:
+ rx_fs_rate = 1;
+ break;
+ case 32000:
+ rx_fs_rate = 2;
+ break;
+ case 48000:
+ rx_fs_rate = 3;
+ break;
+ case 96000:
+ rx_fs_rate = 4;
+ break;
+ case 192000:
+ rx_fs_rate = 5;
+ break;
+ default:
+ dev_err(tasha->dev,
+ "%s: Invalid RX sample rate: %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ };
+ snd_soc_update_bits(codec,
+ WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL,
+ 0x20, i2s_bit_mode << 5);
+ snd_soc_update_bits(codec,
+ WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL,
+ 0x1c, (rx_fs_rate << 2));
+ }
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ switch (params_rate(params)) {
+ case 8000:
+ tx_fs_rate = 0;
+ break;
+ case 16000:
+ tx_fs_rate = 1;
+ break;
+ case 32000:
+ tx_fs_rate = 3;
+ break;
+ case 48000:
+ tx_fs_rate = 4;
+ break;
+ case 96000:
+ tx_fs_rate = 5;
+ break;
+ case 192000:
+ tx_fs_rate = 6;
+ break;
+ case 384000:
+ tx_fs_rate = 7;
+ break;
+ default:
+ dev_err(tasha->dev, "%s: Invalid TX sample rate: %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+
+ };
+ if (dai->id != AIF4_VIFEED &&
+ dai->id != AIF4_MAD_TX) {
+ ret = tasha_set_decimator_rate(dai, tx_fs_rate,
+ params_rate(params));
+ if (ret < 0) {
+ dev_err(tasha->dev, "%s: cannot set TX Decimator rate: %d\n",
+ __func__, tx_fs_rate);
+ return ret;
+ }
+ }
+ tasha->dai[dai->id].rate = params_rate(params);
+ switch (params_width(params)) {
+ case 16:
+ tasha->dai[dai->id].bit_width = 16;
+ i2s_bit_mode = 0x01;
+ break;
+ case 24:
+ tasha->dai[dai->id].bit_width = 24;
+ i2s_bit_mode = 0x00;
+ break;
+ case 32:
+ tasha->dai[dai->id].bit_width = 32;
+ i2s_bit_mode = 0x00;
+ break;
+ default:
+ dev_err(tasha->dev, "%s: Invalid format 0x%x\n",
+ __func__, params_width(params));
+ return -EINVAL;
+ };
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ snd_soc_update_bits(codec,
+ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL,
+ 0x20, i2s_bit_mode << 5);
+ if (tx_fs_rate > 1)
+ tx_fs_rate--;
+ snd_soc_update_bits(codec,
+ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL,
+ 0x1c, tx_fs_rate << 2);
+ snd_soc_update_bits(codec,
+ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG,
+ 0x05, 0x05);
+
+ snd_soc_update_bits(codec,
+ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG,
+ 0x05, 0x05);
+
+ snd_soc_update_bits(codec,
+ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG,
+ 0x05, 0x05);
+
+ snd_soc_update_bits(codec,
+ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG,
+ 0x05, 0x05);
+ }
+ break;
+ default:
+ pr_err("%s: Invalid stream type %d\n", __func__,
+ substream->stream);
+ return -EINVAL;
+ };
+ if (dai->id == AIF4_VIFEED)
+ tasha->dai[dai->id].bit_width = 32;
+
+ return 0;
+}
+
+static int tasha_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* CPU is master */
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ if (dai->id == AIF1_CAP)
+ snd_soc_update_bits(dai->codec,
+ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL,
+ 0x2, 0);
+ else if (dai->id == AIF1_PB)
+ snd_soc_update_bits(dai->codec,
+ WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL,
+ 0x2, 0);
+ }
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* CPU is slave */
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ if (dai->id == AIF1_CAP)
+ snd_soc_update_bits(dai->codec,
+ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL,
+ 0x2, 0x2);
+ else if (dai->id == AIF1_PB)
+ snd_soc_update_bits(dai->codec,
+ WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL,
+ 0x2, 0x2);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int tasha_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static struct snd_soc_dai_ops tasha_dai_ops = {
+ .startup = tasha_startup,
+ .shutdown = tasha_shutdown,
+ .hw_params = tasha_hw_params,
+ .prepare = tasha_prepare,
+ .set_sysclk = tasha_set_dai_sysclk,
+ .set_fmt = tasha_set_dai_fmt,
+ .set_channel_map = tasha_set_channel_map,
+ .get_channel_map = tasha_get_channel_map,
+};
+
+static struct snd_soc_dai_driver tasha_dai[] = {
+ {
+ .name = "tasha_rx1",
+ .id = AIF1_PB,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_tx1",
+ .id = AIF1_CAP,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .rates = WCD9335_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_rx2",
+ .id = AIF2_PB,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_tx2",
+ .id = AIF2_CAP,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .rates = WCD9335_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_rx3",
+ .id = AIF3_PB,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_tx3",
+ .id = AIF3_CAP,
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .rates = WCD9335_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_rx4",
+ .id = AIF4_PB,
+ .playback = {
+ .stream_name = "AIF4 Playback",
+ .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_mix_rx1",
+ .id = AIF_MIX1_PB,
+ .playback = {
+ .stream_name = "AIF Mix Playback",
+ .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_mad1",
+ .id = AIF4_MAD_TX,
+ .capture = {
+ .stream_name = "AIF4 MAD TX",
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000,
+ .formats = TASHA_FORMATS_S16_S24_S32_LE,
+ .rate_min = 16000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_vifeedback",
+ .id = AIF4_VIFEED,
+ .capture = {
+ .stream_name = "VIfeed",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+ .formats = TASHA_FORMATS_S16_S24_S32_LE,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_cpe",
+ .id = AIF5_CPE_TX,
+ .capture = {
+ .stream_name = "AIF5 CPE TX",
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
+ .formats = TASHA_FORMATS_S16_S24_S32_LE,
+ .rate_min = 16000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ },
+};
+
+static struct snd_soc_dai_driver tasha_i2s_dai[] = {
+ {
+ .name = "tasha_i2s_rx1",
+ .id = AIF1_PB,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .rates = WCD9335_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_i2s_tx1",
+ .id = AIF1_CAP,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .rates = WCD9335_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_i2s_rx2",
+ .id = AIF2_PB,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .rates = WCD9335_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tasha_dai_ops,
+ },
+ {
+ .name = "tasha_i2s_tx2",
+ .id = AIF2_CAP,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .rates = WCD9335_RATES_MASK,
+ .formats = TASHA_FORMATS_S16_S24_LE,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tasha_dai_ops,
+ },
+};
+
+static void tasha_codec_power_gate_digital_core(struct tasha_priv *tasha)
+{
+ struct snd_soc_codec *codec = tasha->codec;
+
+ if (!codec)
+ return;
+
+ mutex_lock(&tasha->power_lock);
+ dev_dbg(codec->dev, "%s: Entering power gating function, %d\n",
+ __func__, tasha->power_active_ref);
+
+ if (tasha->power_active_ref > 0)
+ goto exit;
+
+ wcd9xxx_set_power_state(tasha->wcd9xxx,
+ WCD_REGION_POWER_COLLAPSE_BEGIN,
+ WCD9XXX_DIG_CORE_REGION_1);
+ snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x02, 0x00);
+ clear_bit(AUDIO_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha, sido_buck_svs_voltage);
+ wcd9xxx_set_power_state(tasha->wcd9xxx, WCD_REGION_POWER_DOWN,
+ WCD9XXX_DIG_CORE_REGION_1);
+exit:
+ dev_dbg(codec->dev, "%s: Exiting power gating function, %d\n",
+ __func__, tasha->power_active_ref);
+ mutex_unlock(&tasha->power_lock);
+}
+
+static void tasha_codec_power_gate_work(struct work_struct *work)
+{
+ struct tasha_priv *tasha;
+ struct delayed_work *dwork;
+ struct snd_soc_codec *codec;
+
+ dwork = to_delayed_work(work);
+ tasha = container_of(dwork, struct tasha_priv, power_gate_work);
+ codec = tasha->codec;
+
+ if (!codec)
+ return;
+
+ tasha_codec_power_gate_digital_core(tasha);
+}
+
+/* called under power_lock acquisition */
+static int tasha_dig_core_remove_power_collapse(struct snd_soc_codec *codec)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ tasha_codec_vote_max_bw(codec, true);
+ snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5);
+ snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7);
+ snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3);
+ snd_soc_update_bits(codec, WCD9335_CODEC_RPM_RST_CTL, 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CODEC_RPM_RST_CTL, 0x02, 0x02);
+
+ wcd9xxx_set_power_state(tasha->wcd9xxx,
+ WCD_REGION_POWER_COLLAPSE_REMOVE,
+ WCD9XXX_DIG_CORE_REGION_1);
+ regcache_mark_dirty(codec->component.regmap);
+ regcache_sync_region(codec->component.regmap,
+ TASHA_DIG_CORE_REG_MIN, TASHA_DIG_CORE_REG_MAX);
+ tasha_codec_vote_max_bw(codec, false);
+
+ return 0;
+}
+
+static int tasha_dig_core_power_collapse(struct tasha_priv *tasha,
+ int req_state)
+{
+ struct snd_soc_codec *codec;
+ int cur_state;
+
+ /* Exit if feature is disabled */
+ if (!dig_core_collapse_enable)
+ return 0;
+
+ mutex_lock(&tasha->power_lock);
+ if (req_state == POWER_COLLAPSE)
+ tasha->power_active_ref--;
+ else if (req_state == POWER_RESUME)
+ tasha->power_active_ref++;
+ else
+ goto unlock_mutex;
+
+ if (tasha->power_active_ref < 0) {
+ dev_info(tasha->dev,
+ "%s: power_active_ref is negative, resetting it\n",
+ __func__);
+ tasha->power_active_ref = 0;
+ goto unlock_mutex;
+ }
+
+ codec = tasha->codec;
+ if (!codec)
+ goto unlock_mutex;
+
+ if (req_state == POWER_COLLAPSE) {
+ if (tasha->power_active_ref == 0) {
+ schedule_delayed_work(&tasha->power_gate_work,
+ msecs_to_jiffies(dig_core_collapse_timer * 1000));
+ }
+ } else if (req_state == POWER_RESUME) {
+ if (tasha->power_active_ref == 1) {
+ /*
+ * At this point, there can be two cases:
+ * 1. Core already in power collapse state
+ * 2. Timer kicked in and still did not expire or
+ * waiting for the power_lock
+ */
+ cur_state = wcd9xxx_get_current_power_state(
+ tasha->wcd9xxx,
+ WCD9XXX_DIG_CORE_REGION_1);
+ if (cur_state == WCD_REGION_POWER_DOWN)
+ tasha_dig_core_remove_power_collapse(codec);
+ else {
+ mutex_unlock(&tasha->power_lock);
+ cancel_delayed_work_sync(
+ &tasha->power_gate_work);
+ mutex_lock(&tasha->power_lock);
+ }
+ }
+ }
+
+unlock_mutex:
+ mutex_unlock(&tasha->power_lock);
+
+ return 0;
+}
+
+static int __tasha_cdc_mclk_enable_locked(struct tasha_priv *tasha,
+ bool enable)
+{
+ int ret = 0;
+
+ if (!tasha->wcd_ext_clk) {
+ dev_err(tasha->dev, "%s: wcd ext clock is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(tasha->dev, "%s: mclk_enable = %u\n", __func__, enable);
+
+ if (enable) {
+ tasha_dig_core_power_collapse(tasha, POWER_RESUME);
+ ret = tasha_cdc_req_mclk_enable(tasha, true);
+ if (ret)
+ goto err;
+
+ set_bit(AUDIO_NOMINAL, &tasha->status_mask);
+ tasha_codec_apply_sido_voltage(tasha,
+ SIDO_VOLTAGE_NOMINAL_MV);
+ } else {
+ if (!dig_core_collapse_enable) {
+ clear_bit(AUDIO_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha,
+ sido_buck_svs_voltage);
+ }
+ tasha_cdc_req_mclk_enable(tasha, false);
+ tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE);
+ }
+
+err:
+ return ret;
+}
+
+static int __tasha_cdc_mclk_enable(struct tasha_priv *tasha,
+ bool enable)
+{
+ int ret;
+
+ WCD9XXX_V2_BG_CLK_LOCK(tasha->resmgr);
+ ret = __tasha_cdc_mclk_enable_locked(tasha, enable);
+ WCD9XXX_V2_BG_CLK_UNLOCK(tasha->resmgr);
+
+ return ret;
+}
+
+int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, bool dapm)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ return __tasha_cdc_mclk_enable(tasha, enable);
+}
+EXPORT_SYMBOL(tasha_cdc_mclk_enable);
+
+int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, bool dapm)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ dev_dbg(tasha->dev, "%s: clk_mode: %d, enable: %d, clk_internal: %d\n",
+ __func__, tasha->clk_mode, enable, tasha->clk_internal);
+ if (tasha->clk_mode || tasha->clk_internal) {
+ if (enable) {
+ tasha_cdc_sido_ccl_enable(tasha, true);
+ wcd_resmgr_enable_master_bias(tasha->resmgr);
+ tasha_dig_core_power_collapse(tasha, POWER_RESUME);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x01, 0x01);
+ set_bit(CPE_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha,
+ SIDO_VOLTAGE_NOMINAL_MV);
+ tasha->clk_internal = true;
+ } else {
+ tasha->clk_internal = false;
+ clear_bit(CPE_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha,
+ sido_buck_svs_voltage);
+ tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE);
+ wcd_resmgr_disable_master_bias(tasha->resmgr);
+ tasha_cdc_sido_ccl_enable(tasha, false);
+ }
+ } else {
+ ret = __tasha_cdc_mclk_enable(tasha, enable);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(tasha_cdc_mclk_tx_enable);
+
+static ssize_t tasha_codec_version_read(struct snd_info_entry *entry,
+ void *file_private_data, struct file *file,
+ char __user *buf, size_t count, loff_t pos)
+{
+ struct tasha_priv *tasha;
+ struct wcd9xxx *wcd9xxx;
+ char buffer[TASHA_VERSION_ENTRY_SIZE];
+ int len = 0;
+
+ tasha = (struct tasha_priv *) entry->private_data;
+ if (!tasha) {
+ pr_err("%s: tasha priv is null\n", __func__);
+ return -EINVAL;
+ }
+
+ wcd9xxx = tasha->wcd9xxx;
+
+ if (wcd9xxx->codec_type->id_major == TASHA_MAJOR) {
+ if (TASHA_IS_1_0(wcd9xxx))
+ len = snprintf(buffer, sizeof(buffer), "WCD9335_1_0\n");
+ else if (TASHA_IS_1_1(wcd9xxx))
+ len = snprintf(buffer, sizeof(buffer), "WCD9335_1_1\n");
+ else
+ snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n");
+ } else if (wcd9xxx->codec_type->id_major == TASHA2P0_MAJOR) {
+ len = snprintf(buffer, sizeof(buffer), "WCD9335_2_0\n");
+ } else
+ len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n");
+
+ return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static struct snd_info_entry_ops tasha_codec_info_ops = {
+ .read = tasha_codec_version_read,
+};
+
+/*
+ * tasha_codec_info_create_codec_entry - creates wcd9335 module
+ * @codec_root: The parent directory
+ * @codec: Codec instance
+ *
+ * Creates wcd9335 module and version entry under the given
+ * parent directory.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int tasha_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+ struct snd_soc_codec *codec)
+{
+ struct snd_info_entry *version_entry;
+ struct tasha_priv *tasha;
+ struct snd_soc_card *card;
+
+ if (!codec_root || !codec)
+ return -EINVAL;
+
+ tasha = snd_soc_codec_get_drvdata(codec);
+ card = codec->component.card;
+ tasha->entry = snd_register_module_info(codec_root->module,
+ "tasha",
+ codec_root);
+ if (!tasha->entry) {
+ dev_dbg(codec->dev, "%s: failed to create wcd9335 entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry = snd_info_create_card_entry(card->snd_card,
+ "version",
+ tasha->entry);
+ if (!version_entry) {
+ dev_dbg(codec->dev, "%s: failed to create wcd9335 version entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry->private_data = tasha;
+ version_entry->size = TASHA_VERSION_ENTRY_SIZE;
+ version_entry->content = SNDRV_INFO_CONTENT_DATA;
+ version_entry->c.ops = &tasha_codec_info_ops;
+
+ if (snd_info_register(version_entry) < 0) {
+ snd_info_free_entry(version_entry);
+ return -ENOMEM;
+ }
+ tasha->version_entry = version_entry;
+
+ return 0;
+}
+EXPORT_SYMBOL(tasha_codec_info_create_codec_entry);
+
+static int __tasha_codec_internal_rco_ctrl(
+ struct snd_soc_codec *codec, bool enable)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if (enable) {
+ tasha_cdc_sido_ccl_enable(tasha, true);
+ if (wcd_resmgr_get_clk_type(tasha->resmgr) ==
+ WCD_CLK_RCO) {
+ ret = wcd_resmgr_enable_clk_block(tasha->resmgr,
+ WCD_CLK_RCO);
+ } else {
+ ret = tasha_cdc_req_mclk_enable(tasha, true);
+ ret |= wcd_resmgr_enable_clk_block(tasha->resmgr,
+ WCD_CLK_RCO);
+ ret |= tasha_cdc_req_mclk_enable(tasha, false);
+ }
+
+ } else {
+ ret = wcd_resmgr_disable_clk_block(tasha->resmgr,
+ WCD_CLK_RCO);
+ tasha_cdc_sido_ccl_enable(tasha, false);
+ }
+
+ if (ret) {
+ dev_err(codec->dev, "%s: Error in %s RCO\n",
+ __func__, (enable ? "enabling" : "disabling"));
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/*
+ * tasha_codec_internal_rco_ctrl()
+ * Make sure that the caller does not acquire
+ * BG_CLK_LOCK.
+ */
+static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+ bool enable)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ WCD9XXX_V2_BG_CLK_LOCK(tasha->resmgr);
+ ret = __tasha_codec_internal_rco_ctrl(codec, enable);
+ WCD9XXX_V2_BG_CLK_UNLOCK(tasha->resmgr);
+ return ret;
+}
+
+/*
+ * tasha_mbhc_hs_detect: starts mbhc insertion/removal functionality
+ * @codec: handle to snd_soc_codec *
+ * @mbhc_cfg: handle to mbhc configuration structure
+ * return 0 if mbhc_start is success or error code in case of failure
+ */
+int tasha_mbhc_hs_detect(struct snd_soc_codec *codec,
+ struct wcd_mbhc_config *mbhc_cfg)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ return wcd_mbhc_start(&tasha->mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL(tasha_mbhc_hs_detect);
+
+/*
+ * tasha_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality
+ * @codec: handle to snd_soc_codec *
+ */
+void tasha_mbhc_hs_detect_exit(struct snd_soc_codec *codec)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ wcd_mbhc_stop(&tasha->mbhc);
+}
+EXPORT_SYMBOL(tasha_mbhc_hs_detect_exit);
+
+static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv)
+{
+ /* min micbias voltage is 1V and maximum is 2.85V */
+ if (micb_mv < 1000 || micb_mv > 2850) {
+ pr_err("%s: unsupported micbias voltage\n", __func__);
+ return -EINVAL;
+ }
+
+ return (micb_mv - 1000) / 50;
+}
+
+static const struct tasha_reg_mask_val tasha_reg_update_reset_val_1_1[] = {
+ {WCD9335_RCO_CTRL_2, 0xFF, 0x47},
+ {WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0xFF, 0x60},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_init_val_1_1[] = {
+ {WCD9335_FLYBACK_VNEG_DAC_CTRL_1, 0xFF, 0x65},
+ {WCD9335_FLYBACK_VNEG_DAC_CTRL_2, 0xFF, 0x52},
+ {WCD9335_FLYBACK_VNEG_DAC_CTRL_3, 0xFF, 0xAF},
+ {WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0xFF, 0x60},
+ {WCD9335_FLYBACK_VNEG_CTRL_3, 0xFF, 0xF4},
+ {WCD9335_FLYBACK_VNEG_CTRL_9, 0xFF, 0x40},
+ {WCD9335_FLYBACK_VNEG_CTRL_2, 0xFF, 0x4F},
+ {WCD9335_FLYBACK_EN, 0xFF, 0x6E},
+ {WCD9335_CDC_RX2_RX_PATH_SEC0, 0xF8, 0xF8},
+ {WCD9335_CDC_RX1_RX_PATH_SEC0, 0xF8, 0xF8},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_init_val_1_0[] = {
+ {WCD9335_FLYBACK_VNEG_CTRL_3, 0xFF, 0x54},
+ {WCD9335_CDC_RX2_RX_PATH_SEC0, 0xFC, 0xFC},
+ {WCD9335_CDC_RX1_RX_PATH_SEC0, 0xFC, 0xFC},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_init_val_2_0[] = {
+ {WCD9335_RCO_CTRL_2, 0x0F, 0x08},
+ {WCD9335_RX_BIAS_FLYB_MID_RST, 0xF0, 0x10},
+ {WCD9335_FLYBACK_CTRL_1, 0x20, 0x20},
+ {WCD9335_HPH_OCP_CTL, 0xFF, 0x7A},
+ {WCD9335_HPH_L_TEST, 0x01, 0x01},
+ {WCD9335_HPH_R_TEST, 0x01, 0x01},
+ {WCD9335_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12},
+ {WCD9335_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08},
+ {WCD9335_CDC_COMPANDER7_CTL7, 0x1E, 0x18},
+ {WCD9335_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12},
+ {WCD9335_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08},
+ {WCD9335_CDC_COMPANDER8_CTL7, 0x1E, 0x18},
+ {WCD9335_CDC_TX0_TX_PATH_SEC7, 0xFF, 0x45},
+ {WCD9335_CDC_RX0_RX_PATH_SEC0, 0xFC, 0xF4},
+ {WCD9335_HPH_REFBUFF_LP_CTL, 0x08, 0x08},
+ {WCD9335_HPH_REFBUFF_LP_CTL, 0x06, 0x02},
+ {WCD9335_DIFF_LO_CORE_OUT_PROG, 0xFC, 0xA0},
+ {WCD9335_SE_LO_COM1, 0xFF, 0xC0},
+ {WCD9335_CDC_RX3_RX_PATH_SEC0, 0xFC, 0xF4},
+ {WCD9335_CDC_RX4_RX_PATH_SEC0, 0xFC, 0xF4},
+ {WCD9335_CDC_RX5_RX_PATH_SEC0, 0xFC, 0xF8},
+ {WCD9335_CDC_RX6_RX_PATH_SEC0, 0xFC, 0xF8},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_defaults[] = {
+ {WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x00},
+ {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x01},
+ {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x04, 0x04},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_i2c_defaults[] = {
+ {WCD9335_ANA_CLK_TOP, 0x20, 0x20},
+ {WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x01},
+ {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x00},
+ {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x05, 0x05},
+ {WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG, 0x01, 0x01},
+ {WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG, 0x01, 0x01},
+ {WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG, 0x01, 0x01},
+ {WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG, 0x01, 0x01},
+ {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG, 0x05, 0x05},
+ {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG, 0x05, 0x05},
+ {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG, 0x05, 0x05},
+ {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG, 0x05, 0x05},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_init_common_val[] = {
+ /* Rbuckfly/R_EAR(32) */
+ {WCD9335_CDC_CLSH_K2_MSB, 0x0F, 0x00},
+ {WCD9335_CDC_CLSH_K2_LSB, 0xFF, 0x60},
+ {WCD9335_CPE_SS_DMIC_CFG, 0x80, 0x00},
+ {WCD9335_CDC_BOOST0_BOOST_CTL, 0x70, 0x50},
+ {WCD9335_CDC_BOOST1_BOOST_CTL, 0x70, 0x50},
+ {WCD9335_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08},
+ {WCD9335_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08},
+ {WCD9335_ANA_LO_1_2, 0x3C, 0X3C},
+ {WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x70, 0x00},
+ {WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03, 0x03},
+ {WCD9335_CDC_TOP_TOP_CFG1, 0x02, 0x02},
+ {WCD9335_CDC_TOP_TOP_CFG1, 0x01, 0x01},
+ {WCD9335_EAR_CMBUFF, 0x08, 0x00},
+ {WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80},
+ {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80},
+ {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01},
+ {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01},
+ {WCD9335_CDC_RX0_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX1_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX2_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX3_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX4_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX5_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX6_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX7_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX8_RX_PATH_CFG0, 0x01, 0x01},
+ {WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x01, 0x01},
+ {WCD9335_VBADC_IBIAS_FE, 0x0C, 0x08},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_init_1_x_val[] = {
+ /* Enable TX HPF Filter & Linear Phase */
+ {WCD9335_CDC_TX0_TX_PATH_CFG0, 0x11, 0x11},
+ {WCD9335_CDC_TX1_TX_PATH_CFG0, 0x11, 0x11},
+ {WCD9335_CDC_TX2_TX_PATH_CFG0, 0x11, 0x11},
+ {WCD9335_CDC_TX3_TX_PATH_CFG0, 0x11, 0x11},
+ {WCD9335_CDC_TX4_TX_PATH_CFG0, 0x11, 0x11},
+ {WCD9335_CDC_TX5_TX_PATH_CFG0, 0x11, 0x11},
+ {WCD9335_CDC_TX6_TX_PATH_CFG0, 0x11, 0x11},
+ {WCD9335_CDC_TX7_TX_PATH_CFG0, 0x11, 0x11},
+ {WCD9335_CDC_TX8_TX_PATH_CFG0, 0x11, 0x11},
+ {WCD9335_CDC_RX0_RX_PATH_SEC0, 0xF8, 0xF8},
+ {WCD9335_CDC_RX0_RX_PATH_SEC1, 0x08, 0x08},
+ {WCD9335_CDC_RX1_RX_PATH_SEC1, 0x08, 0x08},
+ {WCD9335_CDC_RX2_RX_PATH_SEC1, 0x08, 0x08},
+ {WCD9335_CDC_RX3_RX_PATH_SEC1, 0x08, 0x08},
+ {WCD9335_CDC_RX4_RX_PATH_SEC1, 0x08, 0x08},
+ {WCD9335_CDC_RX5_RX_PATH_SEC1, 0x08, 0x08},
+ {WCD9335_CDC_RX6_RX_PATH_SEC1, 0x08, 0x08},
+ {WCD9335_CDC_RX7_RX_PATH_SEC1, 0x08, 0x08},
+ {WCD9335_CDC_RX8_RX_PATH_SEC1, 0x08, 0x08},
+ {WCD9335_CDC_RX0_RX_PATH_MIX_SEC0, 0x08, 0x08},
+ {WCD9335_CDC_RX1_RX_PATH_MIX_SEC0, 0x08, 0x08},
+ {WCD9335_CDC_RX2_RX_PATH_MIX_SEC0, 0x08, 0x08},
+ {WCD9335_CDC_RX3_RX_PATH_MIX_SEC0, 0x08, 0x08},
+ {WCD9335_CDC_RX4_RX_PATH_MIX_SEC0, 0x08, 0x08},
+ {WCD9335_CDC_RX5_RX_PATH_MIX_SEC0, 0x08, 0x08},
+ {WCD9335_CDC_RX6_RX_PATH_MIX_SEC0, 0x08, 0x08},
+ {WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, 0x08, 0x08},
+ {WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x08, 0x08},
+ {WCD9335_CDC_TX0_TX_PATH_SEC2, 0x01, 0x01},
+ {WCD9335_CDC_TX1_TX_PATH_SEC2, 0x01, 0x01},
+ {WCD9335_CDC_TX2_TX_PATH_SEC2, 0x01, 0x01},
+ {WCD9335_CDC_TX3_TX_PATH_SEC2, 0x01, 0x01},
+ {WCD9335_CDC_TX4_TX_PATH_SEC2, 0x01, 0x01},
+ {WCD9335_CDC_TX5_TX_PATH_SEC2, 0x01, 0x01},
+ {WCD9335_CDC_TX6_TX_PATH_SEC2, 0x01, 0x01},
+ {WCD9335_CDC_TX7_TX_PATH_SEC2, 0x01, 0x01},
+ {WCD9335_CDC_TX8_TX_PATH_SEC2, 0x01, 0x01},
+ {WCD9335_CDC_RX3_RX_PATH_SEC0, 0xF8, 0xF0},
+ {WCD9335_CDC_RX4_RX_PATH_SEC0, 0xF8, 0xF0},
+ {WCD9335_CDC_RX5_RX_PATH_SEC0, 0xF8, 0xF8},
+ {WCD9335_CDC_RX6_RX_PATH_SEC0, 0xF8, 0xF8},
+ {WCD9335_RX_OCP_COUNT, 0xFF, 0xFF},
+ {WCD9335_HPH_OCP_CTL, 0xF0, 0x70},
+ {WCD9335_CPE_SS_CPAR_CFG, 0xFF, 0x00},
+ {WCD9335_FLYBACK_VNEG_CTRL_1, 0xFF, 0x63},
+ {WCD9335_FLYBACK_VNEG_CTRL_4, 0xFF, 0x7F},
+ {WCD9335_CLASSH_CTRL_VCL_1, 0xFF, 0x60},
+ {WCD9335_CLASSH_CTRL_CCL_5, 0xFF, 0x40},
+ {WCD9335_RX_TIMER_DIV, 0xFF, 0x32},
+ {WCD9335_SE_LO_COM2, 0xFF, 0x01},
+ {WCD9335_MBHC_ZDET_ANA_CTL, 0x0F, 0x07},
+ {WCD9335_RX_BIAS_HPH_PA, 0xF0, 0x60},
+ {WCD9335_HPH_RDAC_LDO_CTL, 0x88, 0x88},
+ {WCD9335_HPH_L_EN, 0x20, 0x20},
+ {WCD9335_HPH_R_EN, 0x20, 0x20},
+ {WCD9335_DIFF_LO_CORE_OUT_PROG, 0xFC, 0xD8},
+ {WCD9335_CDC_RX5_RX_PATH_SEC3, 0xBD, 0xBD},
+ {WCD9335_CDC_RX6_RX_PATH_SEC3, 0xBD, 0xBD},
+ {WCD9335_DIFF_LO_COM_PA_FREQ, 0x70, 0x40},
+};
+
+static void tasha_update_reg_reset_values(struct snd_soc_codec *codec)
+{
+ u32 i;
+ struct wcd9xxx *tasha_core = dev_get_drvdata(codec->dev->parent);
+
+ if (TASHA_IS_1_1(tasha_core)) {
+ for (i = 0; i < ARRAY_SIZE(tasha_reg_update_reset_val_1_1);
+ i++)
+ snd_soc_write(codec,
+ tasha_reg_update_reset_val_1_1[i].reg,
+ tasha_reg_update_reset_val_1_1[i].val);
+ }
+}
+
+static void tasha_codec_init_reg(struct snd_soc_codec *codec)
+{
+ u32 i;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_common_val); i++)
+ snd_soc_update_bits(codec,
+ tasha_codec_reg_init_common_val[i].reg,
+ tasha_codec_reg_init_common_val[i].mask,
+ tasha_codec_reg_init_common_val[i].val);
+
+ if (TASHA_IS_1_1(wcd9xxx) ||
+ TASHA_IS_1_0(wcd9xxx))
+ for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_1_x_val); i++)
+ snd_soc_update_bits(codec,
+ tasha_codec_reg_init_1_x_val[i].reg,
+ tasha_codec_reg_init_1_x_val[i].mask,
+ tasha_codec_reg_init_1_x_val[i].val);
+
+ if (TASHA_IS_1_1(wcd9xxx)) {
+ for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_1); i++)
+ snd_soc_update_bits(codec,
+ tasha_codec_reg_init_val_1_1[i].reg,
+ tasha_codec_reg_init_val_1_1[i].mask,
+ tasha_codec_reg_init_val_1_1[i].val);
+ } else if (TASHA_IS_1_0(wcd9xxx)) {
+ for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_0); i++)
+ snd_soc_update_bits(codec,
+ tasha_codec_reg_init_val_1_0[i].reg,
+ tasha_codec_reg_init_val_1_0[i].mask,
+ tasha_codec_reg_init_val_1_0[i].val);
+ } else if (TASHA_IS_2_0(wcd9xxx)) {
+ for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_2_0); i++)
+ snd_soc_update_bits(codec,
+ tasha_codec_reg_init_val_2_0[i].reg,
+ tasha_codec_reg_init_val_2_0[i].mask,
+ tasha_codec_reg_init_val_2_0[i].val);
+ }
+}
+
+static void tasha_update_reg_defaults(struct tasha_priv *tasha)
+{
+ u32 i;
+ struct wcd9xxx *wcd9xxx;
+
+ wcd9xxx = tasha->wcd9xxx;
+ for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_defaults); i++)
+ regmap_update_bits(wcd9xxx->regmap,
+ tasha_codec_reg_defaults[i].reg,
+ tasha_codec_reg_defaults[i].mask,
+ tasha_codec_reg_defaults[i].val);
+
+ tasha->intf_type = wcd9xxx_get_intf_type();
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C)
+ for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_i2c_defaults); i++)
+ regmap_update_bits(wcd9xxx->regmap,
+ tasha_codec_reg_i2c_defaults[i].reg,
+ tasha_codec_reg_i2c_defaults[i].mask,
+ tasha_codec_reg_i2c_defaults[i].val);
+
+ return;
+}
+
+static void tasha_slim_interface_init_reg(struct snd_soc_codec *codec)
+{
+ int i;
+ struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
+ wcd9xxx_interface_reg_write(priv->wcd9xxx,
+ TASHA_SLIM_PGD_PORT_INT_EN0 + i,
+ 0xFF);
+}
+
+static irqreturn_t tasha_slimbus_irq(int irq, void *data)
+{
+ struct tasha_priv *priv = data;
+ unsigned long status = 0;
+ int i, j, port_id, k;
+ u32 bit;
+ u8 val, int_val = 0;
+ bool tx, cleared;
+ unsigned short reg = 0;
+
+ for (i = TASHA_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0;
+ i <= TASHA_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) {
+ val = wcd9xxx_interface_reg_read(priv->wcd9xxx, i);
+ status |= ((u32)val << (8 * j));
+ }
+
+ for_each_set_bit(j, &status, 32) {
+ tx = (j >= 16 ? true : false);
+ port_id = (tx ? j - 16 : j);
+ val = wcd9xxx_interface_reg_read(priv->wcd9xxx,
+ TASHA_SLIM_PGD_PORT_INT_RX_SOURCE0 + j);
+ if (val) {
+ if (!tx)
+ reg = TASHA_SLIM_PGD_PORT_INT_EN0 +
+ (port_id / 8);
+ else
+ reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 +
+ (port_id / 8);
+ int_val = wcd9xxx_interface_reg_read(
+ priv->wcd9xxx, reg);
+ /*
+ * Ignore interrupts for ports for which the
+ * interrupts are not specifically enabled.
+ */
+ if (!(int_val & (1 << (port_id % 8))))
+ continue;
+ }
+ if (val & TASHA_SLIM_IRQ_OVERFLOW)
+ pr_err_ratelimited(
+ "%s: overflow error on %s port %d, value %x\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val);
+ if (val & TASHA_SLIM_IRQ_UNDERFLOW)
+ pr_err_ratelimited(
+ "%s: underflow error on %s port %d, value %x\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val);
+ if ((val & TASHA_SLIM_IRQ_OVERFLOW) ||
+ (val & TASHA_SLIM_IRQ_UNDERFLOW)) {
+ if (!tx)
+ reg = TASHA_SLIM_PGD_PORT_INT_EN0 +
+ (port_id / 8);
+ else
+ reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 +
+ (port_id / 8);
+ int_val = wcd9xxx_interface_reg_read(
+ priv->wcd9xxx, reg);
+ if (int_val & (1 << (port_id % 8))) {
+ int_val = int_val ^ (1 << (port_id % 8));
+ wcd9xxx_interface_reg_write(priv->wcd9xxx,
+ reg, int_val);
+ }
+ }
+ if (val & TASHA_SLIM_IRQ_PORT_CLOSED) {
+ /*
+ * INT SOURCE register starts from RX to TX
+ * but port number in the ch_mask is in opposite way
+ */
+ bit = (tx ? j - 16 : j + 16);
+ pr_debug("%s: %s port %d closed value %x, bit %u\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val,
+ bit);
+ for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) {
+ pr_debug("%s: priv->dai[%d].ch_mask = 0x%lx\n",
+ __func__, k, priv->dai[k].ch_mask);
+ if (test_and_clear_bit(bit,
+ &priv->dai[k].ch_mask)) {
+ cleared = true;
+ if (!priv->dai[k].ch_mask)
+ wake_up(&priv->dai[k].dai_wait);
+ /*
+ * There are cases when multiple DAIs
+ * might be using the same slimbus
+ * channel. Hence don't break here.
+ */
+ }
+ }
+ WARN(!cleared,
+ "Couldn't find slimbus %s port %d for closing\n",
+ (tx ? "TX" : "RX"), port_id);
+ }
+ wcd9xxx_interface_reg_write(priv->wcd9xxx,
+ TASHA_SLIM_PGD_PORT_INT_CLR_RX_0 +
+ (j / 8),
+ 1 << (j % 8));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int tasha_setup_irqs(struct tasha_priv *tasha)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = tasha->codec;
+ struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+
+ ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS,
+ tasha_slimbus_irq, "SLIMBUS Slave", tasha);
+ if (ret)
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_SLIMBUS);
+ else
+ tasha_slim_interface_init_reg(codec);
+
+ return ret;
+}
+
+static void tasha_init_slim_slave_cfg(struct snd_soc_codec *codec)
+{
+ struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct afe_param_cdc_slimbus_slave_cfg *cfg;
+ struct wcd9xxx *wcd9xxx = priv->wcd9xxx;
+ uint64_t eaddr = 0;
+
+ cfg = &priv->slimbus_slave_cfg;
+ cfg->minor_version = 1;
+ cfg->tx_slave_port_offset = 0;
+ cfg->rx_slave_port_offset = 16;
+
+ memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr));
+ WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6);
+ cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF;
+ cfg->device_enum_addr_msw = eaddr >> 32;
+
+ dev_dbg(codec->dev, "%s: slimbus logical address 0x%llx\n",
+ __func__, eaddr);
+}
+
+static void tasha_cleanup_irqs(struct tasha_priv *tasha)
+{
+ struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+
+ wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tasha);
+}
+
+static int tasha_handle_pdata(struct tasha_priv *tasha,
+ struct wcd9xxx_pdata *pdata)
+{
+ struct snd_soc_codec *codec = tasha->codec;
+ u8 dmic_ctl_val, mad_dmic_ctl_val;
+ u8 anc_ctl_value;
+ u32 def_dmic_rate, dmic_clk_drv;
+ int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4;
+ int rc = 0;
+
+ if (!pdata) {
+ dev_err(codec->dev, "%s: NULL pdata\n", __func__);
+ return -ENODEV;
+ }
+
+ /* set micbias voltage */
+ vout_ctl_1 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb1_mv);
+ vout_ctl_2 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb2_mv);
+ vout_ctl_3 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb3_mv);
+ vout_ctl_4 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb4_mv);
+
+ if (IS_ERR_VALUE(vout_ctl_1) || IS_ERR_VALUE(vout_ctl_2) ||
+ IS_ERR_VALUE(vout_ctl_3) || IS_ERR_VALUE(vout_ctl_4)) {
+ rc = -EINVAL;
+ goto done;
+ }
+ snd_soc_update_bits(codec, WCD9335_ANA_MICB1, 0x3F, vout_ctl_1);
+ snd_soc_update_bits(codec, WCD9335_ANA_MICB2, 0x3F, vout_ctl_2);
+ snd_soc_update_bits(codec, WCD9335_ANA_MICB3, 0x3F, vout_ctl_3);
+ snd_soc_update_bits(codec, WCD9335_ANA_MICB4, 0x3F, vout_ctl_4);
+
+ /* Set the DMIC sample rate */
+ switch (pdata->mclk_rate) {
+ case TASHA_MCLK_CLK_9P6MHZ:
+ def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+ break;
+ case TASHA_MCLK_CLK_12P288MHZ:
+ def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ;
+ break;
+ default:
+ /* should never happen */
+ dev_err(codec->dev, "%s: Invalid mclk_rate %d\n",
+ __func__, pdata->mclk_rate);
+ rc = -EINVAL;
+ goto done;
+ };
+
+ if (pdata->dmic_sample_rate ==
+ WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+ dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n",
+ __func__, def_dmic_rate);
+ pdata->dmic_sample_rate = def_dmic_rate;
+ }
+ if (pdata->mad_dmic_sample_rate ==
+ WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+ dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n",
+ __func__, def_dmic_rate);
+ /*
+ * use dmic_sample_rate as the default for MAD
+ * if mad dmic sample rate is undefined
+ */
+ pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate;
+ }
+ if (pdata->ecpp_dmic_sample_rate ==
+ WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+ dev_info(codec->dev,
+ "%s: ecpp_dmic_rate invalid default = %d\n",
+ __func__, def_dmic_rate);
+ /*
+ * use dmic_sample_rate as the default for ECPP DMIC
+ * if ecpp dmic sample rate is undefined
+ */
+ pdata->ecpp_dmic_sample_rate = pdata->dmic_sample_rate;
+ }
+
+ if (pdata->dmic_clk_drv ==
+ WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) {
+ pdata->dmic_clk_drv = WCD9335_DMIC_CLK_DRIVE_DEFAULT;
+ dev_info(codec->dev,
+ "%s: dmic_clk_strength invalid, default = %d\n",
+ __func__, pdata->dmic_clk_drv);
+ }
+
+ switch (pdata->dmic_clk_drv) {
+ case 2:
+ dmic_clk_drv = 0;
+ break;
+ case 4:
+ dmic_clk_drv = 1;
+ break;
+ case 8:
+ dmic_clk_drv = 2;
+ break;
+ case 16:
+ dmic_clk_drv = 3;
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: invalid dmic_clk_drv %d, using default\n",
+ __func__, pdata->dmic_clk_drv);
+ dmic_clk_drv = 0;
+ break;
+ }
+
+ snd_soc_update_bits(codec, WCD9335_TEST_DEBUG_PAD_DRVCTL,
+ 0x0C, dmic_clk_drv << 2);
+
+ /*
+ * Default the DMIC clk rates to mad_dmic_sample_rate,
+ * whereas, the anc/txfe dmic rates to dmic_sample_rate
+ * since the anc/txfe are independent of mad block.
+ */
+ mad_dmic_ctl_val = tasha_get_dmic_clk_val(tasha->codec,
+ pdata->mclk_rate,
+ pdata->mad_dmic_sample_rate);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC0_CTL,
+ 0x0E, mad_dmic_ctl_val << 1);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC1_CTL,
+ 0x0E, mad_dmic_ctl_val << 1);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC2_CTL,
+ 0x0E, mad_dmic_ctl_val << 1);
+
+ dmic_ctl_val = tasha_get_dmic_clk_val(tasha->codec,
+ pdata->mclk_rate,
+ pdata->dmic_sample_rate);
+
+ if (dmic_ctl_val == WCD9335_DMIC_CLK_DIV_2)
+ anc_ctl_value = WCD9335_ANC_DMIC_X2_FULL_RATE;
+ else
+ anc_ctl_value = WCD9335_ANC_DMIC_X2_HALF_RATE;
+
+ snd_soc_update_bits(codec, WCD9335_CDC_ANC0_MODE_2_CTL,
+ 0x40, anc_ctl_value << 6);
+ snd_soc_update_bits(codec, WCD9335_CDC_ANC0_MODE_2_CTL,
+ 0x20, anc_ctl_value << 5);
+ snd_soc_update_bits(codec, WCD9335_CDC_ANC1_MODE_2_CTL,
+ 0x40, anc_ctl_value << 6);
+ snd_soc_update_bits(codec, WCD9335_CDC_ANC1_MODE_2_CTL,
+ 0x20, anc_ctl_value << 5);
+done:
+ return rc;
+}
+
+static struct wcd_cpe_core *tasha_codec_get_cpe_core(
+ struct snd_soc_codec *codec)
+{
+ struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+ return priv->cpe_core;
+}
+
+static int tasha_codec_cpe_fll_update_divider(
+ struct snd_soc_codec *codec, u32 cpe_fll_rate)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u32 div_val = 0, l_val = 0;
+ u32 computed_cpe_fll;
+
+ if (cpe_fll_rate != CPE_FLL_CLK_75MHZ &&
+ cpe_fll_rate != CPE_FLL_CLK_150MHZ) {
+ dev_err(codec->dev,
+ "%s: Invalid CPE fll rate request %u\n",
+ __func__, cpe_fll_rate);
+ return -EINVAL;
+ }
+
+ if (wcd9xxx->mclk_rate == TASHA_MCLK_CLK_12P288MHZ) {
+ /* update divider to 10 and enable 5x divider */
+ snd_soc_write(codec, WCD9335_CPE_FLL_USER_CTL_1,
+ 0x55);
+ div_val = 10;
+ } else if (wcd9xxx->mclk_rate == TASHA_MCLK_CLK_9P6MHZ) {
+ /* update divider to 8 and enable 2x divider */
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0,
+ 0x7C, 0x70);
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_1,
+ 0xE0, 0x20);
+ div_val = 8;
+ } else {
+ dev_err(codec->dev,
+ "%s: Invalid MCLK rate %u\n",
+ __func__, wcd9xxx->mclk_rate);
+ return -EINVAL;
+ }
+
+ l_val = ((cpe_fll_rate / 1000) * div_val) /
+ (wcd9xxx->mclk_rate / 1000);
+
+ /* If l_val was integer truncated, increment l_val once */
+ computed_cpe_fll = (wcd9xxx->mclk_rate / div_val) * l_val;
+ if (computed_cpe_fll < cpe_fll_rate)
+ l_val++;
+
+
+ /* update L value LSB and MSB */
+ snd_soc_write(codec, WCD9335_CPE_FLL_L_VAL_CTL_0,
+ (l_val & 0xFF));
+ snd_soc_write(codec, WCD9335_CPE_FLL_L_VAL_CTL_1,
+ ((l_val >> 8) & 0xFF));
+
+ tasha->current_cpe_clk_freq = cpe_fll_rate;
+ dev_dbg(codec->dev,
+ "%s: updated l_val to %u for cpe_clk %u and mclk %u\n",
+ __func__, l_val, cpe_fll_rate, wcd9xxx->mclk_rate);
+
+ return 0;
+}
+
+static int __tasha_cdc_change_cpe_clk(struct snd_soc_codec *codec,
+ u32 clk_freq)
+{
+ int ret = 0;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ if (!tasha_cdc_is_svs_enabled(tasha)) {
+ dev_dbg(codec->dev,
+ "%s: SVS not enabled or tasha is not 2p0, return\n",
+ __func__);
+ return 0;
+ }
+ dev_dbg(codec->dev, "%s: clk_freq = %u\n", __func__, clk_freq);
+
+ if (clk_freq == CPE_FLL_CLK_75MHZ) {
+ /* Change to SVS */
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+ 0x08, 0x08);
+ if (tasha_codec_cpe_fll_update_divider(codec, clk_freq)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+ 0x10, 0x10);
+
+ clear_bit(CPE_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha, sido_buck_svs_voltage);
+
+ } else if (clk_freq == CPE_FLL_CLK_150MHZ) {
+ /* change to nominal */
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+ 0x08, 0x08);
+
+ set_bit(CPE_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha, SIDO_VOLTAGE_NOMINAL_MV);
+
+ if (tasha_codec_cpe_fll_update_divider(codec, clk_freq)) {
+ ret = -EINVAL;
+ goto done;
+ }
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+ 0x10, 0x10);
+ } else {
+ dev_err(codec->dev,
+ "%s: Invalid clk_freq request %d for CPE FLL\n",
+ __func__, clk_freq);
+ ret = -EINVAL;
+ }
+
+done:
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+ 0x10, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+ 0x08, 0x00);
+ return ret;
+}
+
+
+static int tasha_codec_cpe_fll_enable(struct snd_soc_codec *codec,
+ bool enable)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u8 clk_sel_reg_val = 0x00;
+
+ dev_dbg(codec->dev, "%s: enable = %s\n",
+ __func__, enable ? "true" : "false");
+
+ if (enable) {
+ if (tasha_cdc_is_svs_enabled(tasha)) {
+ /* FLL enable is always at SVS */
+ if (__tasha_cdc_change_cpe_clk(codec,
+ CPE_FLL_CLK_75MHZ)) {
+ dev_err(codec->dev,
+ "%s: clk change to %d failed\n",
+ __func__, CPE_FLL_CLK_75MHZ);
+ return -EINVAL;
+ }
+ } else {
+ if (tasha_codec_cpe_fll_update_divider(codec,
+ CPE_FLL_CLK_75MHZ)) {
+ dev_err(codec->dev,
+ "%s: clk change to %d failed\n",
+ __func__, CPE_FLL_CLK_75MHZ);
+ return -EINVAL;
+ }
+ }
+
+ if (TASHA_IS_1_0(wcd9xxx)) {
+ tasha_cdc_mclk_enable(codec, true, false);
+ clk_sel_reg_val = 0x02;
+ }
+
+ /* Setup CPE reference clk */
+ snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP,
+ 0x02, clk_sel_reg_val);
+
+ /* enable CPE FLL reference clk */
+ snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP,
+ 0x01, 0x01);
+
+ /* program the PLL */
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0,
+ 0x01, 0x01);
+
+ /* TEST clk setting */
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_TEST_CTL_0,
+ 0x80, 0x80);
+ /* set FLL mode to HW controlled */
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+ 0x60, 0x00);
+ snd_soc_write(codec, WCD9335_CPE_FLL_FLL_MODE, 0x80);
+ } else {
+ /* disable CPE FLL reference clk */
+ snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP,
+ 0x01, 0x00);
+ /* undo TEST clk setting */
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_TEST_CTL_0,
+ 0x80, 0x00);
+ /* undo FLL mode to HW control */
+ snd_soc_write(codec, WCD9335_CPE_FLL_FLL_MODE, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+ 0x60, 0x20);
+ /* undo the PLL */
+ snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0,
+ 0x01, 0x00);
+
+ if (TASHA_IS_1_0(wcd9xxx))
+ tasha_cdc_mclk_enable(codec, false, false);
+
+ /*
+ * FLL could get disabled while at nominal,
+ * scale it back to SVS
+ */
+ if (tasha_cdc_is_svs_enabled(tasha))
+ __tasha_cdc_change_cpe_clk(codec,
+ CPE_FLL_CLK_75MHZ);
+ }
+
+ return 0;
+
+}
+
+static void tasha_cdc_query_cpe_clk_plan(void *data,
+ struct cpe_svc_cfg_clk_plan *clk_freq)
+{
+ struct snd_soc_codec *codec = data;
+ struct tasha_priv *tasha;
+ u32 cpe_clk_khz;
+
+ if (!codec) {
+ pr_err("%s: Invalid codec handle\n",
+ __func__);
+ return;
+ }
+
+ tasha = snd_soc_codec_get_drvdata(codec);
+ cpe_clk_khz = tasha->current_cpe_clk_freq / 1000;
+
+ dev_dbg(codec->dev,
+ "%s: current_clk_freq = %u\n",
+ __func__, tasha->current_cpe_clk_freq);
+
+ clk_freq->current_clk_feq = cpe_clk_khz;
+ clk_freq->num_clk_freqs = 2;
+
+ if (tasha_cdc_is_svs_enabled(tasha)) {
+ clk_freq->clk_freqs[0] = CPE_FLL_CLK_75MHZ / 1000;
+ clk_freq->clk_freqs[1] = CPE_FLL_CLK_150MHZ / 1000;
+ } else {
+ clk_freq->clk_freqs[0] = CPE_FLL_CLK_75MHZ;
+ clk_freq->clk_freqs[1] = CPE_FLL_CLK_150MHZ;
+ }
+}
+
+static void tasha_cdc_change_cpe_clk(void *data,
+ u32 clk_freq)
+{
+ struct snd_soc_codec *codec = data;
+ struct tasha_priv *tasha;
+ u32 cpe_clk_khz, req_freq = 0;
+
+ if (!codec) {
+ pr_err("%s: Invalid codec handle\n",
+ __func__);
+ return;
+ }
+
+ tasha = snd_soc_codec_get_drvdata(codec);
+ cpe_clk_khz = tasha->current_cpe_clk_freq / 1000;
+
+ if (tasha_cdc_is_svs_enabled(tasha)) {
+ if ((clk_freq * 1000) <= CPE_FLL_CLK_75MHZ)
+ req_freq = CPE_FLL_CLK_75MHZ;
+ else
+ req_freq = CPE_FLL_CLK_150MHZ;
+ }
+
+ dev_dbg(codec->dev,
+ "%s: requested clk_freq = %u, current clk_freq = %u\n",
+ __func__, clk_freq * 1000,
+ tasha->current_cpe_clk_freq);
+
+ if (tasha_cdc_is_svs_enabled(tasha)) {
+ if (__tasha_cdc_change_cpe_clk(codec, req_freq))
+ dev_err(codec->dev,
+ "%s: clock/voltage scaling failed\n",
+ __func__);
+ }
+}
+
+static int tasha_codec_slim_reserve_bw(struct snd_soc_codec *codec,
+ u32 bw_ops, bool commit)
+{
+ struct wcd9xxx *wcd9xxx;
+ if (!codec) {
+ pr_err("%s: Invalid handle to codec\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ if (!wcd9xxx) {
+ dev_err(codec->dev, "%s: Invalid parent drv_data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return wcd9xxx_slim_reserve_bw(wcd9xxx, bw_ops, commit);
+}
+
+static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec,
+ bool vote)
+{
+ u32 bw_ops;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C)
+ return 0;
+
+ mutex_lock(&tasha->sb_clk_gear_lock);
+ if (vote) {
+ tasha->ref_count++;
+ if (tasha->ref_count == 1) {
+ bw_ops = SLIM_BW_CLK_GEAR_9;
+ tasha_codec_slim_reserve_bw(codec,
+ bw_ops, true);
+ }
+ } else if (!vote && tasha->ref_count > 0) {
+ tasha->ref_count--;
+ if (tasha->ref_count == 0) {
+ bw_ops = SLIM_BW_UNVOTE;
+ tasha_codec_slim_reserve_bw(codec,
+ bw_ops, true);
+ }
+ };
+
+ dev_dbg(codec->dev, "%s Value of counter after vote or un-vote is %d\n",
+ __func__, tasha->ref_count);
+
+ mutex_unlock(&tasha->sb_clk_gear_lock);
+
+ return 0;
+}
+
+static int tasha_cpe_err_irq_control(struct snd_soc_codec *codec,
+ enum cpe_err_irq_cntl_type cntl_type, u8 *status)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ u8 irq_bits;
+
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
+ irq_bits = 0xFF;
+ else
+ irq_bits = 0x3F;
+
+ if (status)
+ irq_bits = (*status) & irq_bits;
+
+ switch (cntl_type) {
+ case CPE_ERR_IRQ_MASK:
+ snd_soc_update_bits(codec,
+ WCD9335_CPE_SS_SS_ERROR_INT_MASK,
+ irq_bits, irq_bits);
+ break;
+ case CPE_ERR_IRQ_UNMASK:
+ snd_soc_update_bits(codec,
+ WCD9335_CPE_SS_SS_ERROR_INT_MASK,
+ irq_bits, 0x00);
+ break;
+ case CPE_ERR_IRQ_CLEAR:
+ snd_soc_write(codec, WCD9335_CPE_SS_SS_ERROR_INT_CLEAR,
+ irq_bits);
+ break;
+ case CPE_ERR_IRQ_STATUS:
+ if (!status)
+ return -EINVAL;
+ *status = snd_soc_read(codec,
+ WCD9335_CPE_SS_SS_ERROR_INT_STATUS);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct wcd_cpe_cdc_cb cpe_cb = {
+ .cdc_clk_en = tasha_codec_internal_rco_ctrl,
+ .cpe_clk_en = tasha_codec_cpe_fll_enable,
+ .get_afe_out_port_id = tasha_codec_get_mad_port_id,
+ .lab_cdc_ch_ctl = tasha_codec_enable_slimtx_mad,
+ .cdc_ext_clk = tasha_cdc_mclk_enable,
+ .bus_vote_bw = tasha_codec_vote_max_bw,
+ .cpe_err_irq_control = tasha_cpe_err_irq_control,
+};
+
+static struct cpe_svc_init_param cpe_svc_params = {
+ .version = CPE_SVC_INIT_PARAM_V1,
+ .query_freq_plans_cb = tasha_cdc_query_cpe_clk_plan,
+ .change_freq_plan_cb = tasha_cdc_change_cpe_clk,
+};
+
+static int tasha_cpe_initialize(struct snd_soc_codec *codec)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd_cpe_params cpe_params;
+
+ memset(&cpe_params, 0,
+ sizeof(struct wcd_cpe_params));
+ cpe_params.codec = codec;
+ cpe_params.get_cpe_core = tasha_codec_get_cpe_core;
+ cpe_params.cdc_cb = &cpe_cb;
+ cpe_params.dbg_mode = cpe_debug_mode;
+ cpe_params.cdc_major_ver = CPE_SVC_CODEC_WCD9335;
+ cpe_params.cdc_minor_ver = CPE_SVC_CODEC_V1P0;
+ cpe_params.cdc_id = CPE_SVC_CODEC_WCD9335;
+
+ cpe_params.cdc_irq_info.cpe_engine_irq =
+ WCD9335_IRQ_SVA_OUTBOX1;
+ cpe_params.cdc_irq_info.cpe_err_irq =
+ WCD9335_IRQ_SVA_ERROR;
+ cpe_params.cdc_irq_info.cpe_fatal_irqs =
+ TASHA_CPE_FATAL_IRQS;
+
+ cpe_svc_params.context = codec;
+ cpe_params.cpe_svc_params = &cpe_svc_params;
+
+ tasha->cpe_core = wcd_cpe_init("cpe_9335", codec,
+ &cpe_params);
+ if (IS_ERR_OR_NULL(tasha->cpe_core)) {
+ dev_err(codec->dev,
+ "%s: Failed to enable CPE\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct wcd_resmgr_cb tasha_resmgr_cb = {
+ .cdc_rco_ctrl = __tasha_codec_internal_rco_ctrl,
+};
+
+static int tasha_device_down(struct wcd9xxx *wcd9xxx)
+{
+ struct snd_soc_codec *codec;
+ struct tasha_priv *priv;
+ int count;
+ int i = 0;
+
+ codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+ priv = snd_soc_codec_get_drvdata(codec);
+ wcd_cpe_ssr_event(priv->cpe_core, WCD_CPE_BUS_DOWN_EVENT);
+ for (i = 0; i < priv->nr; i++)
+ swrm_wcd_notify(priv->swr_ctrl_data[i].swr_pdev,
+ SWR_DEVICE_DOWN, NULL);
+ snd_soc_card_change_online_state(codec->component.card, 0);
+ for (count = 0; count < NUM_CODEC_DAIS; count++)
+ priv->dai[count].bus_down_in_recovery = true;
+
+ priv->resmgr->sido_input_src = SIDO_SOURCE_INTERNAL;
+
+ return 0;
+}
+
+static int tasha_post_reset_cb(struct wcd9xxx *wcd9xxx)
+{
+ int i, ret = 0;
+ struct wcd9xxx *control;
+ struct snd_soc_codec *codec;
+ struct tasha_priv *tasha;
+ struct wcd9xxx_pdata *pdata;
+
+ codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+ tasha = snd_soc_codec_get_drvdata(codec);
+ control = dev_get_drvdata(codec->dev->parent);
+
+ wcd9xxx_set_power_state(tasha->wcd9xxx,
+ WCD_REGION_POWER_COLLAPSE_REMOVE,
+ WCD9XXX_DIG_CORE_REGION_1);
+
+ mutex_lock(&tasha->codec_mutex);
+
+ tasha_slimbus_slave_port_cfg.slave_dev_intfdev_la =
+ control->slim_slave->laddr;
+ tasha_slimbus_slave_port_cfg.slave_dev_pgd_la =
+ control->slim->laddr;
+ tasha_init_slim_slave_cfg(codec);
+ if (tasha->machine_codec_event_cb)
+ tasha->machine_codec_event_cb(codec,
+ WCD9335_CODEC_EVENT_CODEC_UP);
+ snd_soc_card_change_online_state(codec->component.card, 1);
+
+ /* Class-H Init*/
+ wcd_clsh_init(&tasha->clsh_d);
+
+ for (i = 0; i < TASHA_MAX_MICBIAS; i++)
+ tasha->micb_ref[i] = 0;
+
+ tasha_update_reg_defaults(tasha);
+
+ tasha->codec = codec;
+
+ dev_dbg(codec->dev, "%s: MCLK Rate = %x\n",
+ __func__, control->mclk_rate);
+
+ if (control->mclk_rate == TASHA_MCLK_CLK_12P288MHZ)
+ snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG,
+ 0x03, 0x00);
+ else if (control->mclk_rate == TASHA_MCLK_CLK_9P6MHZ)
+ snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG,
+ 0x03, 0x01);
+ tasha_codec_init_reg(codec);
+
+ wcd_resmgr_post_ssr_v2(tasha->resmgr);
+
+ tasha_enable_efuse_sensing(codec);
+
+ regcache_mark_dirty(codec->component.regmap);
+ regcache_sync(codec->component.regmap);
+
+ pdata = dev_get_platdata(codec->dev->parent);
+ ret = tasha_handle_pdata(tasha, pdata);
+ if (IS_ERR_VALUE(ret))
+ dev_err(codec->dev, "%s: invalid pdata\n", __func__);
+
+ /* Reset reference counter for voting for max bw */
+ tasha->ref_count = 0;
+ /* MBHC Init */
+ wcd_mbhc_deinit(&tasha->mbhc);
+ tasha->mbhc_started = false;
+
+ /* Initialize MBHC module */
+ ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids,
+ wcd_mbhc_registers, TASHA_ZDET_SUPPORTED);
+ if (ret)
+ dev_err(codec->dev, "%s: mbhc initialization failed\n",
+ __func__);
+ else
+ tasha_mbhc_hs_detect(codec, tasha->mbhc.mbhc_cfg);
+
+ tasha_cleanup_irqs(tasha);
+ ret = tasha_setup_irqs(tasha);
+ if (ret) {
+ dev_err(codec->dev, "%s: tasha irq setup failed %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ tasha_set_spkr_mode(codec, tasha->spkr_mode);
+ wcd_cpe_ssr_event(tasha->cpe_core, WCD_CPE_BUS_UP_EVENT);
+
+err:
+ mutex_unlock(&tasha->codec_mutex);
+ return ret;
+}
+
+static struct regulator *tasha_codec_find_ondemand_regulator(
+ struct snd_soc_codec *codec, const char *name)
+{
+ int i;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+ struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+
+ for (i = 0; i < wcd9xxx->num_of_supplies; ++i) {
+ if (pdata->regulator[i].ondemand &&
+ wcd9xxx->supplies[i].supply &&
+ !strcmp(wcd9xxx->supplies[i].supply, name))
+ return wcd9xxx->supplies[i].consumer;
+ }
+
+ dev_dbg(tasha->dev, "Warning: regulator not found:%s\n",
+ name);
+ return NULL;
+}
+
+static int tasha_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wcd9xxx *control;
+ struct tasha_priv *tasha;
+ struct wcd9xxx_pdata *pdata;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ int i, ret;
+ void *ptr = NULL;
+ struct regulator *supply;
+
+ control = dev_get_drvdata(codec->dev->parent);
+
+ dev_info(codec->dev, "%s()\n", __func__);
+ tasha = snd_soc_codec_get_drvdata(codec);
+ tasha->intf_type = wcd9xxx_get_intf_type();
+
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ control->dev_down = tasha_device_down;
+ control->post_reset = tasha_post_reset_cb;
+ control->ssr_priv = (void *)codec;
+ }
+
+ /* Resource Manager post Init */
+ ret = wcd_resmgr_post_init(tasha->resmgr, &tasha_resmgr_cb, codec);
+ if (ret) {
+ dev_err(codec->dev, "%s: wcd resmgr post init failed\n",
+ __func__);
+ goto err;
+ }
+ /* Class-H Init*/
+ wcd_clsh_init(&tasha->clsh_d);
+ /* Default HPH Mode to Class-H HiFi */
+ tasha->hph_mode = CLS_H_HIFI;
+
+ tasha->codec = codec;
+ for (i = 0; i < COMPANDER_MAX; i++)
+ tasha->comp_enabled[i] = 0;
+
+ tasha->spkr_gain_offset = RX_GAIN_OFFSET_0_DB;
+ tasha->intf_type = wcd9xxx_get_intf_type();
+ tasha_update_reg_reset_values(codec);
+ pr_debug("%s: MCLK Rate = %x\n", __func__, control->mclk_rate);
+ if (control->mclk_rate == TASHA_MCLK_CLK_12P288MHZ)
+ snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG,
+ 0x03, 0x00);
+ else if (control->mclk_rate == TASHA_MCLK_CLK_9P6MHZ)
+ snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG,
+ 0x03, 0x01);
+ tasha_codec_init_reg(codec);
+
+ tasha_enable_efuse_sensing(codec);
+
+ pdata = dev_get_platdata(codec->dev->parent);
+ ret = tasha_handle_pdata(tasha, pdata);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: bad pdata\n", __func__);
+ goto err;
+ }
+
+ supply = tasha_codec_find_ondemand_regulator(codec,
+ on_demand_supply_name[ON_DEMAND_MICBIAS]);
+ if (supply) {
+ tasha->on_demand_list[ON_DEMAND_MICBIAS].supply = supply;
+ tasha->on_demand_list[ON_DEMAND_MICBIAS].ondemand_supply_count =
+ 0;
+ }
+
+ tasha->fw_data = devm_kzalloc(codec->dev,
+ sizeof(*(tasha->fw_data)), GFP_KERNEL);
+ if (!tasha->fw_data) {
+ dev_err(codec->dev, "Failed to allocate fw_data\n");
+ goto err;
+ }
+ set_bit(WCD9XXX_ANC_CAL, tasha->fw_data->cal_bit);
+ set_bit(WCD9XXX_MBHC_CAL, tasha->fw_data->cal_bit);
+ set_bit(WCD9XXX_MAD_CAL, tasha->fw_data->cal_bit);
+ set_bit(WCD9XXX_VBAT_CAL, tasha->fw_data->cal_bit);
+
+ ret = wcd_cal_create_hwdep(tasha->fw_data,
+ WCD9XXX_CODEC_HWDEP_NODE, codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
+ goto err_hwdep;
+ }
+
+ /* Initialize MBHC module */
+ if (TASHA_IS_2_0(tasha->wcd9xxx)) {
+ wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].reg =
+ WCD9335_MBHC_FSM_STATUS;
+ wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].mask = 0x01;
+ }
+ ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids,
+ wcd_mbhc_registers, TASHA_ZDET_SUPPORTED);
+ if (ret) {
+ pr_err("%s: mbhc initialization failed\n", __func__);
+ goto err_hwdep;
+ }
+
+ ptr = devm_kzalloc(codec->dev, (sizeof(tasha_rx_chs) +
+ sizeof(tasha_tx_chs)), GFP_KERNEL);
+ if (!ptr) {
+ pr_err("%s: no mem for slim chan ctl data\n", __func__);
+ ret = -ENOMEM;
+ goto err_hwdep;
+ }
+
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ snd_soc_dapm_new_controls(dapm, tasha_dapm_i2s_widgets,
+ ARRAY_SIZE(tasha_dapm_i2s_widgets));
+ snd_soc_dapm_add_routes(dapm, audio_i2s_map,
+ ARRAY_SIZE(audio_i2s_map));
+ for (i = 0; i < ARRAY_SIZE(tasha_i2s_dai); i++) {
+ INIT_LIST_HEAD(&tasha->dai[i].wcd9xxx_ch_list);
+ init_waitqueue_head(&tasha->dai[i].dai_wait);
+ }
+ } else if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ for (i = 0; i < NUM_CODEC_DAIS; i++) {
+ INIT_LIST_HEAD(&tasha->dai[i].wcd9xxx_ch_list);
+ init_waitqueue_head(&tasha->dai[i].dai_wait);
+ }
+ tasha_slimbus_slave_port_cfg.slave_dev_intfdev_la =
+ control->slim_slave->laddr;
+ tasha_slimbus_slave_port_cfg.slave_dev_pgd_la =
+ control->slim->laddr;
+ tasha_slimbus_slave_port_cfg.slave_port_mapping[0] =
+ TASHA_TX13;
+ tasha_init_slim_slave_cfg(codec);
+ }
+
+ snd_soc_add_codec_controls(codec, impedance_detect_controls,
+ ARRAY_SIZE(impedance_detect_controls));
+ snd_soc_add_codec_controls(codec, hph_type_detect_controls,
+ ARRAY_SIZE(hph_type_detect_controls));
+
+ snd_soc_add_codec_controls(codec,
+ tasha_analog_gain_controls,
+ ARRAY_SIZE(tasha_analog_gain_controls));
+ control->num_rx_port = TASHA_RX_MAX;
+ control->rx_chs = ptr;
+ memcpy(control->rx_chs, tasha_rx_chs, sizeof(tasha_rx_chs));
+ control->num_tx_port = TASHA_TX_MAX;
+ control->tx_chs = ptr + sizeof(tasha_rx_chs);
+ memcpy(control->tx_chs, tasha_tx_chs, sizeof(tasha_tx_chs));
+
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture");
+
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF Mix Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX");
+ snd_soc_dapm_ignore_suspend(dapm, "VIfeed");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF5 CPE TX");
+ }
+
+ snd_soc_dapm_sync(dapm);
+
+ ret = tasha_setup_irqs(tasha);
+ if (ret) {
+ pr_err("%s: tasha irq setup failed %d\n", __func__, ret);
+ goto err_pdata;
+ }
+
+ ret = tasha_cpe_initialize(codec);
+ if (ret) {
+ dev_err(codec->dev,
+ "%s: cpe initialization failed, err = %d\n",
+ __func__, ret);
+ /* Do not fail probe if CPE failed */
+ ret = 0;
+ }
+
+ for (i = 0; i < TASHA_NUM_DECIMATORS; i++) {
+ tasha->tx_hpf_work[i].tasha = tasha;
+ tasha->tx_hpf_work[i].decimator = i;
+ INIT_DELAYED_WORK(&tasha->tx_hpf_work[i].dwork,
+ tasha_tx_hpf_corner_freq_callback);
+ }
+
+ for (i = 0; i < TASHA_NUM_DECIMATORS; i++) {
+ tasha->tx_mute_dwork[i].tasha = tasha;
+ tasha->tx_mute_dwork[i].decimator = i;
+ INIT_DELAYED_WORK(&tasha->tx_mute_dwork[i].dwork,
+ tasha_tx_mute_update_callback);
+ }
+
+ tasha->spk_anc_dwork.tasha = tasha;
+ INIT_DELAYED_WORK(&tasha->spk_anc_dwork.dwork,
+ tasha_spk_anc_update_callback);
+
+ mutex_lock(&tasha->codec_mutex);
+ snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1");
+ snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2");
+ snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1 PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2 PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA");
+ mutex_unlock(&tasha->codec_mutex);
+ snd_soc_dapm_sync(dapm);
+
+ return ret;
+
+err_pdata:
+ devm_kfree(codec->dev, ptr);
+ control->rx_chs = NULL;
+ control->tx_chs = NULL;
+err_hwdep:
+ devm_kfree(codec->dev, tasha->fw_data);
+ tasha->fw_data = NULL;
+err:
+ return ret;
+}
+
+static int tasha_codec_remove(struct snd_soc_codec *codec)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *control;
+
+ control = dev_get_drvdata(codec->dev->parent);
+ control->rx_chs = NULL;
+ control->tx_chs = NULL;
+
+ tasha_cleanup_irqs(tasha);
+ /* Cleanup MBHC */
+ /* Cleanup resmgr */
+
+ return 0;
+}
+
+static struct regmap *tasha_get_regmap(struct device *dev)
+{
+ struct wcd9xxx *control = dev_get_drvdata(dev->parent);
+
+ return control->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_tasha = {
+ .probe = tasha_codec_probe,
+ .remove = tasha_codec_remove,
+ .controls = tasha_snd_controls,
+ .num_controls = ARRAY_SIZE(tasha_snd_controls),
+ .dapm_widgets = tasha_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tasha_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .get_regmap = tasha_get_regmap,
+};
+
+#ifdef CONFIG_PM
+static int tasha_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tasha_priv *tasha = platform_get_drvdata(pdev);
+
+ dev_dbg(dev, "%s: system suspend\n", __func__);
+ if (cancel_delayed_work_sync(&tasha->power_gate_work))
+ tasha_codec_power_gate_digital_core(tasha);
+
+ return 0;
+}
+
+static int tasha_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tasha_priv *tasha = platform_get_drvdata(pdev);
+
+ if (!tasha) {
+ dev_err(dev, "%s: tasha private data is NULL\n", __func__);
+ return -EINVAL;
+ }
+ dev_dbg(dev, "%s: system resume\n", __func__);
+ return 0;
+}
+
+static const struct dev_pm_ops tasha_pm_ops = {
+ .suspend = tasha_suspend,
+ .resume = tasha_resume,
+};
+#endif
+
+static int tasha_swrm_read(void *handle, int reg)
+{
+ struct tasha_priv *tasha;
+ struct wcd9xxx *wcd9xxx;
+ unsigned short swr_rd_addr_base;
+ unsigned short swr_rd_data_base;
+ int val, ret;
+
+ if (!handle) {
+ pr_err("%s: NULL handle\n", __func__);
+ return -EINVAL;
+ }
+ tasha = (struct tasha_priv *)handle;
+ wcd9xxx = tasha->wcd9xxx;
+
+ dev_dbg(tasha->dev, "%s: Reading soundwire register, 0x%x\n",
+ __func__, reg);
+ swr_rd_addr_base = WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0;
+ swr_rd_data_base = WCD9335_SWR_AHB_BRIDGE_RD_DATA_0;
+ /* read_lock */
+ mutex_lock(&tasha->swr_read_lock);
+ ret = regmap_bulk_write(wcd9xxx->regmap, swr_rd_addr_base,
+ (u8 *)&reg, 4);
+ if (ret < 0) {
+ pr_err("%s: RD Addr Failure\n", __func__);
+ goto err;
+ }
+ /* Check for RD status */
+ ret = regmap_bulk_read(wcd9xxx->regmap, swr_rd_data_base,
+ (u8 *)&val, 4);
+ if (ret < 0) {
+ pr_err("%s: RD Data Failure\n", __func__);
+ goto err;
+ }
+ ret = val;
+err:
+ /* read_unlock */
+ mutex_unlock(&tasha->swr_read_lock);
+ return ret;
+}
+
+static int tasha_swrm_i2s_bulk_write(struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_reg_val *bulk_reg,
+ size_t len)
+{
+ int i, ret = 0;
+ unsigned short swr_wr_addr_base;
+ unsigned short swr_wr_data_base;
+
+ swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0;
+ swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0;
+
+ for (i = 0; i < (len * 2); i += 2) {
+ /* First Write the Data to register */
+ ret = regmap_bulk_write(wcd9xxx->regmap,
+ swr_wr_data_base, bulk_reg[i].buf, 4);
+ if (ret < 0) {
+ dev_err(wcd9xxx->dev, "%s: WR Data Failure\n",
+ __func__);
+ break;
+ }
+ /* Next Write Address */
+ ret = regmap_bulk_write(wcd9xxx->regmap,
+ swr_wr_addr_base, bulk_reg[i+1].buf, 4);
+ if (ret < 0) {
+ dev_err(wcd9xxx->dev, "%s: WR Addr Failure\n",
+ __func__);
+ break;
+ }
+ }
+ return ret;
+}
+
+static int tasha_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len)
+{
+ struct tasha_priv *tasha;
+ struct wcd9xxx *wcd9xxx;
+ struct wcd9xxx_reg_val *bulk_reg;
+ unsigned short swr_wr_addr_base;
+ unsigned short swr_wr_data_base;
+ int i, j, ret;
+
+ if (!handle) {
+ pr_err("%s: NULL handle\n", __func__);
+ return -EINVAL;
+ }
+ if (len <= 0) {
+ pr_err("%s: Invalid size: %zu\n", __func__, len);
+ return -EINVAL;
+ }
+ tasha = (struct tasha_priv *)handle;
+ wcd9xxx = tasha->wcd9xxx;
+
+ swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0;
+ swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0;
+
+ bulk_reg = kzalloc((2 * len * sizeof(struct wcd9xxx_reg_val)),
+ GFP_KERNEL);
+ if (!bulk_reg)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < (len * 2); i += 2, j++) {
+ bulk_reg[i].reg = swr_wr_data_base;
+ bulk_reg[i].buf = (u8 *)(&val[j]);
+ bulk_reg[i].bytes = 4;
+ bulk_reg[i+1].reg = swr_wr_addr_base;
+ bulk_reg[i+1].buf = (u8 *)(&reg[j]);
+ bulk_reg[i+1].bytes = 4;
+ }
+ mutex_lock(&tasha->swr_write_lock);
+
+ if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) {
+ ret = tasha_swrm_i2s_bulk_write(wcd9xxx, bulk_reg, len);
+ if (ret) {
+ dev_err(tasha->dev, "%s: i2s bulk write failed, ret: %d\n",
+ __func__, ret);
+ }
+ } else {
+ ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg,
+ (len * 2), false);
+ if (ret) {
+ dev_err(tasha->dev, "%s: swrm bulk write failed, ret: %d\n",
+ __func__, ret);
+ }
+ }
+
+ mutex_unlock(&tasha->swr_write_lock);
+ kfree(bulk_reg);
+
+ return ret;
+}
+
+static int tasha_swrm_write(void *handle, int reg, int val)
+{
+ struct tasha_priv *tasha;
+ struct wcd9xxx *wcd9xxx;
+ unsigned short swr_wr_addr_base;
+ unsigned short swr_wr_data_base;
+ struct wcd9xxx_reg_val bulk_reg[2];
+ int ret;
+
+ if (!handle) {
+ pr_err("%s: NULL handle\n", __func__);
+ return -EINVAL;
+ }
+ tasha = (struct tasha_priv *)handle;
+ wcd9xxx = tasha->wcd9xxx;
+
+ swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0;
+ swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0;
+
+ /* First Write the Data to register */
+ bulk_reg[0].reg = swr_wr_data_base;
+ bulk_reg[0].buf = (u8 *)(&val);
+ bulk_reg[0].bytes = 4;
+ bulk_reg[1].reg = swr_wr_addr_base;
+ bulk_reg[1].buf = (u8 *)(&reg);
+ bulk_reg[1].bytes = 4;
+
+ mutex_lock(&tasha->swr_write_lock);
+
+ if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) {
+ ret = tasha_swrm_i2s_bulk_write(wcd9xxx, bulk_reg, 1);
+ if (ret) {
+ dev_err(tasha->dev, "%s: i2s swrm write failed, ret: %d\n",
+ __func__, ret);
+ }
+ } else {
+ ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false);
+ if (ret < 0)
+ pr_err("%s: WR Data Failure\n", __func__);
+ }
+
+ mutex_unlock(&tasha->swr_write_lock);
+ return ret;
+}
+
+static int tasha_swrm_clock(void *handle, bool enable)
+{
+ struct tasha_priv *tasha = (struct tasha_priv *) handle;
+
+ mutex_lock(&tasha->swr_clk_lock);
+
+ dev_dbg(tasha->dev, "%s: swrm clock %s\n",
+ __func__, (enable?"enable" : "disable"));
+ if (enable) {
+ tasha->swr_clk_users++;
+ if (tasha->swr_clk_users == 1) {
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
+ regmap_update_bits(
+ tasha->wcd9xxx->regmap,
+ WCD9335_TEST_DEBUG_NPL_DLY_TEST_1,
+ 0x10, 0x00);
+ __tasha_cdc_mclk_enable(tasha, true);
+ regmap_update_bits(tasha->wcd9xxx->regmap,
+ WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL,
+ 0x01, 0x01);
+ }
+ } else {
+ tasha->swr_clk_users--;
+ if (tasha->swr_clk_users == 0) {
+ regmap_update_bits(tasha->wcd9xxx->regmap,
+ WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL,
+ 0x01, 0x00);
+ __tasha_cdc_mclk_enable(tasha, false);
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
+ regmap_update_bits(
+ tasha->wcd9xxx->regmap,
+ WCD9335_TEST_DEBUG_NPL_DLY_TEST_1,
+ 0x10, 0x10);
+ }
+ }
+ dev_dbg(tasha->dev, "%s: swrm clock users %d\n",
+ __func__, tasha->swr_clk_users);
+ mutex_unlock(&tasha->swr_clk_lock);
+ return 0;
+}
+
+static int tasha_swrm_handle_irq(void *handle,
+ irqreturn_t (*swrm_irq_handler)(int irq,
+ void *data),
+ void *swrm_handle,
+ int action)
+{
+ struct tasha_priv *tasha;
+ int ret = 0;
+ struct wcd9xxx *wcd9xxx;
+
+ if (!handle) {
+ pr_err("%s: null handle received\n", __func__);
+ return -EINVAL;
+ }
+ tasha = (struct tasha_priv *) handle;
+ wcd9xxx = tasha->wcd9xxx;
+
+ if (action) {
+ ret = wcd9xxx_request_irq(&wcd9xxx->core_res,
+ WCD9335_IRQ_SOUNDWIRE,
+ swrm_irq_handler,
+ "Tasha SWR Master", swrm_handle);
+ if (ret)
+ dev_err(tasha->dev, "%s: Failed to request irq %d\n",
+ __func__, WCD9335_IRQ_SOUNDWIRE);
+ } else
+ wcd9xxx_free_irq(&wcd9xxx->core_res, WCD9335_IRQ_SOUNDWIRE,
+ swrm_handle);
+
+ return ret;
+}
+
+static void tasha_add_child_devices(struct work_struct *work)
+{
+ struct tasha_priv *tasha;
+ struct platform_device *pdev;
+ struct device_node *node;
+ struct wcd9xxx *wcd9xxx;
+ struct tasha_swr_ctrl_data *swr_ctrl_data = NULL, *temp;
+ int ret, ctrl_num = 0;
+ struct wcd_swr_ctrl_platform_data *platdata;
+ char plat_dev_name[WCD9335_STRING_LEN];
+
+ tasha = container_of(work, struct tasha_priv,
+ tasha_add_child_devices_work);
+ if (!tasha) {
+ pr_err("%s: Memory for WCD9335 does not exist\n",
+ __func__);
+ return;
+ }
+ wcd9xxx = tasha->wcd9xxx;
+ if (!wcd9xxx) {
+ pr_err("%s: Memory for WCD9XXX does not exist\n",
+ __func__);
+ return;
+ }
+ if (!wcd9xxx->dev->of_node) {
+ pr_err("%s: DT node for wcd9xxx does not exist\n",
+ __func__);
+ return;
+ }
+
+ platdata = &tasha->swr_plat_data;
+
+ for_each_child_of_node(wcd9xxx->dev->of_node, node) {
+ if (!strcmp(node->name, "swr_master"))
+ strlcpy(plat_dev_name, "tasha_swr_ctrl",
+ (WCD9335_STRING_LEN - 1));
+ else if (strnstr(node->name, "msm_cdc_pinctrl",
+ strlen("msm_cdc_pinctrl")) != NULL)
+ strlcpy(plat_dev_name, node->name,
+ (WCD9335_STRING_LEN - 1));
+ else
+ continue;
+
+ pdev = platform_device_alloc(plat_dev_name, -1);
+ if (!pdev) {
+ dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err;
+ }
+ pdev->dev.parent = tasha->dev;
+ pdev->dev.of_node = node;
+
+ if (!strcmp(node->name, "swr_master")) {
+ ret = platform_device_add_data(pdev, platdata,
+ sizeof(*platdata));
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: cannot add plat data ctrl:%d\n",
+ __func__, ctrl_num);
+ goto fail_pdev_add;
+ }
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: Cannot add platform device\n",
+ __func__);
+ goto fail_pdev_add;
+ }
+
+ if (!strcmp(node->name, "swr_master")) {
+ temp = krealloc(swr_ctrl_data,
+ (ctrl_num + 1) * sizeof(
+ struct tasha_swr_ctrl_data),
+ GFP_KERNEL);
+ if (!temp) {
+ dev_err(wcd9xxx->dev, "out of memory\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ swr_ctrl_data = temp;
+ swr_ctrl_data[ctrl_num].swr_pdev = pdev;
+ ctrl_num++;
+ dev_dbg(&pdev->dev,
+ "%s: Added soundwire ctrl device(s)\n",
+ __func__);
+ tasha->nr = ctrl_num;
+ tasha->swr_ctrl_data = swr_ctrl_data;
+ }
+ }
+
+ return;
+fail_pdev_add:
+ platform_device_put(pdev);
+err:
+ return;
+}
+
+/*
+ * tasha_codec_ver: to get tasha codec version
+ * @codec: handle to snd_soc_codec *
+ * return enum codec_variant - version
+ */
+enum codec_variant tasha_codec_ver(void)
+{
+ return codec_ver;
+}
+EXPORT_SYMBOL(tasha_codec_ver);
+
+static int __tasha_enable_efuse_sensing(struct tasha_priv *tasha)
+{
+ int val, rc;
+
+ __tasha_cdc_mclk_enable(tasha, true);
+
+ regmap_update_bits(tasha->wcd9xxx->regmap,
+ WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x20);
+ regmap_update_bits(tasha->wcd9xxx->regmap,
+ WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x01, 0x01);
+
+ /*
+ * 5ms sleep required after enabling efuse control
+ * before checking the status.
+ */
+ usleep_range(5000, 5500);
+ rc = regmap_read(tasha->wcd9xxx->regmap,
+ WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS, &val);
+
+ if (rc || (!(val & 0x01)))
+ WARN(1, "%s: Efuse sense is not complete\n", __func__);
+
+ __tasha_cdc_mclk_enable(tasha, false);
+
+ return rc;
+}
+
+void tasha_get_codec_ver(struct tasha_priv *tasha)
+{
+ int i;
+ int val;
+ struct tasha_reg_mask_val codec_reg[] = {
+ {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10, 0xFF, 0xFF},
+ {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11, 0xFF, 0x83},
+ {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12, 0xFF, 0x0A},
+ };
+
+ __tasha_enable_efuse_sensing(tasha);
+ for (i = 0; i < ARRAY_SIZE(codec_reg); i++) {
+ regmap_read(tasha->wcd9xxx->regmap, codec_reg[i].reg, &val);
+ if (!(val && codec_reg[i].val)) {
+ codec_ver = WCD9335;
+ goto ret;
+ }
+ }
+ codec_ver = WCD9326;
+ret:
+ pr_debug("%s: codec is %d\n", __func__, codec_ver);
+}
+EXPORT_SYMBOL(tasha_get_codec_ver);
+
+static int tasha_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct tasha_priv *tasha;
+ struct clk *wcd_ext_clk, *wcd_native_clk;
+ struct wcd9xxx_resmgr_v2 *resmgr;
+ struct wcd9xxx_power_region *cdc_pwr;
+
+ if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) {
+ if (apr_get_subsys_state() == APR_SUBSYS_DOWN) {
+ dev_err(&pdev->dev, "%s: dsp down\n", __func__);
+ return -EPROBE_DEFER;
+ }
+ }
+
+ tasha = devm_kzalloc(&pdev->dev, sizeof(struct tasha_priv),
+ GFP_KERNEL);
+ if (!tasha) {
+ dev_err(&pdev->dev, "%s: cannot create memory for wcd9335\n",
+ __func__);
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, tasha);
+
+ tasha->wcd9xxx = dev_get_drvdata(pdev->dev.parent);
+ tasha->dev = &pdev->dev;
+ INIT_DELAYED_WORK(&tasha->power_gate_work, tasha_codec_power_gate_work);
+ mutex_init(&tasha->power_lock);
+ mutex_init(&tasha->sido_lock);
+ INIT_WORK(&tasha->tasha_add_child_devices_work,
+ tasha_add_child_devices);
+ BLOCKING_INIT_NOTIFIER_HEAD(&tasha->notifier);
+ mutex_init(&tasha->micb_lock);
+ mutex_init(&tasha->swr_read_lock);
+ mutex_init(&tasha->swr_write_lock);
+ mutex_init(&tasha->swr_clk_lock);
+ mutex_init(&tasha->sb_clk_gear_lock);
+ mutex_init(&tasha->mclk_lock);
+
+ cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region),
+ GFP_KERNEL);
+ if (!cdc_pwr) {
+ ret = -ENOMEM;
+ goto err_cdc_pwr;
+ }
+ tasha->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr;
+ cdc_pwr->pwr_collapse_reg_min = TASHA_DIG_CORE_REG_MIN;
+ cdc_pwr->pwr_collapse_reg_max = TASHA_DIG_CORE_REG_MAX;
+ wcd9xxx_set_power_state(tasha->wcd9xxx,
+ WCD_REGION_POWER_COLLAPSE_REMOVE,
+ WCD9XXX_DIG_CORE_REGION_1);
+
+ mutex_init(&tasha->codec_mutex);
+ /*
+ * Init resource manager so that if child nodes such as SoundWire
+ * requests for clock, resource manager can honor the request
+ */
+ resmgr = wcd_resmgr_init(&tasha->wcd9xxx->core_res, NULL);
+ if (IS_ERR(resmgr)) {
+ ret = PTR_ERR(resmgr);
+ dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n",
+ __func__);
+ goto err_resmgr;
+ }
+ tasha->resmgr = resmgr;
+ tasha->swr_plat_data.handle = (void *) tasha;
+ tasha->swr_plat_data.read = tasha_swrm_read;
+ tasha->swr_plat_data.write = tasha_swrm_write;
+ tasha->swr_plat_data.bulk_write = tasha_swrm_bulk_write;
+ tasha->swr_plat_data.clk = tasha_swrm_clock;
+ tasha->swr_plat_data.handle_irq = tasha_swrm_handle_irq;
+
+ /* Register for Clock */
+ wcd_ext_clk = clk_get(tasha->wcd9xxx->dev, "wcd_clk");
+ if (IS_ERR(wcd_ext_clk)) {
+ dev_err(tasha->wcd9xxx->dev, "%s: clk get %s failed\n",
+ __func__, "wcd_ext_clk");
+ goto err_clk;
+ }
+ tasha->wcd_ext_clk = wcd_ext_clk;
+ tasha->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV;
+ set_bit(AUDIO_NOMINAL, &tasha->status_mask);
+ tasha->sido_ccl_cnt = 0;
+
+ /* Register native clk for 44.1 playback */
+ wcd_native_clk = clk_get(tasha->wcd9xxx->dev, "wcd_native_clk");
+ if (IS_ERR(wcd_native_clk))
+ dev_dbg(tasha->wcd9xxx->dev, "%s: clk get %s failed\n",
+ __func__, "wcd_native_clk");
+ else
+ tasha->wcd_native_clk = wcd_native_clk;
+
+ if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha,
+ tasha_dai, ARRAY_SIZE(tasha_dai));
+ else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha,
+ tasha_i2s_dai,
+ ARRAY_SIZE(tasha_i2s_dai));
+ else
+ ret = -EINVAL;
+ if (ret) {
+ dev_err(&pdev->dev, "%s: Codec registration failed, ret = %d\n",
+ __func__, ret);
+ goto err_cdc_reg;
+ }
+ /* Update codec register default values */
+ tasha_update_reg_defaults(tasha);
+ schedule_work(&tasha->tasha_add_child_devices_work);
+ tasha_get_codec_ver(tasha);
+
+ dev_info(&pdev->dev, "%s: Tasha driver probe done\n", __func__);
+ return ret;
+
+err_cdc_reg:
+ clk_put(tasha->wcd_ext_clk);
+ if (tasha->wcd_native_clk)
+ clk_put(tasha->wcd_native_clk);
+err_clk:
+ wcd_resmgr_remove(tasha->resmgr);
+err_resmgr:
+ devm_kfree(&pdev->dev, cdc_pwr);
+err_cdc_pwr:
+ mutex_destroy(&tasha->mclk_lock);
+ devm_kfree(&pdev->dev, tasha);
+ return ret;
+}
+
+static int tasha_remove(struct platform_device *pdev)
+{
+ struct tasha_priv *tasha;
+
+ tasha = platform_get_drvdata(pdev);
+
+ mutex_destroy(&tasha->codec_mutex);
+ clk_put(tasha->wcd_ext_clk);
+ if (tasha->wcd_native_clk)
+ clk_put(tasha->wcd_native_clk);
+ mutex_destroy(&tasha->mclk_lock);
+ devm_kfree(&pdev->dev, tasha);
+ snd_soc_unregister_codec(&pdev->dev);
+ mutex_destroy(&tasha->sb_clk_gear_lock);
+ return 0;
+}
+
+static struct platform_driver tasha_codec_driver = {
+ .probe = tasha_probe,
+ .remove = tasha_remove,
+ .driver = {
+ .name = "tasha_codec",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &tasha_pm_ops,
+#endif
+ },
+};
+
+module_platform_driver(tasha_codec_driver);
+
+MODULE_DESCRIPTION("Tasha Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h
new file mode 100644
index 000000000000..8d38399ba92f
--- /dev/null
+++ b/sound/soc/codecs/wcd9335.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef WCD9335_H
+#define WCD9335_H
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/apr_audio-v2.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include "wcd-mbhc-v2.h"
+
+#define TASHA_REG_VAL(reg, val) {reg, 0, val}
+
+#define TASHA_REGISTER_START_OFFSET 0x800
+#define TASHA_SB_PGD_PORT_RX_BASE 0x40
+#define TASHA_SB_PGD_PORT_TX_BASE 0x50
+
+#define TASHA_ZDET_SUPPORTED true
+/* z value defined in milliohm */
+#define TASHA_ZDET_VAL_32 32000
+#define TASHA_ZDET_VAL_400 400000
+#define TASHA_ZDET_VAL_1200 1200000
+#define TASHA_ZDET_VAL_100K 100000000
+/* z floating defined in ohms */
+#define TASHA_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE
+
+#define WCD9335_DMIC_CLK_DIV_2 0x0
+#define WCD9335_DMIC_CLK_DIV_3 0x1
+#define WCD9335_DMIC_CLK_DIV_4 0x2
+#define WCD9335_DMIC_CLK_DIV_6 0x3
+#define WCD9335_DMIC_CLK_DIV_8 0x4
+#define WCD9335_DMIC_CLK_DIV_16 0x5
+#define WCD9335_DMIC_CLK_DRIVE_DEFAULT 0x02
+
+#define WCD9335_ANC_DMIC_X2_FULL_RATE 1
+#define WCD9335_ANC_DMIC_X2_HALF_RATE 0
+
+/* Number of input and output Slimbus port */
+enum {
+ TASHA_RX0 = 0,
+ TASHA_RX1,
+ TASHA_RX2,
+ TASHA_RX3,
+ TASHA_RX4,
+ TASHA_RX5,
+ TASHA_RX6,
+ TASHA_RX7,
+ TASHA_RX8,
+ TASHA_RX9,
+ TASHA_RX10,
+ TASHA_RX11,
+ TASHA_RX12,
+ TASHA_RX_MAX,
+};
+
+enum {
+ TASHA_TX0 = 0,
+ TASHA_TX1,
+ TASHA_TX2,
+ TASHA_TX3,
+ TASHA_TX4,
+ TASHA_TX5,
+ TASHA_TX6,
+ TASHA_TX7,
+ TASHA_TX8,
+ TASHA_TX9,
+ TASHA_TX10,
+ TASHA_TX11,
+ TASHA_TX12,
+ TASHA_TX13,
+ TASHA_TX14,
+ TASHA_TX15,
+ TASHA_TX_MAX,
+};
+
+enum {
+ /* INTR_REG 0 */
+ WCD9335_IRQ_FLL_LOCK_LOSS = 1,
+ WCD9335_IRQ_HPH_PA_OCPL_FAULT,
+ WCD9335_IRQ_HPH_PA_OCPR_FAULT,
+ WCD9335_IRQ_EAR_PA_OCP_FAULT,
+ WCD9335_IRQ_HPH_PA_CNPL_COMPLETE,
+ WCD9335_IRQ_HPH_PA_CNPR_COMPLETE,
+ WCD9335_IRQ_EAR_PA_CNP_COMPLETE,
+ /* INTR_REG 1 */
+ WCD9335_IRQ_MBHC_SW_DET,
+ WCD9335_IRQ_MBHC_ELECT_INS_REM_DET,
+ WCD9335_IRQ_MBHC_BUTTON_PRESS_DET,
+ WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET,
+ WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+ WCD9335_IRQ_RESERVED_0,
+ WCD9335_IRQ_RESERVED_1,
+ WCD9335_IRQ_RESERVED_2,
+ /* INTR_REG 2 */
+ WCD9335_IRQ_LINE_PA1_CNP_COMPLETE,
+ WCD9335_IRQ_LINE_PA2_CNP_COMPLETE,
+ WCD9335_IRQ_LINE_PA3_CNP_COMPLETE,
+ WCD9335_IRQ_LINE_PA4_CNP_COMPLETE,
+ WCD9335_IRQ_SOUNDWIRE,
+ WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE,
+ WCD9335_IRQ_RCO_ERROR,
+ WCD9335_IRQ_SVA_ERROR,
+ /* INTR_REG 3 */
+ WCD9335_IRQ_MAD_AUDIO,
+ WCD9335_IRQ_MAD_BEACON,
+ WCD9335_IRQ_MAD_ULTRASOUND,
+ WCD9335_IRQ_VBAT_ATTACK,
+ WCD9335_IRQ_VBAT_RESTORE,
+ WCD9335_IRQ_SVA_OUTBOX1,
+ WCD9335_IRQ_SVA_OUTBOX2,
+ WCD9335_NUM_IRQS,
+};
+
+enum wcd9335_codec_event {
+ WCD9335_CODEC_EVENT_CODEC_UP = 0,
+};
+
+enum tasha_on_demand_supply {
+ ON_DEMAND_MICBIAS = 0,
+ ON_DEMAND_SUPPLIES_MAX,
+};
+
+/* structure used to put the defined
+ * ondemand supply for codec
+ * and count being used.
+ */
+struct on_demand_supply {
+ struct regulator *supply;
+ int ondemand_supply_count;
+};
+
+/* Dai data structure holds the
+ * dai specific info like rate,
+ * channel number etc.
+ */
+struct tasha_codec_dai_data {
+ u32 rate;
+ u32 *ch_num;
+ u32 ch_act;
+ u32 ch_tot;
+};
+
+/* Structure used to update codec
+ * register defaults after reset
+ */
+struct tasha_reg_mask_val {
+ u16 reg;
+ u8 mask;
+ u8 val;
+};
+
+/* Selects compander and smart boost settings
+ * for a given speaker mode
+ */
+enum {
+ SPKR_MODE_DEFAULT,
+ SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */
+};
+
+/*
+ * Rx path gain offsets
+ */
+enum {
+ RX_GAIN_OFFSET_M1P5_DB,
+ RX_GAIN_OFFSET_0_DB,
+};
+
+extern void *tasha_get_afe_config(struct snd_soc_codec *codec,
+ enum afe_config_type config_type);
+extern int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+extern int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+extern int tasha_enable_efuse_sensing(struct snd_soc_codec *codec);
+extern int tasha_mbhc_hs_detect(struct snd_soc_codec *codec,
+ struct wcd_mbhc_config *mbhc_cfg);
+extern void tasha_mbhc_hs_detect_exit(struct snd_soc_codec *codec);
+extern void tasha_mbhc_zdet_gpio_ctrl(
+ int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high),
+ struct snd_soc_codec *codec);
+extern int tasha_codec_info_create_codec_entry(struct snd_info_entry *,
+ struct snd_soc_codec *);
+extern void tasha_event_register(
+ int (*machine_event_cb)(struct snd_soc_codec *codec,
+ enum wcd9335_codec_event),
+ struct snd_soc_codec *codec);
+extern int tasha_codec_enable_standalone_micbias(struct snd_soc_codec *codec,
+ int micb_num,
+ bool enable);
+extern int tasha_set_spkr_mode(struct snd_soc_codec *codec, int mode);
+extern int tasha_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset);
+extern enum codec_variant tasha_codec_ver(void);
+#endif
diff --git a/sound/soc/codecs/wcd934x/Makefile b/sound/soc/codecs/wcd934x/Makefile
new file mode 100644
index 000000000000..2843fa11d58e
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for wcd934x codec driver.
+#
+snd-soc-wcd934x-objs := wcd934x.o wcd934x-dsp-cntl.o
+obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o
+snd-soc-wcd934x-mbhc-objs := wcd934x-mbhc.o
+obj-$(CONFIG_SND_SOC_WCD934X_MBHC) += snd-soc-wcd934x-mbhc.o
+snd-soc-wcd934x-dsd-objs := wcd934x-dsd.o
+obj-$(CONFIG_SND_SOC_WCD934X_DSD) += snd-soc-wcd934x-dsd.o
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
new file mode 100644
index 000000000000..3e23e3749bda
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
@@ -0,0 +1,772 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <sound/tlv.h>
+#include <sound/control.h>
+#include "wcd934x-dsd.h"
+
+#define DSD_VOLUME_MAX_0dB 0
+#define DSD_VOLUME_MIN_M110dB -110
+
+#define DSD_VOLUME_RANGE_CHECK(x) ((x >= DSD_VOLUME_MIN_M110dB) &&\
+ (x <= DSD_VOLUME_MAX_0dB))
+#define DSD_VOLUME_STEPS 3
+#define DSD_VOLUME_UPDATE_DELAY_MS 30
+#define DSD_VOLUME_USLEEP_MARGIN_US 100
+#define DSD_VOLUME_STEP_DELAY_US ((1000 * DSD_VOLUME_UPDATE_DELAY_MS) / \
+ (2 * DSD_VOLUME_STEPS))
+
+#define TAVIL_VERSION_1_0 0
+#define TAVIL_VERSION_1_1 1
+
+static const DECLARE_TLV_DB_MINMAX(tavil_dsd_db_scale, DSD_VOLUME_MIN_M110dB,
+ DSD_VOLUME_MAX_0dB);
+
+static const char *const dsd_if_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7",
+ "DSD_DATA_PAD"
+};
+
+static const char * const dsd_filt0_mux_text[] = {
+ "ZERO", "DSD_L IF MUX",
+};
+
+static const char * const dsd_filt1_mux_text[] = {
+ "ZERO", "DSD_R IF MUX",
+};
+
+static const struct soc_enum dsd_filt0_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_DSD0_PATH_CTL, 0,
+ ARRAY_SIZE(dsd_filt0_mux_text), dsd_filt0_mux_text);
+
+static const struct soc_enum dsd_filt1_mux_enum =
+ SOC_ENUM_SINGLE(WCD934X_CDC_DSD1_PATH_CTL, 0,
+ ARRAY_SIZE(dsd_filt1_mux_text), dsd_filt1_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(dsd_l_if_enum, WCD934X_CDC_DSD0_CFG0,
+ 2, dsd_if_text);
+static SOC_ENUM_SINGLE_DECL(dsd_r_if_enum, WCD934X_CDC_DSD1_CFG0,
+ 2, dsd_if_text);
+
+static const struct snd_kcontrol_new dsd_filt0_mux =
+ SOC_DAPM_ENUM("DSD Filt0 Mux", dsd_filt0_mux_enum);
+
+static const struct snd_kcontrol_new dsd_filt1_mux =
+ SOC_DAPM_ENUM("DSD Filt1 Mux", dsd_filt1_mux_enum);
+
+static const struct snd_kcontrol_new dsd_l_if_mux =
+ SOC_DAPM_ENUM("DSD Left If Mux", dsd_l_if_enum);
+static const struct snd_kcontrol_new dsd_r_if_mux =
+ SOC_DAPM_ENUM("DSD Right If Mux", dsd_r_if_enum);
+
+static const struct snd_soc_dapm_route tavil_dsd_audio_map[] = {
+ {"DSD_L IF MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"DSD_L IF MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"DSD_L IF MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"DSD_L IF MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"DSD_L IF MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"DSD_L IF MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"DSD_L IF MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"DSD_L IF MUX", "RX7", "CDC_IF RX7 MUX"},
+
+ {"DSD_FILTER_0", NULL, "DSD_L IF MUX"},
+ {"DSD_FILTER_0", NULL, "RX INT1 NATIVE SUPPLY"},
+ {"RX INT1 MIX3", "DSD HPHL Switch", "DSD_FILTER_0"},
+
+ {"DSD_R IF MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"DSD_R IF MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"DSD_R IF MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"DSD_R IF MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"DSD_R IF MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"DSD_R IF MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"DSD_R IF MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"DSD_R IF MUX", "RX7", "CDC_IF RX7 MUX"},
+
+ {"DSD_FILTER_1", NULL, "DSD_R IF MUX"},
+ {"DSD_FILTER_1", NULL, "RX INT2 NATIVE SUPPLY"},
+ {"RX INT2 MIX3", "DSD HPHR Switch", "DSD_FILTER_1"},
+
+ {"DSD_FILTER_0", NULL, "RX INT3 NATIVE SUPPLY"},
+ {"RX INT3 MIX3", "DSD LO1 Switch", "DSD_FILTER_0"},
+ {"DSD_FILTER_1", NULL, "RX INT4 NATIVE SUPPLY"},
+ {"RX INT4 MIX3", "DSD LO2 Switch", "DSD_FILTER_1"},
+};
+
+static bool is_valid_dsd_interpolator(int interp_num)
+{
+ if ((interp_num == INTERP_HPHL) || (interp_num == INTERP_HPHR) ||
+ (interp_num == INTERP_LO1) || (interp_num == INTERP_LO2))
+ return true;
+
+ return false;
+}
+
+/**
+ * tavil_dsd_set_mixer_value - Set DSD HPH/LO mixer value
+ *
+ * @dsd_conf: pointer to dsd config
+ * @interp_num: Interpolator number (HPHL/R, LO1/2)
+ * @sw_value: Mixer switch value
+ *
+ * Returns 0 on success or -EINVAL on failure
+ */
+int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num, int sw_value)
+{
+ if (!dsd_conf)
+ return -EINVAL;
+
+ if (!is_valid_dsd_interpolator(interp_num))
+ return -EINVAL;
+
+ dsd_conf->dsd_interp_mixer[interp_num] = !!sw_value;
+
+ return 0;
+}
+EXPORT_SYMBOL(tavil_dsd_set_mixer_value);
+
+/**
+ * tavil_dsd_get_current_mixer_value - Get DSD HPH/LO mixer value
+ *
+ * @dsd_conf: pointer to dsd config
+ * @interp_num: Interpolator number (HPHL/R, LO1/2)
+ *
+ * Returns current mixer val for success or -EINVAL for failure
+ */
+int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num)
+{
+ if (!dsd_conf)
+ return -EINVAL;
+
+ if (!is_valid_dsd_interpolator(interp_num))
+ return -EINVAL;
+
+ return dsd_conf->dsd_interp_mixer[interp_num];
+}
+EXPORT_SYMBOL(tavil_dsd_get_current_mixer_value);
+
+/**
+ * tavil_dsd_set_out_select - DSD0/1 out select to HPH or LO
+ *
+ * @dsd_conf: pointer to dsd config
+ * @interp_num: Interpolator number (HPHL/R, LO1/2)
+ *
+ * Returns 0 for success or -EINVAL for failure
+ */
+int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf,
+ int interp_num)
+{
+ unsigned int reg, val;
+ struct snd_soc_codec *codec;
+
+ if (!dsd_conf || !dsd_conf->codec)
+ return -EINVAL;
+
+ codec = dsd_conf->codec;
+
+ if (!is_valid_dsd_interpolator(interp_num)) {
+ dev_err(codec->dev, "%s: Invalid Interpolator: %d for DSD\n",
+ __func__, interp_num);
+ return -EINVAL;
+ }
+
+ switch (interp_num) {
+ case INTERP_HPHL:
+ reg = WCD934X_CDC_DSD0_CFG0;
+ val = 0x00;
+ break;
+ case INTERP_HPHR:
+ reg = WCD934X_CDC_DSD1_CFG0;
+ val = 0x00;
+ break;
+ case INTERP_LO1:
+ reg = WCD934X_CDC_DSD0_CFG0;
+ val = 0x02;
+ break;
+ case INTERP_LO2:
+ reg = WCD934X_CDC_DSD1_CFG0;
+ val = 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, reg, 0x02, val);
+
+ return 0;
+}
+EXPORT_SYMBOL(tavil_dsd_set_out_select);
+
+/**
+ * tavil_dsd_reset - Reset DSD block
+ *
+ * @dsd_conf: pointer to dsd config
+ *
+ */
+void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf)
+{
+ if (!dsd_conf || !dsd_conf->codec)
+ return;
+
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x02, 0x02);
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x01, 0x00);
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x02, 0x02);
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x01, 0x00);
+}
+EXPORT_SYMBOL(tavil_dsd_reset);
+
+/**
+ * tavil_dsd_set_interp_rate - Set interpolator rate for DSD
+ *
+ * @dsd_conf: pointer to dsd config
+ * @rx_port: RX port number
+ * @sample_rate: Sample rate of the RX interpolator
+ * @sample_rate_val: Interpolator rate value
+ */
+void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port,
+ u32 sample_rate, u8 sample_rate_val)
+{
+ u8 dsd_inp_sel;
+ u8 dsd0_inp, dsd1_inp;
+ u8 val0, val1;
+ u8 dsd0_out_sel, dsd1_out_sel;
+ u16 int_fs_reg, interp_num = 0;
+ struct snd_soc_codec *codec;
+
+ if (!dsd_conf || !dsd_conf->codec)
+ return;
+
+ codec = dsd_conf->codec;
+
+ dsd_inp_sel = DSD_INP_SEL_RX0 + rx_port - WCD934X_RX_PORT_START_NUMBER;
+
+ val0 = snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0);
+ val1 = snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0);
+ dsd0_inp = (val0 & 0x3C) >> 2;
+ dsd1_inp = (val1 & 0x3C) >> 2;
+ dsd0_out_sel = (val0 & 0x02) >> 1;
+ dsd1_out_sel = (val1 & 0x02) >> 1;
+
+ /* Set HPHL or LO1 interp rate based on out select */
+ if (dsd_inp_sel == dsd0_inp) {
+ interp_num = dsd0_out_sel ? INTERP_LO1 : INTERP_HPHL;
+ dsd_conf->base_sample_rate[DSD0] = sample_rate;
+ }
+
+ /* Set HPHR or LO2 interp rate based on out select */
+ if (dsd_inp_sel == dsd1_inp) {
+ interp_num = dsd1_out_sel ? INTERP_LO2 : INTERP_HPHR;
+ dsd_conf->base_sample_rate[DSD1] = sample_rate;
+ }
+
+ if (interp_num) {
+ int_fs_reg = WCD934X_CDC_RX0_RX_PATH_CTL + 20 * interp_num;
+ if ((snd_soc_read(codec, int_fs_reg) & 0x0f) < 0x09) {
+ dev_dbg(codec->dev, "%s: Set Interp %d to sample_rate val 0x%x\n",
+ __func__, interp_num, sample_rate_val);
+ snd_soc_update_bits(codec, int_fs_reg, 0x0F,
+ sample_rate_val);
+ }
+ }
+}
+EXPORT_SYMBOL(tavil_dsd_set_interp_rate);
+
+static int tavil_set_dsd_mode(struct snd_soc_codec *codec, int dsd_num,
+ u8 *pcm_rate_val)
+{
+ unsigned int dsd_out_sel_reg;
+ u8 dsd_mode;
+ u32 sample_rate;
+ struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
+
+ if (!dsd_conf)
+ return -EINVAL;
+
+ if ((dsd_num < 0) || (dsd_num > 1))
+ return -EINVAL;
+
+ sample_rate = dsd_conf->base_sample_rate[dsd_num];
+ dsd_out_sel_reg = WCD934X_CDC_DSD0_CFG0 + dsd_num * 16;
+
+ switch (sample_rate) {
+ case 176400:
+ dsd_mode = 0; /* DSD_64 */
+ *pcm_rate_val = 0xb;
+ break;
+ case 352800:
+ dsd_mode = 1; /* DSD_128 */
+ *pcm_rate_val = 0xc;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid DSD rate: %d\n",
+ __func__, sample_rate);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, dsd_out_sel_reg, 0x01, dsd_mode);
+
+ return 0;
+}
+
+static void tavil_dsd_data_pull(struct snd_soc_codec *codec, int dsd_num,
+ u8 pcm_rate_val, bool enable)
+{
+ u8 clk_en, mute_en;
+ u8 dsd_inp_sel;
+
+ if (enable) {
+ clk_en = 0x20;
+ mute_en = 0x10;
+ } else {
+ clk_en = 0x00;
+ mute_en = 0x00;
+ }
+
+ if (dsd_num & 0x01) {
+ snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_MIX_CTL,
+ 0x20, clk_en);
+ dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) &
+ 0x3C) >> 2;
+ dsd_inp_sel = (enable) ? dsd_inp_sel : 0;
+ if (dsd_inp_sel < 9) {
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1,
+ 0x0F, dsd_inp_sel);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX7_RX_PATH_MIX_CTL,
+ 0x0F, pcm_rate_val);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX7_RX_PATH_MIX_CTL,
+ 0x10, mute_en);
+ }
+ }
+ if (dsd_num & 0x02) {
+ snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_MIX_CTL,
+ 0x20, clk_en);
+ dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) &
+ 0x3C) >> 2;
+ dsd_inp_sel = (enable) ? dsd_inp_sel : 0;
+ if (dsd_inp_sel < 9) {
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1,
+ 0x0F, dsd_inp_sel);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX8_RX_PATH_MIX_CTL,
+ 0x0F, pcm_rate_val);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX8_RX_PATH_MIX_CTL,
+ 0x10, mute_en);
+ }
+ }
+}
+
+static void tavil_dsd_update_volume(struct tavil_dsd_config *dsd_conf)
+{
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0,
+ 0x01, 0x01);
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0,
+ 0x01, 0x00);
+}
+
+static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
+ int rc, clk_users;
+ int interp_idx;
+ u8 pcm_rate_val;
+
+ if (!dsd_conf) {
+ dev_err(codec->dev, "%s: null dsd_config pointer\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "%s: DSD%d, event: %d\n", __func__,
+ w->shift, event);
+
+ if (w->shift == DSD0) {
+ /* Read out select */
+ if (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x02)
+ interp_idx = INTERP_LO1;
+ else
+ interp_idx = INTERP_HPHL;
+ } else if (w->shift == DSD1) {
+ /* Read out select */
+ if (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x02)
+ interp_idx = INTERP_LO2;
+ else
+ interp_idx = INTERP_HPHR;
+ } else {
+ dev_err(codec->dev, "%s: Unsupported DSD:%d\n",
+ __func__, w->shift);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ clk_users = tavil_codec_enable_interp_clk(codec, event,
+ interp_idx);
+
+ rc = tavil_set_dsd_mode(codec, w->shift, &pcm_rate_val);
+ if (rc)
+ return rc;
+
+ tavil_dsd_data_pull(codec, (1 << w->shift), pcm_rate_val,
+ true);
+
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x01,
+ 0x01);
+ if (w->shift == DSD0) {
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x01, 0x01);
+ /* Apply Gain */
+ snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1,
+ dsd_conf->volume[DSD0]);
+ if (dsd_conf->version == TAVIL_VERSION_1_1)
+ tavil_dsd_update_volume(dsd_conf);
+
+ } else if (w->shift == DSD1) {
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x01, 0x01);
+ /* Apply Gain */
+ snd_soc_write(codec, WCD934X_CDC_DSD1_CFG1,
+ dsd_conf->volume[DSD1]);
+ if (dsd_conf->version == TAVIL_VERSION_1_1)
+ tavil_dsd_update_volume(dsd_conf);
+ }
+ /* 10msec sleep required after DSD clock is set */
+ usleep_range(10000, 10100);
+
+ if (clk_users > 1) {
+ snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
+ if (w->shift == DSD0)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x00);
+ if (w->shift == DSD1)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_DSD1_CFG2,
+ 0x04, 0x00);
+
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (w->shift == DSD0) {
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+ 0x01, 0x00);
+ } else if (w->shift == DSD1) {
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x01, 0x00);
+ }
+
+ tavil_codec_enable_interp_clk(codec, event, interp_idx);
+
+ if (!(snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01) &&
+ !(snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) {
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL,
+ 0x01, 0x00);
+ tavil_dsd_data_pull(codec, 0x03, 0x04, false);
+ tavil_dsd_reset(dsd_conf);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int tavil_dsd_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = DSD_VOLUME_MIN_M110dB;
+ uinfo->value.integer.max = DSD_VOLUME_MAX_0dB;
+
+ return 0;
+}
+
+static int tavil_dsd_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
+ int nv[DSD_MAX], cv[DSD_MAX];
+ int step_size, nv1;
+ int i, dsd_idx;
+
+ if (!dsd_conf)
+ return 0;
+
+ mutex_lock(&dsd_conf->vol_mutex);
+
+ for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) {
+ cv[dsd_idx] = dsd_conf->volume[dsd_idx];
+ nv[dsd_idx] = ucontrol->value.integer.value[dsd_idx];
+ }
+
+ if ((!DSD_VOLUME_RANGE_CHECK(nv[DSD0])) ||
+ (!DSD_VOLUME_RANGE_CHECK(nv[DSD1])))
+ goto done;
+
+ for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) {
+ if (cv[dsd_idx] == nv[dsd_idx])
+ continue;
+
+ dev_dbg(codec->dev, "%s: DSD%d cur.vol: %d, new vol: %d\n",
+ __func__, dsd_idx, cv[dsd_idx], nv[dsd_idx]);
+
+ step_size = (nv[dsd_idx] - cv[dsd_idx]) /
+ DSD_VOLUME_STEPS;
+
+ nv1 = cv[dsd_idx];
+
+ for (i = 0; i < DSD_VOLUME_STEPS; i++) {
+ nv1 += step_size;
+ snd_soc_write(codec,
+ WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx,
+ nv1);
+ if (dsd_conf->version == TAVIL_VERSION_1_1)
+ tavil_dsd_update_volume(dsd_conf);
+
+ /* sleep required after each volume step */
+ usleep_range(DSD_VOLUME_STEP_DELAY_US,
+ (DSD_VOLUME_STEP_DELAY_US +
+ DSD_VOLUME_USLEEP_MARGIN_US));
+ }
+ if (nv1 != nv[dsd_idx]) {
+ snd_soc_write(codec,
+ WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx,
+ nv[dsd_idx]);
+
+ if (dsd_conf->version == TAVIL_VERSION_1_1)
+ tavil_dsd_update_volume(dsd_conf);
+ }
+
+ dsd_conf->volume[dsd_idx] = nv[dsd_idx];
+ }
+
+done:
+ mutex_unlock(&dsd_conf->vol_mutex);
+
+ return 0;
+}
+
+static int tavil_dsd_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
+
+ if (dsd_conf) {
+ ucontrol->value.integer.value[0] = dsd_conf->volume[DSD0];
+ ucontrol->value.integer.value[1] = dsd_conf->volume[DSD1];
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new tavil_dsd_vol_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "DSD Volume",
+ .info = tavil_dsd_vol_info,
+ .get = tavil_dsd_vol_get,
+ .put = tavil_dsd_vol_put,
+ .tlv = { .p = tavil_dsd_db_scale },
+ },
+};
+
+static const struct snd_soc_dapm_widget tavil_dsd_widgets[] = {
+ SND_SOC_DAPM_MUX("DSD_L IF MUX", SND_SOC_NOPM, 0, 0, &dsd_l_if_mux),
+ SND_SOC_DAPM_MUX_E("DSD_FILTER_0", SND_SOC_NOPM, 0, 0, &dsd_filt0_mux,
+ tavil_enable_dsd,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("DSD_R IF MUX", SND_SOC_NOPM, 0, 0, &dsd_r_if_mux),
+ SND_SOC_DAPM_MUX_E("DSD_FILTER_1", SND_SOC_NOPM, 1, 0, &dsd_filt1_mux,
+ tavil_enable_dsd,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+/**
+ * tavil_dsd_post_ssr_init - DSD intialization after subsystem restart
+ *
+ * @codec: pointer to snd_soc_codec
+ *
+ * Returns 0 on success or error on failure
+ */
+int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_conf)
+{
+ struct snd_soc_codec *codec;
+
+ if (!dsd_conf || !dsd_conf->codec)
+ return -EINVAL;
+
+ codec = dsd_conf->codec;
+ /* Disable DSD Interrupts */
+ snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08);
+
+ /* DSD registers init */
+ if (dsd_conf->version == TAVIL_VERSION_1_0) {
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00);
+ }
+ /* DSD0: Mute EN */
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04);
+ /* DSD1: Mute EN */
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E,
+ 0x0A);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E,
+ 0x0A);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07,
+ 0x04);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07,
+ 0x04);
+
+ /* Enable DSD Interrupts */
+ snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00);
+
+ return 0;
+}
+EXPORT_SYMBOL(tavil_dsd_post_ssr_init);
+
+/**
+ * tavil_dsd_init - DSD intialization
+ *
+ * @codec: pointer to snd_soc_codec
+ *
+ * Returns pointer to tavil_dsd_config for success or NULL for failure
+ */
+struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm;
+ struct tavil_dsd_config *dsd_conf;
+ u8 val;
+
+ if (!codec)
+ return NULL;
+
+ dapm = snd_soc_codec_get_dapm(codec);
+
+ /* Read efuse register to check if DSD is supported */
+ val = snd_soc_read(codec, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14);
+ if (val & 0x80) {
+ dev_info(codec->dev, "%s: DSD unsupported for this codec version\n",
+ __func__);
+ return NULL;
+ }
+
+ dsd_conf = devm_kzalloc(codec->dev, sizeof(struct tavil_dsd_config),
+ GFP_KERNEL);
+ if (!dsd_conf)
+ return NULL;
+
+ dsd_conf->codec = codec;
+
+ /* Read version */
+ dsd_conf->version = snd_soc_read(codec,
+ WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0);
+ /* DSD registers init */
+ if (dsd_conf->version == TAVIL_VERSION_1_0) {
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00);
+ }
+ /* DSD0: Mute EN */
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04);
+ /* DSD1: Mute EN */
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E,
+ 0x0A);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E,
+ 0x0A);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07,
+ 0x04);
+ snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07,
+ 0x04);
+
+ snd_soc_dapm_new_controls(dapm, tavil_dsd_widgets,
+ ARRAY_SIZE(tavil_dsd_widgets));
+
+ snd_soc_dapm_add_routes(dapm, tavil_dsd_audio_map,
+ ARRAY_SIZE(tavil_dsd_audio_map));
+
+ mutex_init(&dsd_conf->vol_mutex);
+ dsd_conf->volume[DSD0] = DSD_VOLUME_MAX_0dB;
+ dsd_conf->volume[DSD1] = DSD_VOLUME_MAX_0dB;
+
+ snd_soc_add_codec_controls(codec, tavil_dsd_vol_controls,
+ ARRAY_SIZE(tavil_dsd_vol_controls));
+
+ /* Enable DSD Interrupts */
+ snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00);
+
+ return dsd_conf;
+}
+EXPORT_SYMBOL(tavil_dsd_init);
+
+/**
+ * tavil_dsd_deinit - DSD de-intialization
+ *
+ * @dsd_conf: pointer to tavil_dsd_config
+ */
+void tavil_dsd_deinit(struct tavil_dsd_config *dsd_conf)
+{
+ struct snd_soc_codec *codec;
+
+ if (!dsd_conf)
+ return;
+
+ codec = dsd_conf->codec;
+
+ mutex_destroy(&dsd_conf->vol_mutex);
+
+ /* Disable DSD Interrupts */
+ snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08);
+
+ devm_kfree(codec->dev, dsd_conf);
+}
+EXPORT_SYMBOL(tavil_dsd_deinit);
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.h b/sound/soc/codecs/wcd934x/wcd934x-dsd.h
new file mode 100644
index 000000000000..498288335b3b
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.h
@@ -0,0 +1,97 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD934X_DSD_H__
+#define __WCD934X_DSD_H__
+
+#include <sound/soc.h>
+#include "wcd934x.h"
+
+enum {
+ DSD0,
+ DSD1,
+ DSD_MAX,
+};
+
+enum {
+ DSD_INP_SEL_ZERO = 0,
+ DSD_INP_SEL_RX0,
+ DSD_INP_SEL_RX1,
+ DSD_INP_SEL_RX2,
+ DSD_INP_SEL_RX3,
+ DSD_INP_SEL_RX4,
+ DSD_INP_SEL_RX5,
+ DSD_INP_SEL_RX6,
+ DSD_INP_SEL_RX7,
+};
+
+struct tavil_dsd_config {
+ struct snd_soc_codec *codec;
+ unsigned int dsd_interp_mixer[INTERP_MAX];
+ u32 base_sample_rate[DSD_MAX];
+ int volume[DSD_MAX];
+ struct mutex vol_mutex;
+ int version;
+};
+
+#ifdef CONFIG_SND_SOC_WCD934X_DSD
+int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num, int sw_value);
+int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num);
+int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf,
+ int interp_num);
+void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf);
+void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port,
+ u32 sample_rate, u8 sample_rate_val);
+struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec);
+void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config);
+int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config);
+#else
+int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num, int sw_value)
+{
+ return 0;
+}
+
+int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf,
+ int interp_num)
+{
+ return 0;
+}
+
+int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf,
+ int interp_num)
+{
+ return 0;
+}
+
+void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf)
+{ }
+
+void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port,
+ u32 sample_rate, u8 sample_rate_val)
+{ }
+
+struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec)
+{
+ return NULL;
+}
+
+void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config)
+{ }
+int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config)
+{
+ return 0;
+}
+#endif
+#endif
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
new file mode 100644
index 000000000000..078e60fdca87
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
@@ -0,0 +1,1373 @@
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/component.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <sound/soc.h>
+#include <sound/wcd-dsp-mgr.h>
+#include "wcd934x.h"
+#include "wcd934x-dsp-cntl.h"
+
+#define WCD_CNTL_DIR_NAME_LEN_MAX 32
+#define WCD_CPE_FLL_MAX_RETRIES 5
+#define WCD_MEM_ENABLE_MAX_RETRIES 20
+#define WCD_DSP_BOOT_TIMEOUT_MS 3000
+#define WCD_SYSFS_ENTRY_MAX_LEN 8
+#define WCD_PROCFS_ENTRY_MAX_LEN 16
+#define WCD_934X_RAMDUMP_START_ADDR 0x20100000
+#define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128)
+#define WCD_DSP_CNTL_MAX_COUNT 2
+
+#define WCD_CNTL_MUTEX_LOCK(codec, lock) \
+{ \
+ dev_dbg(codec->dev, "%s: mutex_lock(%s)\n", \
+ __func__, __stringify_1(lock)); \
+ mutex_lock(&lock); \
+}
+
+#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \
+{ \
+ dev_dbg(codec->dev, "%s: mutex_unlock(%s)\n", \
+ __func__, __stringify_1(lock)); \
+ mutex_unlock(&lock); \
+}
+
+enum wcd_mem_type {
+ WCD_MEM_TYPE_ALWAYS_ON,
+ WCD_MEM_TYPE_SWITCHABLE,
+};
+
+struct wcd_cntl_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct wcd_dsp_cntl *cntl, char *buf);
+ ssize_t (*store)(struct wcd_dsp_cntl *cntl, const char *buf,
+ ssize_t count);
+};
+
+#define WCD_CNTL_ATTR(_name, _mode, _show, _store) \
+static struct wcd_cntl_attribute cntl_attr_##_name = { \
+ .attr = {.name = __stringify(_name), .mode = _mode}, \
+ .show = _show, \
+ .store = _store, \
+}
+
+#define to_wcd_cntl_attr(a) \
+ container_of((a), struct wcd_cntl_attribute, attr)
+
+#define to_wcd_cntl(kobj) \
+ container_of((kobj), struct wcd_dsp_cntl, wcd_kobj)
+
+static u8 mem_enable_values[] = {
+ 0xFE, 0xFC, 0xF8, 0xF0,
+ 0xE0, 0xC0, 0x80, 0x00,
+};
+
+static ssize_t wdsp_boot_show(struct wcd_dsp_cntl *cntl, char *buf)
+{
+ return snprintf(buf, WCD_SYSFS_ENTRY_MAX_LEN,
+ "%u", cntl->boot_reqs);
+}
+
+static ssize_t wdsp_boot_store(struct wcd_dsp_cntl *cntl,
+ const char *buf, ssize_t count)
+{
+ u32 val;
+ bool vote;
+ int ret;
+
+ ret = kstrtou32(buf, 10, &val);
+ if (ret) {
+ dev_err(cntl->codec->dev,
+ "%s: Invalid entry, ret = %d\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ if (val > 0) {
+ cntl->boot_reqs++;
+ vote = true;
+ } else {
+ cntl->boot_reqs--;
+ vote = false;
+ }
+
+ if (cntl->m_dev && cntl->m_ops &&
+ cntl->m_ops->vote_for_dsp)
+ ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote);
+ else
+ ret = -EINVAL;
+
+ if (IS_ERR_VALUE(ret))
+ dev_err(cntl->codec->dev,
+ "%s: failed to %s dsp\n", __func__,
+ vote ? "enable" : "disable");
+ return count;
+}
+
+WCD_CNTL_ATTR(boot, 0660, wdsp_boot_show, wdsp_boot_store);
+
+static ssize_t wcd_cntl_sysfs_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr);
+ struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj);
+ ssize_t ret = -EINVAL;
+
+ if (cntl && wcd_attr->show)
+ ret = wcd_attr->show(cntl, buf);
+
+ return ret;
+}
+
+static ssize_t wcd_cntl_sysfs_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf,
+ size_t count)
+{
+ struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr);
+ struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj);
+ ssize_t ret = -EINVAL;
+
+ if (cntl && wcd_attr->store)
+ ret = wcd_attr->store(cntl, buf, count);
+
+ return ret;
+}
+
+static const struct sysfs_ops wcd_cntl_sysfs_ops = {
+ .show = wcd_cntl_sysfs_show,
+ .store = wcd_cntl_sysfs_store,
+};
+
+static struct kobj_type wcd_cntl_ktype = {
+ .sysfs_ops = &wcd_cntl_sysfs_ops,
+};
+
+static void wcd_cntl_change_online_state(struct wcd_dsp_cntl *cntl,
+ u8 online)
+{
+ struct wdsp_ssr_entry *ssr_entry = &cntl->ssr_entry;
+ unsigned long ret;
+
+ WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
+ ssr_entry->offline = !online;
+ /* Make sure the write is complete */
+ wmb();
+ ret = xchg(&ssr_entry->offline_change, 1);
+ wake_up_interruptible(&ssr_entry->offline_poll_wait);
+ dev_dbg(cntl->codec->dev,
+ "%s: requested %u, offline %u offline_change %u, ret = %ldn",
+ __func__, online, ssr_entry->offline,
+ ssr_entry->offline_change, ret);
+ WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
+}
+
+static ssize_t wdsp_ssr_entry_read(struct snd_info_entry *entry,
+ void *file_priv_data, struct file *file,
+ char __user *buf, size_t count, loff_t pos)
+{
+ int len = 0;
+ char buffer[WCD_PROCFS_ENTRY_MAX_LEN];
+ struct wcd_dsp_cntl *cntl;
+ struct wdsp_ssr_entry *ssr_entry;
+ ssize_t ret;
+ u8 offline;
+
+ cntl = (struct wcd_dsp_cntl *) entry->private_data;
+ if (!cntl) {
+ pr_err("%s: Invalid private data for SSR procfs entry\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ssr_entry = &cntl->ssr_entry;
+
+ WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
+ offline = ssr_entry->offline;
+ /* Make sure the read is complete */
+ rmb();
+ dev_dbg(cntl->codec->dev, "%s: offline = %s\n", __func__,
+ offline ? "true" : "false");
+ len = snprintf(buffer, sizeof(buffer), "%s\n",
+ offline ? "OFFLINE" : "ONLINE");
+ ret = simple_read_from_buffer(buf, count, &pos, buffer, len);
+ WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
+
+ return ret;
+}
+
+static unsigned int wdsp_ssr_entry_poll(struct snd_info_entry *entry,
+ void *private_data, struct file *file,
+ poll_table *wait)
+{
+ struct wcd_dsp_cntl *cntl;
+ struct wdsp_ssr_entry *ssr_entry;
+ unsigned int ret = 0;
+
+ if (!entry || !entry->private_data) {
+ pr_err("%s: %s is NULL\n", __func__,
+ (!entry) ? "entry" : "private_data");
+ return -EINVAL;
+ }
+
+ cntl = (struct wcd_dsp_cntl *) entry->private_data;
+ ssr_entry = &cntl->ssr_entry;
+
+ dev_dbg(cntl->codec->dev, "%s: Poll wait, offline = %u\n",
+ __func__, ssr_entry->offline);
+ poll_wait(file, &ssr_entry->offline_poll_wait, wait);
+ dev_dbg(cntl->codec->dev, "%s: Woken up Poll wait, offline = %u\n",
+ __func__, ssr_entry->offline);
+
+ WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
+ if (xchg(&ssr_entry->offline_change, 0))
+ ret = POLLIN | POLLPRI | POLLRDNORM;
+ dev_dbg(cntl->codec->dev, "%s: ret (%d) from poll_wait\n",
+ __func__, ret);
+ WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
+
+ return ret;
+}
+
+static struct snd_info_entry_ops wdsp_ssr_entry_ops = {
+ .read = wdsp_ssr_entry_read,
+ .poll = wdsp_ssr_entry_poll,
+};
+
+static int wcd_cntl_cpe_fll_calibrate(struct wcd_dsp_cntl *cntl)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+ int ret = 0, retry = 0;
+ u8 cal_lsb, cal_msb;
+ u8 lock_det;
+
+ /* Make sure clocks are gated */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL,
+ 0x05, 0x00);
+
+ /* Enable CPE FLL reference clock */
+ snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1,
+ 0x80, 0x80);
+
+ snd_soc_update_bits(codec, WCD934X_CPE_FLL_USER_CTL_5,
+ 0xF3, 0x13);
+ snd_soc_write(codec, WCD934X_CPE_FLL_L_VAL_CTL_0, 0x50);
+
+ /* Disable CPAR reset and Enable CPAR clk */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL,
+ 0x02, 0x02);
+
+ /* Write calibration l-value based on cdc clk rate */
+ if (cntl->clk_rate == 9600000) {
+ cal_lsb = 0x6d;
+ cal_msb = 0x00;
+ } else {
+ cal_lsb = 0x56;
+ cal_msb = 0x00;
+ }
+ snd_soc_write(codec, WCD934X_CPE_FLL_USER_CTL_6, cal_lsb);
+ snd_soc_write(codec, WCD934X_CPE_FLL_USER_CTL_7, cal_msb);
+
+ /* FLL mode to follow power up sequence */
+ snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE,
+ 0x60, 0x00);
+
+ /* HW controlled CPE FLL */
+ snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE,
+ 0x80, 0x80);
+
+ /* Force on CPE FLL */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG,
+ 0x04, 0x04);
+
+ do {
+ /* Time for FLL calibration to complete */
+ usleep_range(1000, 1100);
+ lock_det = snd_soc_read(codec, WCD934X_CPE_FLL_STATUS_3);
+ retry++;
+ } while (!(lock_det & 0x01) &&
+ retry <= WCD_CPE_FLL_MAX_RETRIES);
+
+ if (!(lock_det & 0x01)) {
+ dev_err(codec->dev, "%s: lock detect not set, 0x%02x\n",
+ __func__, lock_det);
+ ret = -EIO;
+ goto err_lock_det;
+ }
+
+ snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE,
+ 0x60, 0x20);
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG,
+ 0x04, 0x00);
+ return ret;
+
+err_lock_det:
+ /* Undo the register settings */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG,
+ 0x04, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE,
+ 0x80, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL,
+ 0x02, 0x00);
+ return ret;
+}
+
+static void wcd_cntl_config_cpar(struct wcd_dsp_cntl *cntl)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+ u8 nom_lo, nom_hi, svs2_lo, svs2_hi;
+
+ /* Configure CPAR */
+ nom_hi = svs2_hi = 0;
+ if (cntl->clk_rate == 9600000) {
+ nom_lo = 0x90;
+ svs2_lo = 0x50;
+ } else {
+ nom_lo = 0x70;
+ svs2_lo = 0x3e;
+ }
+
+ snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_NOM_LOW, nom_lo);
+ snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_NOM_HIGH, nom_hi);
+ snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW, svs2_lo);
+ snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH, svs2_hi);
+
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_PWR_CPEFLL_CTL,
+ 0x03, 0x03);
+}
+
+static int wcd_cntl_cpe_fll_ctrl(struct wcd_dsp_cntl *cntl,
+ bool enable)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+ int ret = 0;
+
+ if (enable) {
+ ret = wcd_cntl_cpe_fll_calibrate(cntl);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev,
+ "%s: cpe_fll_cal failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ wcd_cntl_config_cpar(cntl);
+
+ /* Enable AHB CLK and CPE CLK*/
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL,
+ 0x05, 0x05);
+ } else {
+ /* Disable AHB CLK and CPE CLK */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL,
+ 0x05, 0x00);
+ /* Reset the CPAR mode for CPE FLL */
+ snd_soc_write(codec, WCD934X_CPE_FLL_FLL_MODE, 0x20);
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG,
+ 0x04, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL,
+ 0x02, 0x00);
+ }
+done:
+ return ret;
+}
+
+static int wcd_cntl_clocks_enable(struct wcd_dsp_cntl *cntl)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+ int ret;
+
+ WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex);
+ /* Enable codec clock */
+ if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en)
+ ret = cntl->cdc_cb->cdc_clk_en(codec, true);
+ else
+ ret = -EINVAL;
+
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev,
+ "%s: Failed to enable cdc clk, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+ /* Pull CPAR out of reset */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x04, 0x00);
+
+ /* Configure and Enable CPE FLL clock */
+ ret = wcd_cntl_cpe_fll_ctrl(cntl, true);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev,
+ "%s: Failed to enable cpe clk, err = %d\n",
+ __func__, ret);
+ goto err_cpe_clk;
+ }
+ cntl->is_clk_enabled = true;
+
+ /* Ungate the CPR clock */
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, 0x10, 0x00);
+done:
+ WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex);
+ return ret;
+
+err_cpe_clk:
+ if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en)
+ cntl->cdc_cb->cdc_clk_en(codec, false);
+
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x04, 0x04);
+ WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex);
+ return ret;
+}
+
+static int wcd_cntl_clocks_disable(struct wcd_dsp_cntl *cntl)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+ int ret = 0;
+
+ WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex);
+ if (!cntl->is_clk_enabled) {
+ dev_info(codec->dev, "%s: clocks already disabled\n",
+ __func__);
+ goto done;
+ }
+
+ /* Gate the CPR clock */
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, 0x10, 0x10);
+
+ /* Disable CPE FLL clock */
+ ret = wcd_cntl_cpe_fll_ctrl(cntl, false);
+ if (IS_ERR_VALUE(ret))
+ dev_err(codec->dev,
+ "%s: Failed to disable cpe clk, err = %d\n",
+ __func__, ret);
+
+ /*
+ * Even if CPE FLL disable failed, go ahead and disable
+ * the codec clock
+ */
+ if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en)
+ ret = cntl->cdc_cb->cdc_clk_en(codec, false);
+ else
+ ret = -EINVAL;
+
+ cntl->is_clk_enabled = false;
+
+ /* Put CPAR in reset */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x04, 0x04);
+done:
+ WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex);
+ return ret;
+}
+
+static void wcd_cntl_cpar_ctrl(struct wcd_dsp_cntl *cntl,
+ bool enable)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+
+ if (enable)
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x03, 0x03);
+ else
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x03, 0x00);
+}
+
+static int wcd_cntl_enable_memory(struct wcd_dsp_cntl *cntl,
+ enum wcd_mem_type mem_type)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ int loop_cnt = 0;
+ u8 status;
+ int ret = 0;
+
+
+ switch (mem_type) {
+
+ case WCD_MEM_TYPE_ALWAYS_ON:
+
+ /* 512KB of always on region */
+ wcd9xxx_slim_write_repeat(wcd9xxx,
+ WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0,
+ ARRAY_SIZE(mem_enable_values),
+ mem_enable_values);
+ wcd9xxx_slim_write_repeat(wcd9xxx,
+ WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1,
+ ARRAY_SIZE(mem_enable_values),
+ mem_enable_values);
+ break;
+
+ case WCD_MEM_TYPE_SWITCHABLE:
+
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL,
+ 0x04, 0x00);
+ snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL,
+ 0x01, 0x01);
+ do {
+ loop_cnt++;
+ /* Time to enable the power domain for memory */
+ usleep_range(100, 150);
+ status = snd_soc_read(codec,
+ WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL);
+ } while ((status & 0x02) != 0x02 &&
+ loop_cnt != WCD_MEM_ENABLE_MAX_RETRIES);
+
+ if ((status & 0x02) != 0x02) {
+ dev_err(cntl->codec->dev,
+ "%s: power domain not enabled, status = 0x%02x\n",
+ __func__, status);
+ ret = -EIO;
+ goto done;
+ }
+
+ /* Rest of the memory */
+ wcd9xxx_slim_write_repeat(wcd9xxx,
+ WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2,
+ ARRAY_SIZE(mem_enable_values),
+ mem_enable_values);
+ wcd9xxx_slim_write_repeat(wcd9xxx,
+ WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3,
+ ARRAY_SIZE(mem_enable_values),
+ mem_enable_values);
+
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN,
+ 0x05);
+ break;
+
+ default:
+ dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n",
+ __func__, mem_type);
+ ret = -EINVAL;
+ break;
+ }
+done:
+ /* Make sure Deep sleep of memories is enabled for all banks */
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF);
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F);
+
+ return ret;
+}
+
+static void wcd_cntl_disable_memory(struct wcd_dsp_cntl *cntl,
+ enum wcd_mem_type mem_type)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+ u8 val;
+
+ switch (mem_type) {
+ case WCD_MEM_TYPE_ALWAYS_ON:
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1,
+ 0xFF);
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0,
+ 0xFF);
+ break;
+ case WCD_MEM_TYPE_SWITCHABLE:
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3,
+ 0xFF);
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2,
+ 0xFF);
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN,
+ 0x07);
+
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL,
+ 0x01, 0x00);
+ val = snd_soc_read(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL);
+ if (val & 0x02)
+ dev_err(codec->dev,
+ "%s: Disable switchable failed, val = 0x%02x",
+ __func__, val);
+
+ snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL,
+ 0x80, 0x00);
+ break;
+ default:
+ dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n",
+ __func__, mem_type);
+ break;
+ }
+
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF);
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F);
+}
+
+static void wcd_cntl_do_shutdown(struct wcd_dsp_cntl *cntl)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+
+ /* Disable WDOG */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG,
+ 0x3F, 0x01);
+
+ /* Put WDSP in reset state */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL,
+ 0x02, 0x00);
+
+ /* If DSP transitions from boot to shutdown, then vote for SVS */
+ if (cntl->is_wdsp_booted)
+ cntl->cdc_cb->cdc_vote_svs(codec, true);
+ cntl->is_wdsp_booted = false;
+}
+
+static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+ int ret = 0;
+
+ /*
+ * Debug mode is set from debugfs file node. If debug_mode
+ * is set, then do not configure the watchdog timer. This
+ * will be required for debugging the DSP firmware.
+ */
+ if (cntl->debug_mode) {
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG,
+ 0x3F, 0x01);
+ } else {
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG,
+ 0x3F, 0x21);
+ }
+
+ /* Make sure all the error interrupts are cleared */
+ snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A, 0xFF);
+ snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B, 0xFF);
+
+ reinit_completion(&cntl->boot_complete);
+
+ /* Remove WDSP out of reset */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL,
+ 0x02, 0x02);
+
+ /*
+ * In debug mode, DSP may not boot up normally,
+ * wait indefinitely for DSP to boot.
+ */
+ if (cntl->debug_mode) {
+ wait_for_completion(&cntl->boot_complete);
+ dev_dbg(codec->dev, "%s: WDSP booted in dbg mode\n", __func__);
+ cntl->is_wdsp_booted = true;
+ goto done;
+ }
+
+ /* Boot in normal mode */
+ ret = wait_for_completion_timeout(&cntl->boot_complete,
+ msecs_to_jiffies(WCD_DSP_BOOT_TIMEOUT_MS));
+ if (!ret) {
+ dev_err(codec->dev, "%s: WDSP boot timed out\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ goto err_boot;
+ } else {
+ /*
+ * Re-initialize the return code to 0, as in success case,
+ * it will hold the remaining time for completion timeout
+ */
+ ret = 0;
+ }
+
+ dev_dbg(codec->dev, "%s: WDSP booted in normal mode\n", __func__);
+ cntl->is_wdsp_booted = true;
+
+ /* Enable WDOG */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG,
+ 0x10, 0x10);
+done:
+ /* If dsp booted up, then remove vote on SVS */
+ if (cntl->is_wdsp_booted)
+ cntl->cdc_cb->cdc_vote_svs(codec, false);
+
+ return ret;
+err_boot:
+ /* call shutdown to perform cleanup */
+ wcd_cntl_do_shutdown(cntl);
+ return ret;
+}
+
+static irqreturn_t wcd_cntl_ipc_irq(int irq, void *data)
+{
+ struct wcd_dsp_cntl *cntl = data;
+ int ret;
+
+ complete(&cntl->boot_complete);
+
+ if (cntl->m_dev && cntl->m_ops &&
+ cntl->m_ops->signal_handler)
+ ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_IPC1_INTR,
+ NULL);
+ else
+ ret = -EINVAL;
+
+ if (IS_ERR_VALUE(ret))
+ dev_err(cntl->codec->dev,
+ "%s: Failed to handle irq %d\n", __func__, irq);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_cntl_err_irq(int irq, void *data)
+{
+ struct wcd_dsp_cntl *cntl = data;
+ struct snd_soc_codec *codec = cntl->codec;
+ struct wdsp_err_signal_arg arg;
+ u16 status = 0;
+ u8 reg_val;
+ int ret = 0;
+
+ reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A);
+ status = status | reg_val;
+
+ reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B);
+ status = status | (reg_val << 8);
+
+ dev_info(codec->dev, "%s: error interrupt status = 0x%x\n",
+ __func__, status);
+
+ if ((status & cntl->irqs.fatal_irqs) &&
+ (cntl->m_dev && cntl->m_ops && cntl->m_ops->signal_handler)) {
+ arg.mem_dumps_enabled = cntl->ramdump_enable;
+ arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR;
+ arg.dump_size = WCD_934X_RAMDUMP_SIZE;
+ ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_ERR_INTR,
+ &arg);
+ if (IS_ERR_VALUE(ret))
+ dev_err(cntl->codec->dev,
+ "%s: Failed to handle fatal irq 0x%x\n",
+ __func__, status & cntl->irqs.fatal_irqs);
+ wcd_cntl_change_online_state(cntl, 0);
+ } else {
+ dev_err(cntl->codec->dev, "%s: Invalid signal_handler\n",
+ __func__);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int wcd_control_handler(struct device *dev, void *priv_data,
+ enum wdsp_event_type event, void *data)
+{
+ struct wcd_dsp_cntl *cntl = priv_data;
+ struct snd_soc_codec *codec = cntl->codec;
+ int ret = 0;
+
+ switch (event) {
+ case WDSP_EVENT_POST_INIT:
+ case WDSP_EVENT_POST_DLOAD_CODE:
+ case WDSP_EVENT_DLOAD_FAILED:
+ case WDSP_EVENT_POST_SHUTDOWN:
+
+ /* Disable CPAR */
+ wcd_cntl_cpar_ctrl(cntl, false);
+ /* Disable all the clocks */
+ ret = wcd_cntl_clocks_disable(cntl);
+ if (IS_ERR_VALUE(ret))
+ dev_err(codec->dev,
+ "%s: Failed to disable clocks, err = %d\n",
+ __func__, ret);
+
+ if (event == WDSP_EVENT_POST_DLOAD_CODE)
+ /* Mark DSP online since code download is complete */
+ wcd_cntl_change_online_state(cntl, 1);
+
+ break;
+
+ case WDSP_EVENT_PRE_DLOAD_DATA:
+ case WDSP_EVENT_PRE_DLOAD_CODE:
+
+ /* Enable all the clocks */
+ ret = wcd_cntl_clocks_enable(cntl);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev,
+ "%s: Failed to enable clocks, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ /* Enable CPAR */
+ wcd_cntl_cpar_ctrl(cntl, true);
+
+ if (event == WDSP_EVENT_PRE_DLOAD_CODE)
+ wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_ALWAYS_ON);
+ else if (event == WDSP_EVENT_PRE_DLOAD_DATA)
+ wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE);
+ break;
+
+ case WDSP_EVENT_DO_BOOT:
+
+ ret = wcd_cntl_do_boot(cntl);
+ if (IS_ERR_VALUE(ret))
+ dev_err(codec->dev,
+ "%s: WDSP boot failed, err = %d\n",
+ __func__, ret);
+ break;
+
+ case WDSP_EVENT_DO_SHUTDOWN:
+
+ wcd_cntl_do_shutdown(cntl);
+ wcd_cntl_disable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE);
+ break;
+
+ default:
+ dev_dbg(codec->dev, "%s: unhandled event %d\n",
+ __func__, event);
+ }
+
+done:
+ return ret;
+}
+
+static int wcd_cntl_sysfs_init(char *dir, struct wcd_dsp_cntl *cntl)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+ int ret = 0;
+
+ ret = kobject_init_and_add(&cntl->wcd_kobj, &wcd_cntl_ktype,
+ kernel_kobj, dir);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev,
+ "%s: Failed to add kobject %s, err = %d\n",
+ __func__, dir, ret);
+ goto done;
+ }
+
+ ret = sysfs_create_file(&cntl->wcd_kobj, &cntl_attr_boot.attr);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev,
+ "%s: Failed to add wdsp_boot sysfs entry to %s\n",
+ __func__, dir);
+ goto fail_create_file;
+ }
+
+ return ret;
+
+fail_create_file:
+ kobject_put(&cntl->wcd_kobj);
+done:
+ return ret;
+}
+
+static void wcd_cntl_sysfs_remove(struct wcd_dsp_cntl *cntl)
+{
+ sysfs_remove_file(&cntl->wcd_kobj, &cntl_attr_boot.attr);
+ kobject_put(&cntl->wcd_kobj);
+}
+
+static void wcd_cntl_debugfs_init(char *dir, struct wcd_dsp_cntl *cntl)
+{
+ struct snd_soc_codec *codec = cntl->codec;
+
+ cntl->entry = debugfs_create_dir(dir, NULL);
+ if (IS_ERR_OR_NULL(dir)) {
+ dev_err(codec->dev, "%s debugfs_create_dir failed for %s\n",
+ __func__, dir);
+ goto done;
+ }
+
+ debugfs_create_u32("debug_mode", S_IRUGO | S_IWUSR,
+ cntl->entry, &cntl->debug_mode);
+ debugfs_create_bool("ramdump_enable", S_IRUGO | S_IWUSR,
+ cntl->entry, &cntl->ramdump_enable);
+done:
+ return;
+}
+
+static void wcd_cntl_debugfs_remove(struct wcd_dsp_cntl *cntl)
+{
+ if (cntl)
+ debugfs_remove(cntl->entry);
+}
+
+static int wcd_miscdev_release(struct inode *inode, struct file *filep)
+{
+ struct wcd_dsp_cntl *cntl = container_of(filep->private_data,
+ struct wcd_dsp_cntl, miscdev);
+ if (!cntl->m_dev || !cntl->m_ops ||
+ !cntl->m_ops->vote_for_dsp) {
+ dev_err(cntl->codec->dev,
+ "%s: DSP not ready to boot\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Make sure the DSP users goes to zero upon closing dev node */
+ while (cntl->boot_reqs > 0) {
+ cntl->m_ops->vote_for_dsp(cntl->m_dev, false);
+ cntl->boot_reqs--;
+ }
+
+ return 0;
+}
+
+static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf,
+ size_t count, loff_t *pos)
+{
+ struct wcd_dsp_cntl *cntl = container_of(filep->private_data,
+ struct wcd_dsp_cntl, miscdev);
+ char val[WCD_DSP_CNTL_MAX_COUNT + 1];
+ bool vote;
+ int ret = 0;
+
+ memset(val, 0, WCD_DSP_CNTL_MAX_COUNT + 1);
+
+ if (count == 0 || count > WCD_DSP_CNTL_MAX_COUNT) {
+ pr_err("%s: Invalid count = %zd\n", __func__, count);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = copy_from_user(val, ubuf, count);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(cntl->codec->dev,
+ "%s: copy_from_user failed, err = %d\n",
+ __func__, ret);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (val[0] == '1') {
+ cntl->boot_reqs++;
+ vote = true;
+ } else if (val[0] == '0') {
+ if (cntl->boot_reqs == 0) {
+ dev_err(cntl->codec->dev,
+ "%s: WDSP already disabled\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ cntl->boot_reqs--;
+ vote = false;
+ } else {
+ dev_err(cntl->codec->dev, "%s: Invalid value %s\n",
+ __func__, val);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ dev_dbg(cntl->codec->dev,
+ "%s: booted = %s, ref_cnt = %d, vote = %s\n",
+ __func__, cntl->is_wdsp_booted ? "true" : "false",
+ cntl->boot_reqs, vote ? "true" : "false");
+
+ if (cntl->m_dev && cntl->m_ops &&
+ cntl->m_ops->vote_for_dsp)
+ ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote);
+ else
+ ret = -EINVAL;
+done:
+ if (ret)
+ return ret;
+ else
+ return count;
+}
+
+static const struct file_operations wcd_miscdev_fops = {
+ .write = wcd_miscdev_write,
+ .release = wcd_miscdev_release,
+};
+
+static int wcd_cntl_miscdev_create(struct wcd_dsp_cntl *cntl)
+{
+ snprintf(cntl->miscdev_name, ARRAY_SIZE(cntl->miscdev_name),
+ "wcd_dsp%u_control", cntl->dsp_instance);
+ cntl->miscdev.minor = MISC_DYNAMIC_MINOR;
+ cntl->miscdev.name = cntl->miscdev_name;
+ cntl->miscdev.fops = &wcd_miscdev_fops;
+ cntl->miscdev.parent = cntl->codec->dev;
+
+ return misc_register(&cntl->miscdev);
+}
+
+static void wcd_cntl_miscdev_destroy(struct wcd_dsp_cntl *cntl)
+{
+ misc_deregister(&cntl->miscdev);
+}
+
+static int wcd_control_init(struct device *dev, void *priv_data)
+{
+ struct wcd_dsp_cntl *cntl = priv_data;
+ struct snd_soc_codec *codec = cntl->codec;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
+ int ret;
+ bool err_irq_requested = false;
+
+ ret = wcd9xxx_request_irq(core_res,
+ cntl->irqs.cpe_ipc1_irq,
+ wcd_cntl_ipc_irq, "CPE IPC1",
+ cntl);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev,
+ "%s: Failed to request cpe ipc irq, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ /* Unmask the fatal irqs */
+ snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A,
+ ~(cntl->irqs.fatal_irqs & 0xFF));
+ snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B,
+ ~((cntl->irqs.fatal_irqs >> 8) & 0xFF));
+
+ /*
+ * CPE ERR irq is used only for error reporting from WCD DSP,
+ * even if this request fails, DSP can be function normally.
+ * Continuing with init even if the CPE ERR irq request fails.
+ */
+ if (wcd9xxx_request_irq(core_res, cntl->irqs.cpe_err_irq,
+ wcd_cntl_err_irq, "CPE ERR", cntl))
+ dev_info(codec->dev, "%s: Failed request_irq(cpe_err_irq)",
+ __func__);
+ else
+ err_irq_requested = true;
+
+
+ /* Enable all the clocks */
+ ret = wcd_cntl_clocks_enable(cntl);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev, "%s: Failed to enable clocks, err = %d\n",
+ __func__, ret);
+ goto err_clk_enable;
+ }
+ wcd_cntl_cpar_ctrl(cntl, true);
+
+ return 0;
+
+err_clk_enable:
+ /* Mask all error interrupts */
+ snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF);
+ snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF);
+
+ /* Free the irq's requested */
+ wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl);
+
+ if (err_irq_requested)
+ wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl);
+done:
+ return ret;
+}
+
+static int wcd_control_deinit(struct device *dev, void *priv_data)
+{
+ struct wcd_dsp_cntl *cntl = priv_data;
+ struct snd_soc_codec *codec = cntl->codec;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
+
+ wcd_cntl_clocks_disable(cntl);
+ wcd_cntl_cpar_ctrl(cntl, false);
+
+ /* Mask all error interrupts */
+ snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF);
+ snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF);
+
+ /* Free the irq's requested */
+ wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl);
+ wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl);
+
+ return 0;
+}
+
+static struct wdsp_cmpnt_ops control_ops = {
+ .init = wcd_control_init,
+ .deinit = wcd_control_deinit,
+ .event_handler = wcd_control_handler,
+};
+
+static int wcd_ctrl_component_bind(struct device *dev,
+ struct device *master,
+ void *data)
+{
+ struct wcd_dsp_cntl *cntl;
+ struct snd_soc_codec *codec;
+ struct snd_card *card;
+ struct snd_info_entry *entry;
+ char proc_name[WCD_PROCFS_ENTRY_MAX_LEN];
+ char wcd_cntl_dir_name[WCD_CNTL_DIR_NAME_LEN_MAX];
+ int ret = 0;
+
+ if (!dev || !master || !data) {
+ pr_err("%s: Invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ cntl = tavil_get_wcd_dsp_cntl(dev);
+ if (!cntl) {
+ dev_err(dev, "%s: Failed to get cntl reference\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ cntl->m_dev = master;
+ cntl->m_ops = data;
+
+ if (!cntl->m_ops->register_cmpnt_ops) {
+ dev_err(dev, "%s: invalid master callback register_cmpnt_ops\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, &control_ops);
+ if (ret) {
+ dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = wcd_cntl_miscdev_create(cntl);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(dev, "%s: misc dev register failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ snprintf(wcd_cntl_dir_name, WCD_CNTL_DIR_NAME_LEN_MAX,
+ "%s%d", "wdsp", cntl->dsp_instance);
+ ret = wcd_cntl_sysfs_init(wcd_cntl_dir_name, cntl);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(dev, "%s: sysfs_init failed, err = %d\n",
+ __func__, ret);
+ goto err_sysfs_init;
+ }
+
+ wcd_cntl_debugfs_init(wcd_cntl_dir_name, cntl);
+
+ codec = cntl->codec;
+ card = codec->component.card->snd_card;
+ snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe",
+ cntl->dsp_instance, "_state");
+ entry = snd_info_create_card_entry(card, proc_name, card->proc_root);
+ if (!entry) {
+ /* Do not treat this as Fatal error */
+ dev_err(dev, "%s: Failed to create procfs entry %s\n",
+ __func__, proc_name);
+ goto err_sysfs_init;
+ }
+
+ cntl->ssr_entry.entry = entry;
+ cntl->ssr_entry.offline = 1;
+ entry->size = WCD_PROCFS_ENTRY_MAX_LEN;
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->c.ops = &wdsp_ssr_entry_ops;
+ entry->private_data = cntl;
+ ret = snd_info_register(entry);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(dev, "%s: Failed to register entry %s, err = %d\n",
+ __func__, proc_name, ret);
+ snd_info_free_entry(entry);
+ /* Let bind still happen even if creating the entry failed */
+ ret = 0;
+ }
+done:
+ return ret;
+
+err_sysfs_init:
+ wcd_cntl_miscdev_destroy(cntl);
+ return ret;
+}
+
+static void wcd_ctrl_component_unbind(struct device *dev,
+ struct device *master,
+ void *data)
+{
+ struct wcd_dsp_cntl *cntl;
+
+ if (!dev) {
+ pr_err("%s: Invalid device\n", __func__);
+ return;
+ }
+
+ cntl = tavil_get_wcd_dsp_cntl(dev);
+ if (!cntl) {
+ dev_err(dev, "%s: Failed to get cntl reference\n",
+ __func__);
+ return;
+ }
+
+ cntl->m_dev = NULL;
+ cntl->m_ops = NULL;
+
+ /* Remove the sysfs entries */
+ wcd_cntl_sysfs_remove(cntl);
+
+ /* Remove the debugfs entries */
+ wcd_cntl_debugfs_remove(cntl);
+
+ /* Remove the misc device */
+ wcd_cntl_miscdev_destroy(cntl);
+}
+
+static const struct component_ops wcd_ctrl_component_ops = {
+ .bind = wcd_ctrl_component_bind,
+ .unbind = wcd_ctrl_component_unbind,
+};
+
+/*
+ * wcd_dsp_ssr_event: handle the SSR event raised by caller.
+ * @cntl: Handle to the wcd_dsp_cntl structure
+ * @event: The SSR event to be handled
+ *
+ * Notifies the manager driver about the SSR event.
+ * Returns 0 on success and negative error code on error.
+ */
+int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event)
+{
+ int ret = 0;
+
+ if (!cntl) {
+ pr_err("%s: Invalid handle to control\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!cntl->m_dev || !cntl->m_ops || !cntl->m_ops->signal_handler) {
+ dev_err(cntl->codec->dev,
+ "%s: Invalid signal_handler callback\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case WCD_CDC_DOWN_EVENT:
+ ret = cntl->m_ops->signal_handler(cntl->m_dev,
+ WDSP_CDC_DOWN_SIGNAL,
+ NULL);
+ if (IS_ERR_VALUE(ret))
+ dev_err(cntl->codec->dev,
+ "%s: WDSP_CDC_DOWN_SIGNAL failed, err = %d\n",
+ __func__, ret);
+ wcd_cntl_change_online_state(cntl, 0);
+ break;
+ case WCD_CDC_UP_EVENT:
+ ret = cntl->m_ops->signal_handler(cntl->m_dev,
+ WDSP_CDC_UP_SIGNAL,
+ NULL);
+ if (IS_ERR_VALUE(ret))
+ dev_err(cntl->codec->dev,
+ "%s: WDSP_CDC_UP_SIGNAL failed, err = %d\n",
+ __func__, ret);
+ break;
+ default:
+ dev_err(cntl->codec->dev, "%s: Invalid event %d\n",
+ __func__, event);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(wcd_dsp_ssr_event);
+
+/*
+ * wcd_dsp_cntl_init: Initialize the wcd-dsp control
+ * @codec: pointer to the codec handle
+ * @params: Parameters required to initialize wcd-dsp control
+ *
+ * This API is expected to be invoked by the codec driver and
+ * provide information essential for the wcd dsp control to
+ * configure and initialize the dsp
+ */
+void wcd_dsp_cntl_init(struct snd_soc_codec *codec,
+ struct wcd_dsp_params *params,
+ struct wcd_dsp_cntl **cntl)
+{
+ struct wcd_dsp_cntl *control;
+ int ret;
+
+ if (!codec || !params) {
+ pr_err("%s: Invalid handle to %s\n", __func__,
+ (!codec) ? "codec" : "params");
+ *cntl = NULL;
+ return;
+ }
+
+ if (*cntl) {
+ pr_err("%s: cntl is non NULL, maybe already initialized ?\n",
+ __func__);
+ return;
+ }
+
+ if (!params->cb || !params->cb->cdc_clk_en ||
+ !params->cb->cdc_vote_svs) {
+ dev_err(codec->dev,
+ "%s: clk_en and vote_svs callbacks must be provided\n",
+ __func__);
+ return;
+ }
+
+ control = kzalloc(sizeof(*control), GFP_KERNEL);
+ if (!(control))
+ return;
+
+ control->codec = codec;
+ control->clk_rate = params->clk_rate;
+ control->cdc_cb = params->cb;
+ control->dsp_instance = params->dsp_instance;
+ memcpy(&control->irqs, &params->irqs, sizeof(control->irqs));
+ init_completion(&control->boot_complete);
+ mutex_init(&control->clk_mutex);
+ mutex_init(&control->ssr_mutex);
+ init_waitqueue_head(&control->ssr_entry.offline_poll_wait);
+
+ /*
+ * The default state of WDSP is in SVS mode.
+ * Vote for SVS now, the vote will be removed only
+ * after DSP is booted up.
+ */
+ control->cdc_cb->cdc_vote_svs(codec, true);
+
+ /*
+ * If this is the last component needed by master to be ready,
+ * then component_bind will be called within the component_add.
+ * Hence, the data pointer should be assigned before component_add,
+ * so that we can access it during this component's bind call.
+ */
+ *cntl = control;
+ ret = component_add(codec->dev, &wcd_ctrl_component_ops);
+ if (ret) {
+ dev_err(codec->dev, "%s: component_add failed, err = %d\n",
+ __func__, ret);
+ kfree(*cntl);
+ *cntl = NULL;
+ }
+}
+EXPORT_SYMBOL(wcd_dsp_cntl_init);
+
+/*
+ * wcd_dsp_cntl_deinit: De-initialize the wcd-dsp control
+ * @cntl: The struct wcd_dsp_cntl to de-initialize
+ *
+ * This API is intended to be invoked by the codec driver
+ * to de-initialize the wcd dsp control
+ */
+void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl)
+{
+ struct wcd_dsp_cntl *control = *cntl;
+ struct snd_soc_codec *codec;
+
+ /* If control is NULL, there is nothing to de-initialize */
+ if (!control)
+ return;
+ codec = control->codec;
+
+ /*
+ * Calling shutdown will cleanup all register states,
+ * irrespective of DSP was booted up or not.
+ */
+ wcd_cntl_do_shutdown(control);
+ wcd_cntl_disable_memory(control, WCD_MEM_TYPE_SWITCHABLE);
+ wcd_cntl_disable_memory(control, WCD_MEM_TYPE_ALWAYS_ON);
+
+ component_del(codec->dev, &wcd_ctrl_component_ops);
+
+ mutex_destroy(&control->clk_mutex);
+ mutex_destroy(&control->ssr_mutex);
+ kfree(*cntl);
+ *cntl = NULL;
+}
+EXPORT_SYMBOL(wcd_dsp_cntl_deinit);
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h
new file mode 100644
index 000000000000..e934638cc487
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD934X_DSP_CNTL_H__
+#define __WCD934X_DSP_CNTL_H__
+
+#include <sound/soc.h>
+#include <sound/wcd-dsp-mgr.h>
+
+enum cdc_ssr_event {
+ WCD_CDC_DOWN_EVENT,
+ WCD_CDC_UP_EVENT,
+};
+
+struct wcd_dsp_cdc_cb {
+ /* Callback to enable codec clock */
+ int (*cdc_clk_en)(struct snd_soc_codec *, bool);
+ /* Callback to vote and unvote for SVS2 mode */
+ void (*cdc_vote_svs)(struct snd_soc_codec *, bool);
+};
+
+struct wcd_dsp_irq_info {
+ /* IPC interrupt */
+ int cpe_ipc1_irq;
+
+ /* CPE error summary interrupt */
+ int cpe_err_irq;
+
+ /*
+ * Bit mask to indicate which of the
+ * error interrupts are to be considered
+ * as fatal.
+ */
+ u16 fatal_irqs;
+};
+
+struct wcd_dsp_params {
+ struct wcd_dsp_cdc_cb *cb;
+ struct wcd_dsp_irq_info irqs;
+
+ /* Rate at which the codec clock operates */
+ u32 clk_rate;
+
+ /*
+ * Represents the dsp instance, will be used
+ * to create sysfs and debugfs entries with
+ * directory wdsp<dsp-instance>
+ */
+ u32 dsp_instance;
+};
+
+struct wdsp_ssr_entry {
+ u8 offline;
+ u8 offline_change;
+ wait_queue_head_t offline_poll_wait;
+ struct snd_info_entry *entry;
+};
+
+struct wcd_dsp_cntl {
+ /* Handle to codec */
+ struct snd_soc_codec *codec;
+
+ /* Clk rate of the codec clock */
+ u32 clk_rate;
+
+ /* Callbacks to codec driver */
+ const struct wcd_dsp_cdc_cb *cdc_cb;
+
+ /* Completion to indicate WDSP boot done */
+ struct completion boot_complete;
+
+ struct wcd_dsp_irq_info irqs;
+ u32 dsp_instance;
+
+ /* Sysfs entries related */
+ int boot_reqs;
+ struct kobject wcd_kobj;
+
+ /* Debugfs related */
+ struct dentry *entry;
+ u32 debug_mode;
+ bool ramdump_enable;
+
+ /* WDSP manager drivers data */
+ struct device *m_dev;
+ struct wdsp_mgr_ops *m_ops;
+
+ /* clk related */
+ struct mutex clk_mutex;
+ bool is_clk_enabled;
+
+ /* Keep track of WDSP boot status */
+ bool is_wdsp_booted;
+
+ /* SSR related */
+ struct wdsp_ssr_entry ssr_entry;
+ struct mutex ssr_mutex;
+
+ /* Misc device related */
+ char miscdev_name[256];
+ struct miscdevice miscdev;
+};
+
+void wcd_dsp_cntl_init(struct snd_soc_codec *codec,
+ struct wcd_dsp_params *params,
+ struct wcd_dsp_cntl **cntl);
+void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl);
+int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event);
+#endif /* end __WCD_DSP_CONTROL_H__ */
diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.c b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c
new file mode 100644
index 000000000000..075ab7b2ff2b
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c
@@ -0,0 +1,1104 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include "wcd934x.h"
+#include "wcd934x-mbhc.h"
+#include "../wcdcal-hwdep.h"
+
+#define TAVIL_ZDET_SUPPORTED true
+/* Z value defined in milliohm */
+#define TAVIL_ZDET_VAL_32 32000
+#define TAVIL_ZDET_VAL_400 400000
+#define TAVIL_ZDET_VAL_1200 1200000
+#define TAVIL_ZDET_VAL_100K 100000000
+/* Z floating defined in ohms */
+#define TAVIL_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE
+
+#define TAVIL_ZDET_NUM_MEASUREMENTS 150
+#define TAVIL_MBHC_GET_C1(c) ((c & 0xC000) >> 14)
+#define TAVIL_MBHC_GET_X1(x) (x & 0x3FFF)
+/* Z value compared in milliOhm */
+#define TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000))
+#define TAVIL_MBHC_ZDET_CONST (86 * 16384)
+#define TAVIL_MBHC_MOISTURE_RREF R_24_KOHM
+
+static struct wcd_mbhc_register
+ wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN",
+ WCD934X_ANA_MBHC_MECH, 0x80, 7, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN",
+ WCD934X_ANA_MBHC_MECH, 0x40, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE",
+ WCD934X_ANA_MBHC_MECH, 0x20, 5, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL",
+ WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x30, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE",
+ WCD934X_ANA_MBHC_ELECT, 0x08, 3, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL",
+ WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0xC0, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL",
+ WCD934X_ANA_MBHC_MECH, 0x04, 2, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE",
+ WCD934X_ANA_MBHC_MECH, 0x10, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE",
+ WCD934X_ANA_MBHC_MECH, 0x08, 3, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND",
+ WCD934X_ANA_MBHC_MECH, 0x01, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC",
+ WCD934X_ANA_MBHC_ELECT, 0x06, 1, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN",
+ WCD934X_ANA_MBHC_ELECT, 0x80, 7, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC",
+ WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC",
+ WCD934X_MBHC_NEW_CTL_1, 0x03, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF",
+ WCD934X_MBHC_NEW_CTL_2, 0x03, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT",
+ WCD934X_ANA_MBHC_RESULT_3, 0x08, 3, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT",
+ WCD934X_ANA_MBHC_RESULT_3, 0x20, 5, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT",
+ WCD934X_ANA_MBHC_RESULT_3, 0x80, 7, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT",
+ WCD934X_ANA_MBHC_RESULT_3, 0x40, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN",
+ WCD934X_HPH_OCP_CTL, 0x10, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT",
+ WCD934X_ANA_MBHC_RESULT_3, 0x07, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL",
+ WCD934X_ANA_MBHC_ELECT, 0x70, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT",
+ WCD934X_ANA_MBHC_RESULT_3, 0xFF, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL",
+ WCD934X_ANA_MICB2, 0xC0, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME",
+ WCD934X_HPH_CNP_WG_TIME, 0xFF, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN",
+ WCD934X_ANA_HPH, 0x40, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN",
+ WCD934X_ANA_HPH, 0x80, 7, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN",
+ WCD934X_ANA_HPH, 0xC0, 6, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE",
+ WCD934X_ANA_MBHC_RESULT_3, 0x10, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL",
+ 0, 0, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN",
+ WCD934X_ANA_MBHC_ZDET, 0x01, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS",
+ WCD934X_MBHC_STATUS_SPARE_1, 0x01, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL",
+ WCD934X_MBHC_NEW_CTL_2, 0x70, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN",
+ WCD934X_HPH_L_TEST, 0x01, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN",
+ WCD934X_HPH_R_TEST, 0x01, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_STATUS",
+ WCD934X_INTR_PIN1_STATUS0, 0x04, 2, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_STATUS",
+ WCD934X_INTR_PIN1_STATUS0, 0x08, 3, 0),
+};
+
+static const struct wcd_mbhc_intr intr_ids = {
+ .mbhc_sw_intr = WCD934X_IRQ_MBHC_SW_DET,
+ .mbhc_btn_press_intr = WCD934X_IRQ_MBHC_BUTTON_PRESS_DET,
+ .mbhc_btn_release_intr = WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET,
+ .mbhc_hs_ins_intr = WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+ .mbhc_hs_rem_intr = WCD934X_IRQ_MBHC_ELECT_INS_REM_DET,
+ .hph_left_ocp = WCD934X_IRQ_HPH_PA_OCPL_FAULT,
+ .hph_right_ocp = WCD934X_IRQ_HPH_PA_OCPR_FAULT,
+};
+
+
+static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = {
+ "cdc-vdd-mic-bias",
+};
+
+struct tavil_mbhc_zdet_param {
+ u16 ldo_ctl;
+ u16 noff;
+ u16 nshift;
+ u16 btn5;
+ u16 btn6;
+ u16 btn7;
+};
+
+static int tavil_mbhc_request_irq(struct snd_soc_codec *codec,
+ int irq, irq_handler_t handler,
+ const char *name, void *data)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+
+ return wcd9xxx_request_irq(core_res, irq, handler, name, data);
+}
+
+static void tavil_mbhc_irq_control(struct snd_soc_codec *codec,
+ int irq, bool enable)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+ if (enable)
+ wcd9xxx_enable_irq(core_res, irq);
+ else
+ wcd9xxx_disable_irq(core_res, irq);
+}
+
+static int tavil_mbhc_free_irq(struct snd_soc_codec *codec,
+ int irq, void *data)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+
+ wcd9xxx_free_irq(core_res, irq, data);
+ return 0;
+}
+
+static void tavil_mbhc_clk_setup(struct snd_soc_codec *codec,
+ bool enable)
+{
+ if (enable)
+ snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1,
+ 0x80, 0x80);
+ else
+ snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1,
+ 0x80, 0x00);
+}
+
+static int tavil_mbhc_btn_to_num(struct snd_soc_codec *codec)
+{
+ return snd_soc_read(codec, WCD934X_ANA_MBHC_RESULT_3) & 0x7;
+}
+
+static int tavil_enable_ext_mb_source(struct wcd_mbhc *mbhc,
+ bool turn_on)
+{
+ struct wcd934x_mbhc *wcd934x_mbhc;
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd934x_on_demand_supply *supply;
+ int ret = 0;
+
+ wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc);
+
+ supply = &wcd934x_mbhc->on_demand_list[WCD934X_ON_DEMAND_MICBIAS];
+ if (!supply->supply) {
+ dev_dbg(codec->dev, "%s: warning supply not present ond for %s\n",
+ __func__, "onDemand Micbias");
+ return ret;
+ }
+
+ dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on,
+ supply->ondemand_supply_count);
+
+ if (turn_on) {
+ if (!(supply->ondemand_supply_count)) {
+ ret = snd_soc_dapm_force_enable_pin(
+ snd_soc_codec_get_dapm(codec),
+ "MICBIAS_REGULATOR");
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+ }
+ supply->ondemand_supply_count++;
+ } else {
+ if (supply->ondemand_supply_count > 0)
+ supply->ondemand_supply_count--;
+ if (!(supply->ondemand_supply_count)) {
+ ret = snd_soc_dapm_disable_pin(
+ snd_soc_codec_get_dapm(codec),
+ "MICBIAS_REGULATOR");
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+ }
+ }
+
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to %s external micbias source\n",
+ __func__, turn_on ? "enable" : "disabled");
+ else
+ dev_dbg(codec->dev, "%s: %s external micbias source\n",
+ __func__, turn_on ? "Enabled" : "Disabled");
+
+ return ret;
+}
+
+static void tavil_mbhc_mbhc_bias_control(struct snd_soc_codec *codec,
+ bool enable)
+{
+ if (enable)
+ snd_soc_update_bits(codec, WCD934X_ANA_MBHC_ELECT,
+ 0x01, 0x01);
+ else
+ snd_soc_update_bits(codec, WCD934X_ANA_MBHC_ELECT,
+ 0x01, 0x00);
+}
+
+static void tavil_mbhc_program_btn_thr(struct snd_soc_codec *codec,
+ s16 *btn_low, s16 *btn_high,
+ int num_btn, bool is_micbias)
+{
+ int i;
+ int vth;
+
+ if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+ dev_err(codec->dev, "%s: invalid number of buttons: %d\n",
+ __func__, num_btn);
+ return;
+ }
+ /*
+ * Tavil just needs one set of thresholds for button detection
+ * due to micbias voltage ramp to pullup upon button press. So
+ * btn_low and is_micbias are ignored and always program button
+ * thresholds using btn_high.
+ */
+ for (i = 0; i < num_btn; i++) {
+ vth = ((btn_high[i] * 2) / 25) & 0x3F;
+ snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN0 + i,
+ 0xFC, vth << 2);
+ dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n",
+ __func__, i, btn_high[i], vth);
+ }
+}
+
+static bool tavil_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+ bool ret = 0;
+
+ if (lock)
+ ret = wcd9xxx_lock_sleep(core_res);
+ else
+ wcd9xxx_unlock_sleep(core_res);
+
+ return ret;
+}
+
+static int tavil_mbhc_register_notifier(struct wcd_mbhc *mbhc,
+ struct notifier_block *nblock,
+ bool enable)
+{
+ struct wcd934x_mbhc *wcd934x_mbhc;
+
+ wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc);
+
+ if (enable)
+ return blocking_notifier_chain_register(&wcd934x_mbhc->notifier,
+ nblock);
+ else
+ return blocking_notifier_chain_unregister(
+ &wcd934x_mbhc->notifier, nblock);
+}
+
+static bool tavil_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num)
+{
+ u8 val;
+
+ if (micb_num == MIC_BIAS_2) {
+ val = (snd_soc_read(mbhc->codec, WCD934X_ANA_MICB2) >> 6);
+ if (val == 0x01)
+ return true;
+ }
+ return false;
+}
+
+static bool tavil_mbhc_hph_pa_on_status(struct snd_soc_codec *codec)
+{
+ return (snd_soc_read(codec, WCD934X_ANA_HPH) & 0xC0) ? true : false;
+}
+
+static void tavil_mbhc_hph_l_pull_up_control(
+ struct snd_soc_codec *codec,
+ enum mbhc_hs_pullup_iref pull_up_cur)
+{
+ /* Default pull up current to 2uA */
+ if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA ||
+ pull_up_cur == I_DEFAULT)
+ pull_up_cur = I_2P0_UA;
+
+ dev_dbg(codec->dev, "%s: HS pull up current:%d\n",
+ __func__, pull_up_cur);
+
+ snd_soc_update_bits(codec, WCD934X_MBHC_NEW_PLUG_DETECT_CTL,
+ 0xC0, pull_up_cur << 6);
+}
+
+static int tavil_mbhc_request_micbias(struct snd_soc_codec *codec,
+ int micb_num, int req)
+{
+ int ret;
+
+ /*
+ * If micbias is requested, make sure that there
+ * is vote to enable mclk
+ */
+ if (req == MICB_ENABLE)
+ tavil_cdc_mclk_enable(codec, true);
+
+ ret = tavil_micbias_control(codec, micb_num, req, false);
+
+ /*
+ * Release vote for mclk while requesting for
+ * micbias disable
+ */
+ if (req == MICB_DISABLE)
+ tavil_cdc_mclk_enable(codec, false);
+
+ return ret;
+}
+
+static void tavil_mbhc_micb_ramp_control(struct snd_soc_codec *codec,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP,
+ 0x1C, 0x0C);
+ snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP,
+ 0x80, 0x80);
+ } else {
+ snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP,
+ 0x80, 0x00);
+ snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP,
+ 0x1C, 0x00);
+ }
+}
+
+static struct firmware_cal *tavil_get_hwdep_fw_cal(struct wcd_mbhc *mbhc,
+ enum wcd_cal_type type)
+{
+ struct wcd934x_mbhc *wcd934x_mbhc;
+ struct firmware_cal *hwdep_cal;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc);
+
+ if (!codec) {
+ pr_err("%s: NULL codec pointer\n", __func__);
+ return NULL;
+ }
+ hwdep_cal = wcdcal_get_fw_cal(wcd934x_mbhc->fw_data, type);
+ if (!hwdep_cal)
+ dev_err(codec->dev, "%s: cal not sent by %d\n",
+ __func__, type);
+
+ return hwdep_cal;
+}
+
+static int tavil_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec,
+ int micb_num, bool req_en)
+{
+ struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+ int rc, micb_mv;
+
+ if (micb_num != MIC_BIAS_2)
+ return -EINVAL;
+
+ /*
+ * If device tree micbias level is already above the minimum
+ * voltage needed to detect threshold microphone, then do
+ * not change the micbias, just return.
+ */
+ if (pdata->micbias.micb2_mv >= WCD_MBHC_THR_HS_MICB_MV)
+ return 0;
+
+ micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : pdata->micbias.micb2_mv;
+
+ rc = tavil_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_2);
+
+ return rc;
+}
+
+static inline void tavil_mbhc_get_result_params(struct wcd9xxx *wcd9xxx,
+ s16 *d1_a, u16 noff,
+ int32_t *zdet)
+{
+ int i;
+ int val, val1;
+ s16 c1;
+ s32 x1, d1;
+ int32_t denom;
+ int minCode_param[] = {
+ 3277, 1639, 820, 410, 205, 103, 52, 26
+ };
+
+ regmap_update_bits(wcd9xxx->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x20);
+ for (i = 0; i < TAVIL_ZDET_NUM_MEASUREMENTS; i++) {
+ regmap_read(wcd9xxx->regmap, WCD934X_ANA_MBHC_RESULT_2, &val);
+ if (val & 0x80)
+ break;
+ }
+ val = val << 0x8;
+ regmap_read(wcd9xxx->regmap, WCD934X_ANA_MBHC_RESULT_1, &val1);
+ val |= val1;
+ regmap_update_bits(wcd9xxx->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x00);
+ x1 = TAVIL_MBHC_GET_X1(val);
+ c1 = TAVIL_MBHC_GET_C1(val);
+ /* If ramp is not complete, give additional 5ms */
+ if ((c1 < 2) && x1)
+ usleep_range(5000, 5050);
+
+ if (!c1 || !x1) {
+ dev_dbg(wcd9xxx->dev,
+ "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n",
+ __func__, c1, x1);
+ goto ramp_down;
+ }
+ d1 = d1_a[c1];
+ denom = (x1 * d1) - (1 << (14 - noff));
+ if (denom > 0)
+ *zdet = (TAVIL_MBHC_ZDET_CONST * 1000) / denom;
+ else if (x1 < minCode_param[noff])
+ *zdet = TAVIL_ZDET_FLOATING_IMPEDANCE;
+
+ dev_dbg(wcd9xxx->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n",
+ __func__, d1, c1, x1, *zdet);
+ramp_down:
+ i = 0;
+ while (x1) {
+ regmap_bulk_read(wcd9xxx->regmap,
+ WCD934X_ANA_MBHC_RESULT_1, (u8 *)&val, 2);
+ x1 = TAVIL_MBHC_GET_X1(val);
+ i++;
+ if (i == TAVIL_ZDET_NUM_MEASUREMENTS)
+ break;
+ }
+}
+
+static void tavil_mbhc_zdet_ramp(struct snd_soc_codec *codec,
+ struct tavil_mbhc_zdet_param *zdet_param,
+ int32_t *zl, int32_t *zr, s16 *d1_a)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ int32_t zdet = 0;
+
+ snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x70,
+ zdet_param->ldo_ctl << 4);
+ snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN5, 0xFC,
+ zdet_param->btn5);
+ snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN6, 0xFC,
+ zdet_param->btn6);
+ snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN7, 0xFC,
+ zdet_param->btn7);
+ snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x0F,
+ zdet_param->noff);
+ snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_RAMP_CTL, 0x0F,
+ zdet_param->nshift);
+
+ if (!zl)
+ goto z_right;
+ /* Start impedance measurement for HPH_L */
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_ANA_MBHC_ZDET, 0x80, 0x80);
+ dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_L, noff = %d\n",
+ __func__, zdet_param->noff);
+ tavil_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_ANA_MBHC_ZDET, 0x80, 0x00);
+
+ *zl = zdet;
+
+z_right:
+ if (!zr)
+ return;
+ /* Start impedance measurement for HPH_R */
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_ANA_MBHC_ZDET, 0x40, 0x40);
+ dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_R, noff = %d\n",
+ __func__, zdet_param->noff);
+ tavil_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_ANA_MBHC_ZDET, 0x40, 0x00);
+
+ *zr = zdet;
+}
+
+static inline void tavil_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec,
+ int32_t *z_val, int flag_l_r)
+{
+ s16 q1;
+ int q1_cal;
+
+ if (*z_val < (TAVIL_ZDET_VAL_400/1000))
+ q1 = snd_soc_read(codec,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r));
+ else
+ q1 = snd_soc_read(codec,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (2 * flag_l_r));
+ if (q1 & 0x80)
+ q1_cal = (10000 - ((q1 & 0x7F) * 25));
+ else
+ q1_cal = (10000 + (q1 * 25));
+ if (q1_cal > 0)
+ *z_val = ((*z_val) * 10000) / q1_cal;
+}
+
+static void tavil_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ s16 reg0, reg1, reg2, reg3, reg4;
+ int32_t z1L, z1R, z1Ls;
+ int zMono, z_diff1, z_diff2;
+ bool is_fsm_disable = false;
+ struct tavil_mbhc_zdet_param zdet_param[] = {
+ {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */
+ {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */
+ {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */
+ {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */
+ };
+ struct tavil_mbhc_zdet_param *zdet_param_ptr = NULL;
+ s16 d1_a[][4] = {
+ {0, 30, 90, 30},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ };
+ s16 *d1 = NULL;
+
+ WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+ reg0 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN5);
+ reg1 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN6);
+ reg2 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN7);
+ reg3 = snd_soc_read(codec, WCD934X_MBHC_CTL_CLK);
+ reg4 = snd_soc_read(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL);
+
+ if (snd_soc_read(codec, WCD934X_ANA_MBHC_ELECT) & 0x80) {
+ is_fsm_disable = true;
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_ANA_MBHC_ELECT, 0x80, 0x00);
+ }
+
+ /* For NO-jack, disable L_DET_EN before Z-det measurements */
+ if (mbhc->hphl_swh)
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_ANA_MBHC_MECH, 0x80, 0x00);
+
+ /* Turn off 100k pull down on HPHL */
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_ANA_MBHC_MECH, 0x01, 0x00);
+
+ /* First get impedance on Left */
+ d1 = d1_a[1];
+ zdet_param_ptr = &zdet_param[1];
+ tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1);
+
+ if (!TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z1L))
+ goto left_ch_impedance;
+
+ /* Second ramp for left ch */
+ if (z1L < TAVIL_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1L > TAVIL_ZDET_VAL_400) && (z1L <= TAVIL_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1L > TAVIL_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1);
+
+left_ch_impedance:
+ if ((z1L == TAVIL_ZDET_FLOATING_IMPEDANCE) ||
+ (z1L > TAVIL_ZDET_VAL_100K)) {
+ *zl = TAVIL_ZDET_FLOATING_IMPEDANCE;
+ zdet_param_ptr = &zdet_param[1];
+ d1 = d1_a[1];
+ } else {
+ *zl = z1L/1000;
+ tavil_wcd_mbhc_qfuse_cal(codec, zl, 0);
+ }
+ dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n",
+ __func__, *zl);
+
+ /* Start of right impedance ramp and calculation */
+ tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1);
+ if (TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) {
+ if (((z1R > TAVIL_ZDET_VAL_1200) &&
+ (zdet_param_ptr->noff == 0x6)) ||
+ ((*zl) != TAVIL_ZDET_FLOATING_IMPEDANCE))
+ goto right_ch_impedance;
+ /* Second ramp for right ch */
+ if (z1R < TAVIL_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1R > TAVIL_ZDET_VAL_400) &&
+ (z1R <= TAVIL_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1R > TAVIL_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1);
+ }
+right_ch_impedance:
+ if ((z1R == TAVIL_ZDET_FLOATING_IMPEDANCE) ||
+ (z1R > TAVIL_ZDET_VAL_100K)) {
+ *zr = TAVIL_ZDET_FLOATING_IMPEDANCE;
+ } else {
+ *zr = z1R/1000;
+ tavil_wcd_mbhc_qfuse_cal(codec, zr, 1);
+ }
+ dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n",
+ __func__, *zr);
+
+ /* Mono/stereo detection */
+ if ((*zl == TAVIL_ZDET_FLOATING_IMPEDANCE) &&
+ (*zr == TAVIL_ZDET_FLOATING_IMPEDANCE)) {
+ dev_dbg(codec->dev,
+ "%s: plug type is invalid or extension cable\n",
+ __func__);
+ goto zdet_complete;
+ }
+ if ((*zl == TAVIL_ZDET_FLOATING_IMPEDANCE) ||
+ (*zr == TAVIL_ZDET_FLOATING_IMPEDANCE) ||
+ ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) ||
+ ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) {
+ dev_dbg(codec->dev,
+ "%s: Mono plug type with one ch floating or shorted to GND\n",
+ __func__);
+ mbhc->hph_type = WCD_MBHC_HPH_MONO;
+ goto zdet_complete;
+ }
+ snd_soc_update_bits(codec, WCD934X_HPH_R_ATEST, 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, 0x40, 0x01);
+ if (*zl < (TAVIL_ZDET_VAL_32/1000))
+ tavil_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1);
+ else
+ tavil_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1);
+ snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, 0x40, 0x00);
+ snd_soc_update_bits(codec, WCD934X_HPH_R_ATEST, 0x02, 0x00);
+ z1Ls /= 1000;
+ tavil_wcd_mbhc_qfuse_cal(codec, &z1Ls, 0);
+ /* Parallel of left Z and 9 ohm pull down resistor */
+ zMono = ((*zl) * 9) / ((*zl) + 9);
+ z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls);
+ z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl));
+ if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) {
+ dev_dbg(codec->dev, "%s: stereo plug type detected\n",
+ __func__);
+ mbhc->hph_type = WCD_MBHC_HPH_STEREO;
+ } else {
+ dev_dbg(codec->dev, "%s: MONO plug type detected\n",
+ __func__);
+ mbhc->hph_type = WCD_MBHC_HPH_MONO;
+ }
+
+zdet_complete:
+ snd_soc_write(codec, WCD934X_ANA_MBHC_BTN5, reg0);
+ snd_soc_write(codec, WCD934X_ANA_MBHC_BTN6, reg1);
+ snd_soc_write(codec, WCD934X_ANA_MBHC_BTN7, reg2);
+ /* Turn on 100k pull down on HPHL */
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_ANA_MBHC_MECH, 0x01, 0x01);
+
+ /* For NO-jack, re-enable L_DET_EN after Z-det measurements */
+ if (mbhc->hphl_swh)
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_ANA_MBHC_MECH, 0x80, 0x80);
+
+ snd_soc_write(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, reg4);
+ snd_soc_write(codec, WCD934X_MBHC_CTL_CLK, reg3);
+ if (is_fsm_disable)
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_ANA_MBHC_ELECT, 0x80, 0x80);
+}
+
+static void tavil_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+ if (enable) {
+ snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH,
+ 0x40, 0x40);
+ } else {
+ snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH,
+ 0x40, 0x00);
+ snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH,
+ 0x02, 0x00);
+ }
+}
+
+static void tavil_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2,
+ 0x40, 0x40);
+ snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2,
+ 0x10, 0x10);
+ } else {
+ snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2,
+ 0x40, 0x00);
+ snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2,
+ 0x10, 0x00);
+ }
+}
+static void tavil_mbhc_moisture_config(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ if ((mbhc->moist_rref == R_OFF) ||
+ (mbhc->mbhc_cfg->enable_usbc_analog)) {
+ snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_2,
+ 0x0C, R_OFF << 2);
+ return;
+ }
+
+ /* Donot enable moisture detection if jack type is NC */
+ if (!mbhc->hphl_swh) {
+ dev_dbg(codec->dev, "%s: disable moisture detection for NC\n",
+ __func__);
+ snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_2,
+ 0x0C, R_OFF << 2);
+ return;
+ }
+
+ snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_2,
+ 0x0C, mbhc->moist_rref << 2);
+}
+
+static bool tavil_hph_register_recovery(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+
+ if (!wcd934x_mbhc)
+ return false;
+
+ wcd934x_mbhc->is_hph_recover = false;
+ snd_soc_dapm_force_enable_pin(snd_soc_codec_get_dapm(codec),
+ "RESET_HPH_REGISTERS");
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+
+ snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec),
+ "RESET_HPH_REGISTERS");
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+
+ return wcd934x_mbhc->is_hph_recover;
+}
+
+static void tavil_update_anc_state(struct snd_soc_codec *codec, bool enable,
+ int anc_num)
+{
+ if (enable)
+ snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CFG0 +
+ (20 * anc_num), 0x10, 0x10);
+ else
+ snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CFG0 +
+ (20 * anc_num), 0x10, 0x00);
+}
+
+static bool tavil_is_anc_on(struct wcd_mbhc *mbhc)
+{
+ bool anc_on = false;
+ u16 ancl, ancr;
+
+ ancl =
+ (snd_soc_read(mbhc->codec, WCD934X_CDC_RX1_RX_PATH_CFG0)) & 0x10;
+ ancr =
+ (snd_soc_read(mbhc->codec, WCD934X_CDC_RX2_RX_PATH_CFG0)) & 0x10;
+
+ anc_on = !!(ancl | ancr);
+
+ return anc_on;
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .request_irq = tavil_mbhc_request_irq,
+ .irq_control = tavil_mbhc_irq_control,
+ .free_irq = tavil_mbhc_free_irq,
+ .clk_setup = tavil_mbhc_clk_setup,
+ .map_btn_code_to_num = tavil_mbhc_btn_to_num,
+ .enable_mb_source = tavil_enable_ext_mb_source,
+ .mbhc_bias = tavil_mbhc_mbhc_bias_control,
+ .set_btn_thr = tavil_mbhc_program_btn_thr,
+ .lock_sleep = tavil_mbhc_lock_sleep,
+ .register_notifier = tavil_mbhc_register_notifier,
+ .micbias_enable_status = tavil_mbhc_micb_en_status,
+ .hph_pa_on_status = tavil_mbhc_hph_pa_on_status,
+ .hph_pull_up_control = tavil_mbhc_hph_l_pull_up_control,
+ .mbhc_micbias_control = tavil_mbhc_request_micbias,
+ .mbhc_micb_ramp_control = tavil_mbhc_micb_ramp_control,
+ .get_hwdep_fw_cal = tavil_get_hwdep_fw_cal,
+ .mbhc_micb_ctrl_thr_mic = tavil_mbhc_micb_ctrl_threshold_mic,
+ .compute_impedance = tavil_wcd_mbhc_calc_impedance,
+ .mbhc_gnd_det_ctrl = tavil_mbhc_gnd_det_ctrl,
+ .hph_pull_down_ctrl = tavil_mbhc_hph_pull_down_ctrl,
+ .mbhc_moisture_config = tavil_mbhc_moisture_config,
+ .hph_register_recovery = tavil_hph_register_recovery,
+ .update_anc_state = tavil_update_anc_state,
+ .is_anc_on = tavil_is_anc_on,
+};
+
+static struct regulator *tavil_codec_find_ondemand_regulator(
+ struct snd_soc_codec *codec, const char *name)
+{
+ int i;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+
+ for (i = 0; i < wcd9xxx->num_of_supplies; ++i) {
+ if (pdata->regulator[i].ondemand &&
+ wcd9xxx->supplies[i].supply &&
+ !strcmp(wcd9xxx->supplies[i].supply, name))
+ return wcd9xxx->supplies[i].consumer;
+ }
+
+ dev_dbg(codec->dev, "Warning: regulator not found:%s\n",
+ name);
+ return NULL;
+}
+
+static int tavil_get_hph_type(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+ struct wcd_mbhc *mbhc;
+
+ if (!wcd934x_mbhc) {
+ dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__);
+ return -EINVAL;
+ }
+
+ mbhc = &wcd934x_mbhc->wcd_mbhc;
+
+ ucontrol->value.integer.value[0] = (u32) mbhc->hph_type;
+ dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type);
+
+ return 0;
+}
+
+static int tavil_hph_impedance_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t zl, zr;
+ bool hphr;
+ struct soc_multi_mixer_control *mc;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+
+ if (!wcd934x_mbhc) {
+ dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__);
+ return -EINVAL;
+ }
+
+ mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+ wcd_mbhc_get_impedance(&wcd934x_mbhc->wcd_mbhc, &zl, &zr);
+ dev_dbg(codec->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr);
+ ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+ SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+ tavil_get_hph_type, NULL),
+};
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+ SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+ tavil_hph_impedance_get, NULL),
+ SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+ tavil_hph_impedance_get, NULL),
+};
+
+/*
+ * tavil_mbhc_get_impedance: get impedance of headphone left and right channels
+ * @wcd934x_mbhc: handle to struct wcd934x_mbhc *
+ * @zl: handle to left-ch impedance
+ * @zr: handle to right-ch impedance
+ * return 0 for success or error code in case of failure
+ */
+int tavil_mbhc_get_impedance(struct wcd934x_mbhc *wcd934x_mbhc,
+ uint32_t *zl, uint32_t *zr)
+{
+ if (!wcd934x_mbhc) {
+ pr_err("%s: mbhc not initialized!\n", __func__);
+ return -EINVAL;
+ }
+ if (!zl || !zr) {
+ pr_err("%s: zl or zr null!\n", __func__);
+ return -EINVAL;
+ }
+
+ return wcd_mbhc_get_impedance(&wcd934x_mbhc->wcd_mbhc, zl, zr);
+}
+EXPORT_SYMBOL(tavil_mbhc_get_impedance);
+
+/*
+ * tavil_mbhc_hs_detect: starts mbhc insertion/removal functionality
+ * @codec: handle to snd_soc_codec *
+ * @mbhc_cfg: handle to mbhc configuration structure
+ * return 0 if mbhc_start is success or error code in case of failure
+ */
+int tavil_mbhc_hs_detect(struct snd_soc_codec *codec,
+ struct wcd_mbhc_config *mbhc_cfg)
+{
+ struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+
+ if (!wcd934x_mbhc) {
+ dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__);
+ return -EINVAL;
+ }
+
+ return wcd_mbhc_start(&wcd934x_mbhc->wcd_mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL(tavil_mbhc_hs_detect);
+
+/*
+ * tavil_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality
+ * @codec: handle to snd_soc_codec *
+ */
+void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec)
+{
+ struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+
+ if (!wcd934x_mbhc) {
+ dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__);
+ return;
+ }
+ wcd_mbhc_stop(&wcd934x_mbhc->wcd_mbhc);
+}
+EXPORT_SYMBOL(tavil_mbhc_hs_detect_exit);
+
+/*
+ * tavil_mbhc_post_ssr_init: initialize mbhc for tavil post subsystem restart
+ * @mbhc: poniter to wcd934x_mbhc structure
+ * @codec: handle to snd_soc_codec *
+ *
+ * return 0 if mbhc_init is success or error code in case of failure
+ */
+int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc,
+ struct snd_soc_codec *codec)
+{
+ int ret;
+
+ if (!mbhc || !codec)
+ return -EINVAL;
+
+ wcd_mbhc_deinit(&mbhc->wcd_mbhc);
+ ret = wcd_mbhc_init(&mbhc->wcd_mbhc, codec, &mbhc_cb, &intr_ids,
+ wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED);
+ if (ret) {
+ dev_err(codec->dev, "%s: mbhc initialization failed\n",
+ __func__);
+ goto done;
+ }
+ snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01);
+
+done:
+ return ret;
+}
+EXPORT_SYMBOL(tavil_mbhc_post_ssr_init);
+
+/*
+ * tavil_mbhc_init: initialize mbhc for tavil
+ * @mbhc: poniter to wcd934x_mbhc struct pointer to store the configs
+ * @codec: handle to snd_soc_codec *
+ * @fw_data: handle to firmware data
+ *
+ * return 0 if mbhc_init is success or error code in case of failure
+ */
+int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, struct snd_soc_codec *codec,
+ struct fw_info *fw_data)
+{
+ struct regulator *supply;
+ struct wcd934x_mbhc *wcd934x_mbhc;
+ int ret;
+
+ wcd934x_mbhc = devm_kzalloc(codec->dev, sizeof(struct wcd934x_mbhc),
+ GFP_KERNEL);
+ if (!wcd934x_mbhc)
+ return -ENOMEM;
+
+ wcd934x_mbhc->wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ wcd934x_mbhc->fw_data = fw_data;
+ BLOCKING_INIT_NOTIFIER_HEAD(&wcd934x_mbhc->notifier);
+
+ ret = wcd_mbhc_init(&wcd934x_mbhc->wcd_mbhc, codec, &mbhc_cb, &intr_ids,
+ wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED);
+ if (ret) {
+ dev_err(codec->dev, "%s: mbhc initialization failed\n",
+ __func__);
+ goto err;
+ }
+
+ supply = tavil_codec_find_ondemand_regulator(codec,
+ on_demand_supply_name[WCD934X_ON_DEMAND_MICBIAS]);
+ if (supply) {
+ wcd934x_mbhc->on_demand_list[
+ WCD934X_ON_DEMAND_MICBIAS].supply =
+ supply;
+ wcd934x_mbhc->on_demand_list[
+ WCD934X_ON_DEMAND_MICBIAS].ondemand_supply_count =
+ 0;
+ }
+
+ (*mbhc) = wcd934x_mbhc;
+ snd_soc_add_codec_controls(codec, impedance_detect_controls,
+ ARRAY_SIZE(impedance_detect_controls));
+ snd_soc_add_codec_controls(codec, hph_type_detect_controls,
+ ARRAY_SIZE(hph_type_detect_controls));
+
+ snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01);
+
+ return 0;
+err:
+ devm_kfree(codec->dev, wcd934x_mbhc);
+ return ret;
+}
+EXPORT_SYMBOL(tavil_mbhc_init);
+
+/*
+ * tavil_mbhc_deinit: deinitialize mbhc for tavil
+ * @codec: handle to snd_soc_codec *
+ */
+void tavil_mbhc_deinit(struct snd_soc_codec *codec)
+{
+ struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+
+ if (wcd934x_mbhc) {
+ wcd_mbhc_deinit(&wcd934x_mbhc->wcd_mbhc);
+ devm_kfree(codec->dev, wcd934x_mbhc);
+ }
+}
+EXPORT_SYMBOL(tavil_mbhc_deinit);
diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.h b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h
new file mode 100644
index 000000000000..27b96799be2b
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD934X_MBHC_H__
+#define __WCD934X_MBHC_H__
+#include "../wcd-mbhc-v2.h"
+
+enum wcd934x_on_demand_supply_name {
+ WCD934X_ON_DEMAND_MICBIAS = 0,
+ WCD934X_ON_DEMAND_SUPPLIES_MAX,
+};
+
+struct wcd934x_on_demand_supply {
+ struct regulator *supply;
+ int ondemand_supply_count;
+};
+
+struct wcd934x_mbhc {
+ struct wcd_mbhc wcd_mbhc;
+ struct blocking_notifier_head notifier;
+ struct wcd934x_on_demand_supply on_demand_list[
+ WCD934X_ON_DEMAND_SUPPLIES_MAX];
+ struct wcd9xxx *wcd9xxx;
+ struct fw_info *fw_data;
+ bool mbhc_started;
+ bool is_hph_recover;
+};
+
+#ifdef CONFIG_SND_SOC_WCD934X_MBHC
+extern int tavil_mbhc_init(struct wcd934x_mbhc **mbhc,
+ struct snd_soc_codec *codec,
+ struct fw_info *fw_data);
+extern void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec);
+extern int tavil_mbhc_hs_detect(struct snd_soc_codec *codec,
+ struct wcd_mbhc_config *mbhc_cfg);
+extern void tavil_mbhc_deinit(struct snd_soc_codec *codec);
+extern int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc,
+ struct snd_soc_codec *codec);
+extern int tavil_mbhc_get_impedance(struct wcd934x_mbhc *wcd934x_mbhc,
+ uint32_t *zl, uint32_t *zr);
+#else
+static inline int tavil_mbhc_init(struct wcd934x_mbhc **mbhc,
+ struct snd_soc_codec *codec,
+ struct fw_info *fw_data)
+{
+ return 0;
+}
+static inline void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec)
+{
+}
+static inline int tavil_mbhc_hs_detect(struct snd_soc_codec *codec,
+ struct wcd_mbhc_config *mbhc_cfg)
+{
+ return 0;
+}
+static inline void tavil_mbhc_deinit(struct snd_soc_codec *codec)
+{
+}
+static inline int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc,
+ struct snd_soc_codec *codec)
+{
+ return 0;
+}
+static inline int tavil_mbhc_get_impedance(struct wcd934x_mbhc *wcd934x_mbhc,
+ uint32_t *zl, uint32_t *zr)
+{
+ if (zl)
+ *zl = 0;
+ if (zr)
+ *zr = 0;
+ return -EINVAL;
+}
+#endif
+
+#endif /* __WCD934X_MBHC_H__ */
+
+
diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h
new file mode 100644
index 000000000000..afd93b2cf56d
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h
@@ -0,0 +1,1171 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef WCD934X_ROUTING_H
+#define WCD934X_ROUTING_H
+
+#include <sound/soc-dapm.h>
+
+const struct snd_soc_dapm_route tavil_slim_audio_map[] = {
+
+ /* Virtual input widgets */
+ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+ {"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+ {"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
+ {"AIF4 MAD", NULL, "AIF4_MAD Mixer"},
+
+ /* Virtual input widget Mixer */
+ {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0"},
+ {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1"},
+ {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2"},
+ {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3"},
+ {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4"},
+ {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5"},
+ {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6"},
+ {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7"},
+ {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8"},
+ {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9"},
+ {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10"},
+ {"AIF1_CAP Mixer", "SLIM TX11", "SLIM TX11"},
+ {"AIF1_CAP Mixer", "SLIM TX13", "SLIM TX13"},
+
+ {"AIF2_CAP Mixer", "SLIM TX0", "SLIM TX0"},
+ {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1"},
+ {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2"},
+ {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3"},
+ {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4"},
+ {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5"},
+ {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6"},
+ {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7"},
+ {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8"},
+ {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9"},
+ {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10"},
+ {"AIF2_CAP Mixer", "SLIM TX11", "SLIM TX11"},
+ {"AIF2_CAP Mixer", "SLIM TX13", "SLIM TX13"},
+
+ {"AIF3_CAP Mixer", "SLIM TX0", "SLIM TX0"},
+ {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1"},
+ {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2"},
+ {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3"},
+ {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4"},
+ {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5"},
+ {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6"},
+ {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7"},
+ {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8"},
+ {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9"},
+ {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10"},
+ {"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11"},
+ {"AIF3_CAP Mixer", "SLIM TX13", "SLIM TX13"},
+
+ {"AIF4_MAD Mixer", "SLIM TX13", "SLIM TX13"},
+
+ {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+ {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+
+ {"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+ {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+
+ {"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+ {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+
+ {"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"},
+ {"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"},
+
+ {"SLIM RX0", NULL, "SLIM RX0 MUX"},
+ {"SLIM RX1", NULL, "SLIM RX1 MUX"},
+ {"SLIM RX2", NULL, "SLIM RX2 MUX"},
+ {"SLIM RX3", NULL, "SLIM RX3 MUX"},
+ {"SLIM RX4", NULL, "SLIM RX4 MUX"},
+ {"SLIM RX5", NULL, "SLIM RX5 MUX"},
+ {"SLIM RX6", NULL, "SLIM RX6 MUX"},
+ {"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
+};
+
+const struct snd_soc_dapm_route tavil_audio_map[] = {
+
+ /* MAD */
+ {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"},
+ {"MAD_SEL MUX", "MSM", "MADINPUT"},
+
+ {"MAD_INP MUX", "MAD", "MAD_SEL MUX"},
+ {"MAD_INP MUX", "DEC1", "ADC MUX1"},
+
+ {"MAD_BROADCAST", "Switch", "MAD_INP MUX"},
+ {"MAD_CPE1", "Switch", "MAD_INP MUX"},
+ {"MAD_CPE2", "Switch", "MAD_INP MUX"},
+
+ {"MAD_CPE_OUT1", NULL, "MAD_CPE1"},
+ {"MAD_CPE_OUT2", NULL, "MAD_CPE2"},
+
+ /* VI Feedback */
+ {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"},
+ {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"},
+ {"AIF4 VI", NULL, "AIF4_VI Mixer"},
+
+ /* CDC Tx interface with SLIMBUS */
+ {"SLIM TX0", NULL, "CDC_IF TX0 MUX"},
+ {"SLIM TX1", NULL, "CDC_IF TX1 MUX"},
+ {"SLIM TX2", NULL, "CDC_IF TX2 MUX"},
+ {"SLIM TX3", NULL, "CDC_IF TX3 MUX"},
+ {"SLIM TX4", NULL, "CDC_IF TX4 MUX"},
+ {"SLIM TX5", NULL, "CDC_IF TX5 MUX"},
+ {"SLIM TX6", NULL, "CDC_IF TX6 MUX"},
+ {"SLIM TX7", NULL, "CDC_IF TX7 MUX"},
+ {"SLIM TX8", NULL, "CDC_IF TX8 MUX"},
+ {"SLIM TX9", NULL, "CDC_IF TX9 MUX"},
+ {"SLIM TX10", NULL, "CDC_IF TX10 MUX"},
+ {"SLIM TX11", NULL, "CDC_IF TX11 MUX"},
+ {"SLIM TX13", NULL, "CDC_IF TX13 MUX"},
+
+ {"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"},
+ {"CDC_IF TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"},
+ {"CDC_IF TX0 MUX", "DEC0_192", "ADC US MUX0"},
+
+ {"CDC_IF TX1 MUX", "DEC1", "ADC MUX1"},
+ {"CDC_IF TX1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"},
+ {"CDC_IF TX1 MUX", "DEC1_192", "ADC US MUX1"},
+
+ {"CDC_IF TX2 MUX", "DEC2", "ADC MUX2"},
+ {"CDC_IF TX2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"},
+ {"CDC_IF TX2 MUX", "DEC2_192", "ADC US MUX2"},
+
+ {"CDC_IF TX3 MUX", "DEC3", "ADC MUX3"},
+ {"CDC_IF TX3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"},
+ {"CDC_IF TX3 MUX", "DEC3_192", "ADC US MUX3"},
+
+ {"CDC_IF TX4 MUX", "DEC4", "ADC MUX4"},
+ {"CDC_IF TX4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"},
+ {"CDC_IF TX4 MUX", "DEC4_192", "ADC US MUX4"},
+
+ {"CDC_IF TX5 MUX", "DEC5", "ADC MUX5"},
+ {"CDC_IF TX5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"},
+ {"CDC_IF TX5 MUX", "DEC5_192", "ADC US MUX5"},
+
+ {"CDC_IF TX6 MUX", "DEC6", "ADC MUX6"},
+ {"CDC_IF TX6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"},
+ {"CDC_IF TX6 MUX", "DEC6_192", "ADC US MUX6"},
+
+ {"CDC_IF TX7 MUX", "DEC7", "ADC MUX7"},
+ {"CDC_IF TX7 MUX", "RX_MIX_TX7", "RX MIX TX7 MUX"},
+ {"CDC_IF TX7 MUX", "DEC7_192", "ADC US MUX7"},
+
+ {"CDC_IF TX8 MUX", "DEC8", "ADC MUX8"},
+ {"CDC_IF TX8 MUX", "RX_MIX_TX8", "RX MIX TX8 MUX"},
+ {"CDC_IF TX8 MUX", "DEC8_192", "ADC US MUX8"},
+
+ {"CDC_IF TX9 MUX", "DEC7", "ADC MUX7"},
+ {"CDC_IF TX9 MUX", "DEC7_192", "ADC US MUX7"},
+ {"CDC_IF TX10 MUX", "DEC6", "ADC MUX6"},
+ {"CDC_IF TX10 MUX", "DEC6_192", "ADC US MUX6"},
+
+ {"CDC_IF TX11 MUX", "DEC_0_5", "CDC_IF TX11 INP1 MUX"},
+ {"CDC_IF TX11 MUX", "DEC_9_12", "CDC_IF TX11 INP1 MUX"},
+ {"CDC_IF TX11 INP1 MUX", "DEC0", "ADC MUX0"},
+ {"CDC_IF TX11 INP1 MUX", "DEC1", "ADC MUX1"},
+ {"CDC_IF TX11 INP1 MUX", "DEC2", "ADC MUX2"},
+ {"CDC_IF TX11 INP1 MUX", "DEC3", "ADC MUX3"},
+ {"CDC_IF TX11 INP1 MUX", "DEC4", "ADC MUX4"},
+ {"CDC_IF TX11 INP1 MUX", "DEC5", "ADC MUX5"},
+ {"CDC_IF TX11 INP1 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"},
+
+ {"CDC_IF TX13 MUX", "MAD_BRDCST", "MAD_BROADCAST"},
+ {"CDC_IF TX13 MUX", "CDC_DEC_5", "CDC_IF TX13 INP1 MUX"},
+ {"CDC_IF TX13 INP1 MUX", "DEC5", "ADC MUX5"},
+ {"CDC_IF TX13 INP1 MUX", "DEC5_192", "ADC US MUX5"},
+
+ {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+ {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+ {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+ {"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+ {"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+ {"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+ {"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+ {"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+ {"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+ {"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+ {"ADC US MUX0", "US_Switch", "ADC MUX0"},
+ {"ADC US MUX1", "US_Switch", "ADC MUX1"},
+ {"ADC US MUX2", "US_Switch", "ADC MUX2"},
+ {"ADC US MUX3", "US_Switch", "ADC MUX3"},
+ {"ADC US MUX4", "US_Switch", "ADC MUX4"},
+ {"ADC US MUX5", "US_Switch", "ADC MUX5"},
+ {"ADC US MUX6", "US_Switch", "ADC MUX6"},
+ {"ADC US MUX7", "US_Switch", "ADC MUX7"},
+ {"ADC US MUX8", "US_Switch", "ADC MUX8"},
+
+ {"ADC MUX0", "DMIC", "DMIC MUX0"},
+ {"ADC MUX0", "AMIC", "AMIC MUX0"},
+ {"ADC MUX1", "DMIC", "DMIC MUX1"},
+ {"ADC MUX1", "AMIC", "AMIC MUX1"},
+ {"ADC MUX2", "DMIC", "DMIC MUX2"},
+ {"ADC MUX2", "AMIC", "AMIC MUX2"},
+ {"ADC MUX3", "DMIC", "DMIC MUX3"},
+ {"ADC MUX3", "AMIC", "AMIC MUX3"},
+ {"ADC MUX4", "DMIC", "DMIC MUX4"},
+ {"ADC MUX4", "AMIC", "AMIC MUX4"},
+ {"ADC MUX5", "DMIC", "DMIC MUX5"},
+ {"ADC MUX5", "AMIC", "AMIC MUX5"},
+ {"ADC MUX6", "DMIC", "DMIC MUX6"},
+ {"ADC MUX6", "AMIC", "AMIC MUX6"},
+ {"ADC MUX7", "DMIC", "DMIC MUX7"},
+ {"ADC MUX7", "AMIC", "AMIC MUX7"},
+ {"ADC MUX8", "DMIC", "DMIC MUX8"},
+ {"ADC MUX8", "AMIC", "AMIC MUX8"},
+ {"ADC MUX10", "DMIC", "DMIC MUX10"},
+ {"ADC MUX10", "AMIC", "AMIC MUX10"},
+ {"ADC MUX11", "DMIC", "DMIC MUX11"},
+ {"ADC MUX11", "AMIC", "AMIC MUX11"},
+ {"ADC MUX12", "DMIC", "DMIC MUX12"},
+ {"ADC MUX12", "AMIC", "AMIC MUX12"},
+ {"ADC MUX13", "DMIC", "DMIC MUX13"},
+ {"ADC MUX13", "AMIC", "AMIC MUX13"},
+
+ {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"},
+
+ {"DMIC MUX0", "DMIC0", "DMIC0"},
+ {"DMIC MUX0", "DMIC1", "DMIC1"},
+ {"DMIC MUX0", "DMIC2", "DMIC2"},
+ {"DMIC MUX0", "DMIC3", "DMIC3"},
+ {"DMIC MUX0", "DMIC4", "DMIC4"},
+ {"DMIC MUX0", "DMIC5", "DMIC5"},
+ {"AMIC MUX0", "ADC1", "ADC1"},
+ {"AMIC MUX0", "ADC2", "ADC2"},
+ {"AMIC MUX0", "ADC3", "ADC3"},
+ {"AMIC MUX0", "ADC4", "ADC4"},
+
+ {"DMIC MUX1", "DMIC0", "DMIC0"},
+ {"DMIC MUX1", "DMIC1", "DMIC1"},
+ {"DMIC MUX1", "DMIC2", "DMIC2"},
+ {"DMIC MUX1", "DMIC3", "DMIC3"},
+ {"DMIC MUX1", "DMIC4", "DMIC4"},
+ {"DMIC MUX1", "DMIC5", "DMIC5"},
+ {"AMIC MUX1", "ADC1", "ADC1"},
+ {"AMIC MUX1", "ADC2", "ADC2"},
+ {"AMIC MUX1", "ADC3", "ADC3"},
+ {"AMIC MUX1", "ADC4", "ADC4"},
+
+ {"DMIC MUX2", "DMIC0", "DMIC0"},
+ {"DMIC MUX2", "DMIC1", "DMIC1"},
+ {"DMIC MUX2", "DMIC2", "DMIC2"},
+ {"DMIC MUX2", "DMIC3", "DMIC3"},
+ {"DMIC MUX2", "DMIC4", "DMIC4"},
+ {"DMIC MUX2", "DMIC5", "DMIC5"},
+ {"AMIC MUX2", "ADC1", "ADC1"},
+ {"AMIC MUX2", "ADC2", "ADC2"},
+ {"AMIC MUX2", "ADC3", "ADC3"},
+ {"AMIC MUX2", "ADC4", "ADC4"},
+
+ {"DMIC MUX3", "DMIC0", "DMIC0"},
+ {"DMIC MUX3", "DMIC1", "DMIC1"},
+ {"DMIC MUX3", "DMIC2", "DMIC2"},
+ {"DMIC MUX3", "DMIC3", "DMIC3"},
+ {"DMIC MUX3", "DMIC4", "DMIC4"},
+ {"DMIC MUX3", "DMIC5", "DMIC5"},
+ {"AMIC MUX3", "ADC1", "ADC1"},
+ {"AMIC MUX3", "ADC2", "ADC2"},
+ {"AMIC MUX3", "ADC3", "ADC3"},
+ {"AMIC MUX3", "ADC4", "ADC4"},
+
+ {"DMIC MUX4", "DMIC0", "DMIC0"},
+ {"DMIC MUX4", "DMIC1", "DMIC1"},
+ {"DMIC MUX4", "DMIC2", "DMIC2"},
+ {"DMIC MUX4", "DMIC3", "DMIC3"},
+ {"DMIC MUX4", "DMIC4", "DMIC4"},
+ {"DMIC MUX4", "DMIC5", "DMIC5"},
+ {"AMIC MUX4", "ADC1", "ADC1"},
+ {"AMIC MUX4", "ADC2", "ADC2"},
+ {"AMIC MUX4", "ADC3", "ADC3"},
+ {"AMIC MUX4", "ADC4", "ADC4"},
+
+ {"DMIC MUX5", "DMIC0", "DMIC0"},
+ {"DMIC MUX5", "DMIC1", "DMIC1"},
+ {"DMIC MUX5", "DMIC2", "DMIC2"},
+ {"DMIC MUX5", "DMIC3", "DMIC3"},
+ {"DMIC MUX5", "DMIC4", "DMIC4"},
+ {"DMIC MUX5", "DMIC5", "DMIC5"},
+ {"AMIC MUX5", "ADC1", "ADC1"},
+ {"AMIC MUX5", "ADC2", "ADC2"},
+ {"AMIC MUX5", "ADC3", "ADC3"},
+ {"AMIC MUX5", "ADC4", "ADC4"},
+
+ {"DMIC MUX6", "DMIC0", "DMIC0"},
+ {"DMIC MUX6", "DMIC1", "DMIC1"},
+ {"DMIC MUX6", "DMIC2", "DMIC2"},
+ {"DMIC MUX6", "DMIC3", "DMIC3"},
+ {"DMIC MUX6", "DMIC4", "DMIC4"},
+ {"DMIC MUX6", "DMIC5", "DMIC5"},
+ {"AMIC MUX6", "ADC1", "ADC1"},
+ {"AMIC MUX6", "ADC2", "ADC2"},
+ {"AMIC MUX6", "ADC3", "ADC3"},
+ {"AMIC MUX6", "ADC4", "ADC4"},
+
+ {"DMIC MUX7", "DMIC0", "DMIC0"},
+ {"DMIC MUX7", "DMIC1", "DMIC1"},
+ {"DMIC MUX7", "DMIC2", "DMIC2"},
+ {"DMIC MUX7", "DMIC3", "DMIC3"},
+ {"DMIC MUX7", "DMIC4", "DMIC4"},
+ {"DMIC MUX7", "DMIC5", "DMIC5"},
+ {"AMIC MUX7", "ADC1", "ADC1"},
+ {"AMIC MUX7", "ADC2", "ADC2"},
+ {"AMIC MUX7", "ADC3", "ADC3"},
+ {"AMIC MUX7", "ADC4", "ADC4"},
+
+ {"DMIC MUX8", "DMIC0", "DMIC0"},
+ {"DMIC MUX8", "DMIC1", "DMIC1"},
+ {"DMIC MUX8", "DMIC2", "DMIC2"},
+ {"DMIC MUX8", "DMIC3", "DMIC3"},
+ {"DMIC MUX8", "DMIC4", "DMIC4"},
+ {"DMIC MUX8", "DMIC5", "DMIC5"},
+ {"AMIC MUX8", "ADC1", "ADC1"},
+ {"AMIC MUX8", "ADC2", "ADC2"},
+ {"AMIC MUX8", "ADC3", "ADC3"},
+ {"AMIC MUX8", "ADC4", "ADC4"},
+
+ {"DMIC MUX10", "DMIC0", "DMIC0"},
+ {"DMIC MUX10", "DMIC1", "DMIC1"},
+ {"DMIC MUX10", "DMIC2", "DMIC2"},
+ {"DMIC MUX10", "DMIC3", "DMIC3"},
+ {"DMIC MUX10", "DMIC4", "DMIC4"},
+ {"DMIC MUX10", "DMIC5", "DMIC5"},
+ {"AMIC MUX10", "ADC1", "ADC1"},
+ {"AMIC MUX10", "ADC2", "ADC2"},
+ {"AMIC MUX10", "ADC3", "ADC3"},
+ {"AMIC MUX10", "ADC4", "ADC4"},
+
+ {"DMIC MUX11", "DMIC0", "DMIC0"},
+ {"DMIC MUX11", "DMIC1", "DMIC1"},
+ {"DMIC MUX11", "DMIC2", "DMIC2"},
+ {"DMIC MUX11", "DMIC3", "DMIC3"},
+ {"DMIC MUX11", "DMIC4", "DMIC4"},
+ {"DMIC MUX11", "DMIC5", "DMIC5"},
+ {"AMIC MUX11", "ADC1", "ADC1"},
+ {"AMIC MUX11", "ADC2", "ADC2"},
+ {"AMIC MUX11", "ADC3", "ADC3"},
+ {"AMIC MUX11", "ADC4", "ADC4"},
+
+ {"DMIC MUX12", "DMIC0", "DMIC0"},
+ {"DMIC MUX12", "DMIC1", "DMIC1"},
+ {"DMIC MUX12", "DMIC2", "DMIC2"},
+ {"DMIC MUX12", "DMIC3", "DMIC3"},
+ {"DMIC MUX12", "DMIC4", "DMIC4"},
+ {"DMIC MUX12", "DMIC5", "DMIC5"},
+ {"AMIC MUX12", "ADC1", "ADC1"},
+ {"AMIC MUX12", "ADC2", "ADC2"},
+ {"AMIC MUX12", "ADC3", "ADC3"},
+ {"AMIC MUX12", "ADC4", "ADC4"},
+
+ {"DMIC MUX13", "DMIC0", "DMIC0"},
+ {"DMIC MUX13", "DMIC1", "DMIC1"},
+ {"DMIC MUX13", "DMIC2", "DMIC2"},
+ {"DMIC MUX13", "DMIC3", "DMIC3"},
+ {"DMIC MUX13", "DMIC4", "DMIC4"},
+ {"DMIC MUX13", "DMIC5", "DMIC5"},
+ {"AMIC MUX13", "ADC1", "ADC1"},
+ {"AMIC MUX13", "ADC2", "ADC2"},
+ {"AMIC MUX13", "ADC3", "ADC3"},
+ {"AMIC MUX13", "ADC4", "ADC4"},
+
+ {"AMIC4_5 SEL", "AMIC4", "AMIC4"},
+ {"AMIC4_5 SEL", "AMIC5", "AMIC5"},
+
+ {"ADC1", NULL, "AMIC1"},
+ {"ADC2", NULL, "AMIC2"},
+ {"ADC3", NULL, "AMIC3"},
+ {"ADC4", NULL, "AMIC4_5 SEL"},
+
+ /* CDC Rx interface with SLIMBUS */
+ {"CDC_IF RX0 MUX", "SLIM RX0", "SLIM RX0"},
+ {"CDC_IF RX1 MUX", "SLIM RX1", "SLIM RX1"},
+ {"CDC_IF RX2 MUX", "SLIM RX2", "SLIM RX2"},
+ {"CDC_IF RX3 MUX", "SLIM RX3", "SLIM RX3"},
+ {"CDC_IF RX4 MUX", "SLIM RX4", "SLIM RX4"},
+ {"CDC_IF RX5 MUX", "SLIM RX5", "SLIM RX5"},
+ {"CDC_IF RX6 MUX", "SLIM RX6", "SLIM RX6"},
+ {"CDC_IF RX7 MUX", "SLIM RX7", "SLIM RX7"},
+
+ {"RX INT0_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT0_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT0_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT0_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT0_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT0_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT0_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT0_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT0_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT0_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT0_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT0_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT0_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT0_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT0_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT0_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT0_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT0_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT0_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT0_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT0_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT0_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT0_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT0_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT1_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT1_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT1_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT1_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT1_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT1_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT1_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT1_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT1_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT1_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT1_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT1_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT1_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT1_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT1_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT1_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT1_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT1_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT1_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT1_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT1_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT1_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT1_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT1_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT2_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT2_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT2_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT2_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT2_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT2_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT2_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT2_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT2_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT2_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT2_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT2_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT2_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT2_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT2_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT2_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT2_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT2_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT2_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT2_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT2_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT2_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT3_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT3_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT3_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT3_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT3_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT3_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT3_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT3_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT3_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT3_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT3_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT3_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT3_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT3_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT3_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT3_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT3_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT3_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT3_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT3_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT3_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT3_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT3_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT3_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT3_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT3_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT3_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT3_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT3_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT3_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT4_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT4_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT4_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT4_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT4_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT4_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT4_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT4_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT4_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT4_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT4_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT4_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT4_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT4_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT4_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT4_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT4_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT4_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT4_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT4_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT4_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT4_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT4_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT4_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT4_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT4_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT4_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT4_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT4_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT4_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT7_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT7_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT7_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT7_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT7_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT7_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT7_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT7_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT7_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT7_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT7_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT7_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT7_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT7_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT7_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT7_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT7_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT7_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT7_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT7_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT7_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT7_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT7_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT7_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT7_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT7_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT7_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT7_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT7_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT7_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT8_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT8_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT8_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT8_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT8_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT8_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT8_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT8_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT8_1 MIX1 INP0", "IIR0", "IIR0"},
+ {"RX INT8_1 MIX1 INP0", "IIR1", "IIR1"},
+ {"RX INT8_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT8_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT8_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT8_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT8_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT8_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT8_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT8_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT8_1 MIX1 INP1", "IIR0", "IIR0"},
+ {"RX INT8_1 MIX1 INP1", "IIR1", "IIR1"},
+ {"RX INT8_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT8_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT8_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT8_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT8_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT8_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT8_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT8_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT8_1 MIX1 INP2", "IIR0", "IIR0"},
+ {"RX INT8_1 MIX1 INP2", "IIR1", "IIR1"},
+
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"},
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"},
+ {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"},
+ {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"},
+ {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"},
+ {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP0"},
+ {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP1"},
+ {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP2"},
+ {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP0"},
+ {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP1"},
+ {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP2"},
+ {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"},
+ {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP1"},
+ {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP2"},
+ {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP0"},
+ {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP1"},
+ {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP2"},
+
+ /* Mixing path INT0 */
+ {"RX INT0_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT0_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT0_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT0_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT0_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT0_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT0_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT0_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT0_2 INTERP", NULL, "RX INT0_2 MUX"},
+ {"RX INT0 SEC MIX", NULL, "RX INT0_2 INTERP"},
+
+ /* Mixing path INT1 */
+ {"RX INT1_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT1_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT1_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT1_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT1_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT1_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT1_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT1_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT1_2 INTERP", NULL, "RX INT1_2 MUX"},
+ {"RX INT1 SEC MIX", NULL, "RX INT1_2 INTERP"},
+
+ /* Mixing path INT2 */
+ {"RX INT2_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT2_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT2_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT2_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT2_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT2_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT2_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT2_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT2_2 INTERP", NULL, "RX INT2_2 MUX"},
+ {"RX INT2 SEC MIX", NULL, "RX INT2_2 INTERP"},
+
+ /* Mixing path INT3 */
+ {"RX INT3_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT3_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT3_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT3_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT3_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT3_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT3_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT3_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT3_2 INTERP", NULL, "RX INT3_2 MUX"},
+ {"RX INT3 SEC MIX", NULL, "RX INT3_2 INTERP"},
+
+ /* Mixing path INT4 */
+ {"RX INT4_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT4_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT4_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT4_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT4_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT4_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT4_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT4_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT4_2 INTERP", NULL, "RX INT4_2 MUX"},
+ {"RX INT4 SEC MIX", NULL, "RX INT4_2 INTERP"},
+
+ /* Mixing path INT7 */
+ {"RX INT7_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT7_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT7_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT7_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT7_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT7_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT7_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT7_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT7_2 INTERP", NULL, "RX INT7_2 MUX"},
+ {"RX INT7 SEC MIX", NULL, "RX INT7_2 INTERP"},
+
+ /* Mixing path INT8 */
+ {"RX INT8_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"RX INT8_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"RX INT8_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"RX INT8_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"RX INT8_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"RX INT8_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"RX INT8_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"RX INT8_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"RX INT8_2 INTERP", NULL, "RX INT8_2 MUX"},
+ {"RX INT8 SEC MIX", NULL, "RX INT8_2 INTERP"},
+
+ {"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"},
+ {"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"},
+ {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"},
+ {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"},
+ {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"},
+ {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"},
+ {"RX INT0 DAC", NULL, "RX_BIAS"},
+ {"EAR PA", NULL, "RX INT0 DAC"},
+ {"EAR", NULL, "EAR PA"},
+
+ {"RX INT1_1 INTERP", NULL, "RX INT1_1 MIX1"},
+ {"RX INT1 SEC MIX", NULL, "RX INT1_1 INTERP"},
+ {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"},
+ {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"},
+ {"RX INT1 MIX3", NULL, "RX INT1 MIX2"},
+ {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX3"},
+ {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"},
+ {"RX INT1 DAC", NULL, "RX_BIAS"},
+ {"HPHL PA", NULL, "RX INT1 DAC"},
+ {"HPHL", NULL, "HPHL PA"},
+
+ {"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"},
+ {"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"},
+ {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"},
+ {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"},
+ {"RX INT2 MIX3", NULL, "RX INT2 MIX2"},
+ {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 MIX3"},
+ {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"},
+ {"RX INT2 DAC", NULL, "RX_BIAS"},
+ {"HPHR PA", NULL, "RX INT2 DAC"},
+ {"HPHR", NULL, "HPHR PA"},
+
+ {"RX INT3_1 INTERP", NULL, "RX INT3_1 MIX1"},
+ {"RX INT3 SEC MIX", NULL, "RX INT3_1 INTERP"},
+ {"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"},
+ {"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"},
+ {"RX INT3 MIX3", NULL, "RX INT3 MIX2"},
+ {"RX INT3 DAC", NULL, "RX INT3 MIX3"},
+ {"RX INT3 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT1 PA", NULL, "RX INT3 DAC"},
+ {"LINEOUT1", NULL, "LINEOUT1 PA"},
+
+ {"RX INT4_1 INTERP", NULL, "RX INT4_1 MIX1"},
+ {"RX INT4 SEC MIX", NULL, "RX INT4_1 INTERP"},
+ {"RX INT4 SEC MIX", NULL, "RX INT4_1 MIX1"},
+ {"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"},
+ {"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"},
+ {"RX INT4 MIX3", NULL, "RX INT4 MIX2"},
+ {"RX INT4 DAC", NULL, "RX INT4 MIX3"},
+ {"RX INT4 DAC", NULL, "RX_BIAS"},
+ {"LINEOUT2 PA", NULL, "RX INT4 DAC"},
+ {"LINEOUT2", NULL, "LINEOUT2 PA"},
+
+ {"RX INT7_1 INTERP", NULL, "RX INT7_1 MIX1"},
+ {"RX INT7 SEC MIX", NULL, "RX INT7_1 INTERP"},
+ {"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"},
+ {"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"},
+ {"RX INT7 CHAIN", NULL, "RX INT7 MIX2"},
+ {"RX INT7 CHAIN", NULL, "RX_BIAS"},
+ {"SPK1 OUT", NULL, "RX INT7 CHAIN"},
+
+ {"RX INT8_1 INTERP", NULL, "RX INT8_1 MIX1"},
+ {"RX INT8 SEC MIX", NULL, "RX INT8_1 INTERP"},
+ {"RX INT8 SEC MIX", NULL, "RX INT8_1 MIX1"},
+ {"RX INT8 CHAIN", NULL, "RX INT8 SEC MIX"},
+ {"RX INT8 CHAIN", NULL, "RX_BIAS"},
+ {"SPK2 OUT", NULL, "RX INT8 CHAIN"},
+
+ /* ANC Routing */
+ {"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"},
+ {"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"},
+ {"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"},
+ {"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"},
+ {"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"},
+ {"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"},
+
+ {"ANC OUT EAR Enable", "Switch", "ADC MUX10"},
+ {"ANC OUT EAR Enable", "Switch", "ADC MUX11"},
+ {"RX INT0 MIX2", NULL, "ANC OUT EAR Enable"},
+
+ {"ANC OUT HPHL Enable", "Switch", "ADC MUX10"},
+ {"ANC OUT HPHL Enable", "Switch", "ADC MUX11"},
+ {"RX INT1 MIX2", NULL, "ANC OUT HPHL Enable"},
+
+ {"ANC OUT HPHR Enable", "Switch", "ADC MUX12"},
+ {"ANC OUT HPHR Enable", "Switch", "ADC MUX13"},
+ {"RX INT2 MIX2", NULL, "ANC OUT HPHR Enable"},
+
+ {"ANC EAR PA", NULL, "RX INT0 DAC"},
+ {"ANC EAR", NULL, "ANC EAR PA"},
+
+ {"ANC HPHL PA", NULL, "RX INT1 DAC"},
+ {"ANC HPHL", NULL, "ANC HPHL PA"},
+
+ {"ANC HPHR PA", NULL, "RX INT2 DAC"},
+ {"ANC HPHR", NULL, "ANC HPHR PA"},
+
+ {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"},
+ {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"},
+ {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"},
+
+ {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"},
+ {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"},
+ {"SPK1 OUT", NULL, "ANC SPK1 PA"},
+
+ /*
+ * SRC0, SRC1 inputs to Sidetone RX Mixer
+ * on RX0, RX1, RX2, RX3, RX4 and RX7 chains
+ */
+ {"IIR0", NULL, "IIR0 INP0 MUX"},
+ {"IIR0 INP0 MUX", "DEC0", "ADC MUX0"},
+ {"IIR0 INP0 MUX", "DEC1", "ADC MUX1"},
+ {"IIR0 INP0 MUX", "DEC2", "ADC MUX2"},
+ {"IIR0 INP0 MUX", "DEC3", "ADC MUX3"},
+ {"IIR0 INP0 MUX", "DEC4", "ADC MUX4"},
+ {"IIR0 INP0 MUX", "DEC5", "ADC MUX5"},
+ {"IIR0 INP0 MUX", "DEC6", "ADC MUX6"},
+ {"IIR0 INP0 MUX", "DEC7", "ADC MUX7"},
+ {"IIR0 INP0 MUX", "DEC8", "ADC MUX8"},
+ {"IIR0 INP0 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"IIR0 INP0 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"IIR0 INP0 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"IIR0 INP0 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"IIR0 INP0 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"IIR0 INP0 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"IIR0 INP0 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"IIR0 INP0 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"IIR0", NULL, "IIR0 INP1 MUX"},
+ {"IIR0 INP1 MUX", "DEC0", "ADC MUX0"},
+ {"IIR0 INP1 MUX", "DEC1", "ADC MUX1"},
+ {"IIR0 INP1 MUX", "DEC2", "ADC MUX2"},
+ {"IIR0 INP1 MUX", "DEC3", "ADC MUX3"},
+ {"IIR0 INP1 MUX", "DEC4", "ADC MUX4"},
+ {"IIR0 INP1 MUX", "DEC5", "ADC MUX5"},
+ {"IIR0 INP1 MUX", "DEC6", "ADC MUX6"},
+ {"IIR0 INP1 MUX", "DEC7", "ADC MUX7"},
+ {"IIR0 INP1 MUX", "DEC8", "ADC MUX8"},
+ {"IIR0 INP1 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"IIR0 INP1 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"IIR0 INP1 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"IIR0 INP1 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"IIR0 INP1 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"IIR0 INP1 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"IIR0 INP1 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"IIR0 INP1 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"IIR0", NULL, "IIR0 INP2 MUX"},
+ {"IIR0 INP2 MUX", "DEC0", "ADC MUX0"},
+ {"IIR0 INP2 MUX", "DEC1", "ADC MUX1"},
+ {"IIR0 INP2 MUX", "DEC2", "ADC MUX2"},
+ {"IIR0 INP2 MUX", "DEC3", "ADC MUX3"},
+ {"IIR0 INP2 MUX", "DEC4", "ADC MUX4"},
+ {"IIR0 INP2 MUX", "DEC5", "ADC MUX5"},
+ {"IIR0 INP2 MUX", "DEC6", "ADC MUX6"},
+ {"IIR0 INP2 MUX", "DEC7", "ADC MUX7"},
+ {"IIR0 INP2 MUX", "DEC8", "ADC MUX8"},
+ {"IIR0 INP2 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"IIR0 INP2 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"IIR0 INP2 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"IIR0 INP2 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"IIR0 INP2 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"IIR0 INP2 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"IIR0 INP2 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"IIR0 INP2 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"IIR0", NULL, "IIR0 INP3 MUX"},
+ {"IIR0 INP3 MUX", "DEC0", "ADC MUX0"},
+ {"IIR0 INP3 MUX", "DEC1", "ADC MUX1"},
+ {"IIR0 INP3 MUX", "DEC2", "ADC MUX2"},
+ {"IIR0 INP3 MUX", "DEC3", "ADC MUX3"},
+ {"IIR0 INP3 MUX", "DEC4", "ADC MUX4"},
+ {"IIR0 INP3 MUX", "DEC5", "ADC MUX5"},
+ {"IIR0 INP3 MUX", "DEC6", "ADC MUX6"},
+ {"IIR0 INP3 MUX", "DEC7", "ADC MUX7"},
+ {"IIR0 INP3 MUX", "DEC8", "ADC MUX8"},
+ {"IIR0 INP3 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"IIR0 INP3 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"IIR0 INP3 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"IIR0 INP3 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"IIR0 INP3 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"IIR0 INP3 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"IIR0 INP3 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"IIR0 INP3 MUX", "RX7", "CDC_IF RX7 MUX"},
+
+ {"IIR1", NULL, "IIR1 INP0 MUX"},
+ {"IIR1 INP0 MUX", "DEC0", "ADC MUX0"},
+ {"IIR1 INP0 MUX", "DEC1", "ADC MUX1"},
+ {"IIR1 INP0 MUX", "DEC2", "ADC MUX2"},
+ {"IIR1 INP0 MUX", "DEC3", "ADC MUX3"},
+ {"IIR1 INP0 MUX", "DEC4", "ADC MUX4"},
+ {"IIR1 INP0 MUX", "DEC5", "ADC MUX5"},
+ {"IIR1 INP0 MUX", "DEC6", "ADC MUX6"},
+ {"IIR1 INP0 MUX", "DEC7", "ADC MUX7"},
+ {"IIR1 INP0 MUX", "DEC8", "ADC MUX8"},
+ {"IIR1 INP0 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"IIR1 INP0 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"IIR1 INP0 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"IIR1 INP0 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"IIR1 INP0 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"IIR1 INP0 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"IIR1 INP0 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"IIR1 INP0 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"IIR1", NULL, "IIR1 INP1 MUX"},
+ {"IIR1 INP1 MUX", "DEC0", "ADC MUX0"},
+ {"IIR1 INP1 MUX", "DEC1", "ADC MUX1"},
+ {"IIR1 INP1 MUX", "DEC2", "ADC MUX2"},
+ {"IIR1 INP1 MUX", "DEC3", "ADC MUX3"},
+ {"IIR1 INP1 MUX", "DEC4", "ADC MUX4"},
+ {"IIR1 INP1 MUX", "DEC5", "ADC MUX5"},
+ {"IIR1 INP1 MUX", "DEC6", "ADC MUX6"},
+ {"IIR1 INP1 MUX", "DEC7", "ADC MUX7"},
+ {"IIR1 INP1 MUX", "DEC8", "ADC MUX8"},
+ {"IIR1 INP1 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"IIR1 INP1 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"IIR1 INP1 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"IIR1 INP1 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"IIR1 INP1 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"IIR1 INP1 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"IIR1 INP1 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"IIR1 INP1 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"IIR1", NULL, "IIR1 INP2 MUX"},
+ {"IIR1 INP2 MUX", "DEC0", "ADC MUX0"},
+ {"IIR1 INP2 MUX", "DEC1", "ADC MUX1"},
+ {"IIR1 INP2 MUX", "DEC2", "ADC MUX2"},
+ {"IIR1 INP2 MUX", "DEC3", "ADC MUX3"},
+ {"IIR1 INP2 MUX", "DEC4", "ADC MUX4"},
+ {"IIR1 INP2 MUX", "DEC5", "ADC MUX5"},
+ {"IIR1 INP2 MUX", "DEC6", "ADC MUX6"},
+ {"IIR1 INP2 MUX", "DEC7", "ADC MUX7"},
+ {"IIR1 INP2 MUX", "DEC8", "ADC MUX8"},
+ {"IIR1 INP2 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"IIR1 INP2 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"IIR1 INP2 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"IIR1 INP2 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"IIR1 INP2 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"IIR1 INP2 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"IIR1 INP2 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"IIR1 INP2 MUX", "RX7", "CDC_IF RX7 MUX"},
+ {"IIR1", NULL, "IIR1 INP3 MUX"},
+ {"IIR1 INP3 MUX", "DEC0", "ADC MUX0"},
+ {"IIR1 INP3 MUX", "DEC1", "ADC MUX1"},
+ {"IIR1 INP3 MUX", "DEC2", "ADC MUX2"},
+ {"IIR1 INP3 MUX", "DEC3", "ADC MUX3"},
+ {"IIR1 INP3 MUX", "DEC4", "ADC MUX4"},
+ {"IIR1 INP3 MUX", "DEC5", "ADC MUX5"},
+ {"IIR1 INP3 MUX", "DEC6", "ADC MUX6"},
+ {"IIR1 INP3 MUX", "DEC7", "ADC MUX7"},
+ {"IIR1 INP3 MUX", "DEC8", "ADC MUX8"},
+ {"IIR1 INP3 MUX", "RX0", "CDC_IF RX0 MUX"},
+ {"IIR1 INP3 MUX", "RX1", "CDC_IF RX1 MUX"},
+ {"IIR1 INP3 MUX", "RX2", "CDC_IF RX2 MUX"},
+ {"IIR1 INP3 MUX", "RX3", "CDC_IF RX3 MUX"},
+ {"IIR1 INP3 MUX", "RX4", "CDC_IF RX4 MUX"},
+ {"IIR1 INP3 MUX", "RX5", "CDC_IF RX5 MUX"},
+ {"IIR1 INP3 MUX", "RX6", "CDC_IF RX6 MUX"},
+ {"IIR1 INP3 MUX", "RX7", "CDC_IF RX7 MUX"},
+
+ {"SRC0", NULL, "IIR0"},
+ {"SRC1", NULL, "IIR1"},
+ {"RX INT0 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT0 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT1 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT1 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT2 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT2 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT3 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT3 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT4 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT4 MIX2 INP", "SRC1", "SRC1"},
+ {"RX INT7 MIX2 INP", "SRC0", "SRC0"},
+ {"RX INT7 MIX2 INP", "SRC1", "SRC1"},
+
+ /* Native clk main path routing */
+ {"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"},
+ {"RX INT1_1 INTERP", NULL, "RX INT1_1 NATIVE MUX"},
+ {"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"},
+
+ {"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"},
+ {"RX INT2_1 INTERP", NULL, "RX INT2_1 NATIVE MUX"},
+ {"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"},
+
+ {"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"},
+ {"RX INT3_1 INTERP", NULL, "RX INT3_1 NATIVE MUX"},
+ {"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"},
+
+ {"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"},
+ {"RX INT4_1 INTERP", NULL, "RX INT4_1 NATIVE MUX"},
+ {"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"},
+
+ /* Native clk mix path routing */
+ {"RX INT1_2 NATIVE MUX", "ON", "RX INT1_2 MUX"},
+ {"RX INT1_2 INTERP", NULL, "RX INT1_2 NATIVE MUX"},
+ {"RX INT1_2 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"},
+
+ {"RX INT2_2 NATIVE MUX", "ON", "RX INT2_2 MUX"},
+ {"RX INT2_2 INTERP", NULL, "RX INT2_2 NATIVE MUX"},
+ {"RX INT2_2 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"},
+
+ {"RX INT3_2 NATIVE MUX", "ON", "RX INT3_2 MUX"},
+ {"RX INT3_2 INTERP", NULL, "RX INT3_2 NATIVE MUX"},
+ {"RX INT3_2 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"},
+
+ {"RX INT4_2 NATIVE MUX", "ON", "RX INT4_2 MUX"},
+ {"RX INT4_2 INTERP", NULL, "RX INT4_2 NATIVE MUX"},
+ {"RX INT4_2 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"},
+
+ {"RX INT7_2 NATIVE MUX", "ON", "RX INT7_2 MUX"},
+ {"RX INT7_2 INTERP", NULL, "RX INT7_2 NATIVE MUX"},
+ {"RX INT7_2 NATIVE MUX", NULL, "RX INT7 NATIVE SUPPLY"},
+
+ {"RX INT8_2 NATIVE MUX", "ON", "RX INT8_2 MUX"},
+ {"RX INT8_2 INTERP", NULL, "RX INT8_2 NATIVE MUX"},
+ {"RX INT8_2 NATIVE MUX", NULL, "RX INT8 NATIVE SUPPLY"},
+
+ /* ASRC Routing */
+ {"ASRC0 MUX", "ASRC_IN_HPHL", "RX INT1_2 INTERP"},
+ {"RX INT1 SEC MIX", "HPHL Switch", "ASRC0 MUX"},
+
+ {"ASRC1 MUX", "ASRC_IN_HPHR", "RX INT2_2 INTERP"},
+ {"RX INT2 SEC MIX", "HPHR Switch", "ASRC1 MUX"},
+
+ {"ASRC0 MUX", "ASRC_IN_LO1", "RX INT3_2 INTERP"},
+ {"RX INT3 SEC MIX", "LO1 Switch", "ASRC0 MUX"},
+
+ {"ASRC1 MUX", "ASRC_IN_LO2", "RX INT4_2 INTERP"},
+ {"RX INT4 SEC MIX", "LO2 Switch", "ASRC1 MUX"},
+
+ {"ASRC2 MUX", "ASRC_IN_SPKR1", "RX INT7_2 INTERP"},
+ {"RX INT7 SEC MIX", NULL, "ASRC2 MUX"},
+
+ {"ASRC3 MUX", "ASRC_IN_SPKR2", "RX INT8_2 INTERP"},
+ {"RX INT8 SEC MIX", NULL, "ASRC3 MUX"},
+};
+
+#endif
diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c
new file mode 100644
index 000000000000..c8cc17d4dfd3
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x.c
@@ -0,0 +1,10133 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/regulator/consumer.h>
+#include <linux/soundwire/swr-wcd.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/info.h>
+#include "wcd934x.h"
+#include "wcd934x-mbhc.h"
+#include "wcd934x-routing.h"
+#include "wcd934x-dsp-cntl.h"
+#include "../wcd9xxx-common-v2.h"
+#include "../wcd9xxx-resmgr-v2.h"
+#include "../wcdcal-hwdep.h"
+#include "wcd934x-dsd.h"
+
+#define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_384000)
+/* Fractional Rates */
+#define WCD934X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400)
+
+#define WCD934X_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define WCD934X_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define WCD934X_FORMATS_S16_LE (SNDRV_PCM_FMTBIT_S16_LE)
+
+/* Macros for packing register writes into a U32 */
+#define WCD934X_PACKED_REG_SIZE sizeof(u32)
+#define WCD934X_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \
+ do { \
+ ((reg) = ((packed >> 16) & (0xffff))); \
+ ((mask) = ((packed >> 8) & (0xff))); \
+ ((val) = ((packed) & (0xff))); \
+ } while (0)
+
+#define STRING(name) #name
+#define WCD_DAPM_ENUM(name, reg, offset, text) \
+static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \
+static const struct snd_kcontrol_new name##_mux = \
+ SOC_DAPM_ENUM(STRING(name), name##_enum)
+
+#define WCD_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \
+static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \
+static const struct snd_kcontrol_new name##_mux = \
+ SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname)
+
+#define WCD_DAPM_MUX(name, shift, kctl) \
+ SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux)
+
+/*
+ * Timeout in milli seconds and it is the wait time for
+ * slim channel removal interrupt to receive.
+ */
+#define WCD934X_SLIM_CLOSE_TIMEOUT 1000
+#define WCD934X_SLIM_IRQ_OVERFLOW (1 << 0)
+#define WCD934X_SLIM_IRQ_UNDERFLOW (1 << 1)
+#define WCD934X_SLIM_IRQ_PORT_CLOSED (1 << 2)
+#define WCD934X_MCLK_CLK_12P288MHZ 12288000
+#define WCD934X_MCLK_CLK_9P6MHZ 9600000
+
+#define WCD934X_INTERP_MUX_NUM_INPUTS 3
+#define WCD934X_NUM_INTERPOLATORS 9
+#define WCD934X_NUM_DECIMATORS 9
+#define WCD934X_RX_PATH_CTL_OFFSET 20
+
+#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE))
+
+#define WCD934X_REG_BITS 8
+#define WCD934X_MAX_VALID_ADC_MUX 13
+#define WCD934X_INVALID_ADC_MUX 9
+
+#define WCD934X_AMIC_PWR_LEVEL_LP 0
+#define WCD934X_AMIC_PWR_LEVEL_DEFAULT 1
+#define WCD934X_AMIC_PWR_LEVEL_HP 2
+#define WCD934X_AMIC_PWR_LVL_MASK 0x60
+#define WCD934X_AMIC_PWR_LVL_SHIFT 0x5
+
+#define WCD934X_DEC_PWR_LVL_MASK 0x06
+#define WCD934X_DEC_PWR_LVL_LP 0x02
+#define WCD934X_DEC_PWR_LVL_HP 0x04
+#define WCD934X_DEC_PWR_LVL_DF 0x00
+#define WCD934X_STRING_LEN 100
+
+#define WCD934X_CDC_SIDETONE_IIR_COEFF_MAX 5
+#define WCD934X_DIG_CORE_REG_MIN WCD934X_CDC_ANC0_CLK_RESET_CTL
+#define WCD934X_DIG_CORE_REG_MAX 0xFFF
+
+#define WCD934X_MAX_MICBIAS 4
+#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone"
+#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone"
+#define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone"
+#define DAPM_MICBIAS4_STANDALONE "MIC BIAS4 Standalone"
+
+#define TX_HPF_CUT_OFF_FREQ_MASK 0x60
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+
+#define CPE_ERR_WDOG_BITE BIT(0)
+#define CPE_FATAL_IRQS CPE_ERR_WDOG_BITE
+
+#define WCD934X_MAD_AUDIO_FIRMWARE_PATH "wcd934x/wcd934x_mad_audio.bin"
+
+#define TAVIL_VERSION_ENTRY_SIZE 17
+
+#define WCD934X_DIG_CORE_COLLAPSE_TIMER_MS (5 * 1000)
+
+enum {
+ POWER_COLLAPSE,
+ POWER_RESUME,
+};
+
+static int dig_core_collapse_enable = 1;
+module_param(dig_core_collapse_enable, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating");
+
+/* dig_core_collapse timer in seconds */
+static int dig_core_collapse_timer = (WCD934X_DIG_CORE_COLLAPSE_TIMER_MS/1000);
+module_param(dig_core_collapse_timer, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating");
+
+#define TAVIL_HPH_REG_RANGE_1 (WCD934X_HPH_R_DAC_CTL - WCD934X_HPH_CNP_EN + 1)
+#define TAVIL_HPH_REG_RANGE_2 (WCD934X_HPH_NEW_ANA_HPH3 -\
+ WCD934X_HPH_NEW_ANA_HPH2 + 1)
+#define TAVIL_HPH_REG_RANGE_3 (WCD934X_HPH_NEW_INT_PA_RDAC_MISC3 -\
+ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL + 1)
+#define TAVIL_HPH_TOTAL_REG (TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2 +\
+ TAVIL_HPH_REG_RANGE_3)
+
+enum {
+ VI_SENSE_1,
+ VI_SENSE_2,
+ AUDIO_NOMINAL,
+ HPH_PA_DELAY,
+ CLSH_Z_CONFIG,
+ ANC_MIC_AMIC1,
+ ANC_MIC_AMIC2,
+ ANC_MIC_AMIC3,
+ ANC_MIC_AMIC4,
+};
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ AIF2_PB,
+ AIF2_CAP,
+ AIF3_PB,
+ AIF3_CAP,
+ AIF4_PB,
+ AIF4_VIFEED,
+ AIF4_MAD_TX,
+ NUM_CODEC_DAIS,
+};
+
+enum {
+ INTn_1_INP_SEL_ZERO = 0,
+ INTn_1_INP_SEL_DEC0,
+ INTn_1_INP_SEL_DEC1,
+ INTn_1_INP_SEL_IIR0,
+ INTn_1_INP_SEL_IIR1,
+ INTn_1_INP_SEL_RX0,
+ INTn_1_INP_SEL_RX1,
+ INTn_1_INP_SEL_RX2,
+ INTn_1_INP_SEL_RX3,
+ INTn_1_INP_SEL_RX4,
+ INTn_1_INP_SEL_RX5,
+ INTn_1_INP_SEL_RX6,
+ INTn_1_INP_SEL_RX7,
+};
+
+enum {
+ INTn_2_INP_SEL_ZERO = 0,
+ INTn_2_INP_SEL_RX0,
+ INTn_2_INP_SEL_RX1,
+ INTn_2_INP_SEL_RX2,
+ INTn_2_INP_SEL_RX3,
+ INTn_2_INP_SEL_RX4,
+ INTn_2_INP_SEL_RX5,
+ INTn_2_INP_SEL_RX6,
+ INTn_2_INP_SEL_RX7,
+ INTn_2_INP_SEL_PROXIMITY,
+};
+
+enum {
+ INTERP_MAIN_PATH,
+ INTERP_MIX_PATH,
+};
+
+struct tavil_idle_detect_config {
+ u8 hph_idle_thr;
+ u8 hph_idle_detect_en;
+};
+
+static const struct intr_data wcd934x_intr_table[] = {
+ {WCD9XXX_IRQ_SLIMBUS, false},
+ {WCD934X_IRQ_MBHC_SW_DET, true},
+ {WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, true},
+ {WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, true},
+ {WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, true},
+ {WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true},
+ {WCD934X_IRQ_MISC, false},
+ {WCD934X_IRQ_HPH_PA_CNPL_COMPLETE, false},
+ {WCD934X_IRQ_HPH_PA_CNPR_COMPLETE, false},
+ {WCD934X_IRQ_EAR_PA_CNP_COMPLETE, false},
+ {WCD934X_IRQ_LINE_PA1_CNP_COMPLETE, false},
+ {WCD934X_IRQ_LINE_PA2_CNP_COMPLETE, false},
+ {WCD934X_IRQ_SLNQ_ANALOG_ERROR, false},
+ {WCD934X_IRQ_RESERVED_3, false},
+ {WCD934X_IRQ_HPH_PA_OCPL_FAULT, false},
+ {WCD934X_IRQ_HPH_PA_OCPR_FAULT, false},
+ {WCD934X_IRQ_EAR_PA_OCP_FAULT, false},
+ {WCD934X_IRQ_SOUNDWIRE, false},
+ {WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE, false},
+ {WCD934X_IRQ_RCO_ERROR, false},
+ {WCD934X_IRQ_CPE_ERROR, false},
+ {WCD934X_IRQ_MAD_AUDIO, false},
+ {WCD934X_IRQ_MAD_BEACON, false},
+ {WCD934X_IRQ_CPE1_INTR, true},
+ {WCD934X_IRQ_RESERVED_4, false},
+ {WCD934X_IRQ_MAD_ULTRASOUND, false},
+ {WCD934X_IRQ_VBAT_ATTACK, false},
+ {WCD934X_IRQ_VBAT_RESTORE, false},
+};
+
+struct tavil_cpr_reg_defaults {
+ int wr_data;
+ int wr_addr;
+};
+
+struct interp_sample_rate {
+ int sample_rate;
+ int rate_val;
+};
+
+static struct interp_sample_rate sr_val_tbl[] = {
+ {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5},
+ {192000, 0x6}, {384000, 0x7}, {44100, 0x9}, {88200, 0xA},
+ {176400, 0xB}, {352800, 0xC},
+};
+
+static const struct wcd9xxx_ch tavil_rx_chs[WCD934X_RX_MAX] = {
+ WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER, 0),
+ WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 1, 1),
+ WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 2, 2),
+ WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 3, 3),
+ WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 4, 4),
+ WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 5, 5),
+ WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 6, 6),
+ WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 7, 7),
+};
+
+static const struct wcd9xxx_ch tavil_tx_chs[WCD934X_TX_MAX] = {
+ WCD9XXX_CH(0, 0),
+ WCD9XXX_CH(1, 1),
+ WCD9XXX_CH(2, 2),
+ WCD9XXX_CH(3, 3),
+ WCD9XXX_CH(4, 4),
+ WCD9XXX_CH(5, 5),
+ WCD9XXX_CH(6, 6),
+ WCD9XXX_CH(7, 7),
+ WCD9XXX_CH(8, 8),
+ WCD9XXX_CH(9, 9),
+ WCD9XXX_CH(10, 10),
+ WCD9XXX_CH(11, 11),
+ WCD9XXX_CH(12, 12),
+ WCD9XXX_CH(13, 13),
+ WCD9XXX_CH(14, 14),
+ WCD9XXX_CH(15, 15),
+};
+
+static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = {
+ 0, /* AIF1_PB */
+ BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */
+ 0, /* AIF2_PB */
+ BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */
+ 0, /* AIF3_PB */
+ BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */
+ 0, /* AIF4_PB */
+};
+
+/* Codec supports 2 IIR filters */
+enum {
+ IIR0 = 0,
+ IIR1,
+ IIR_MAX,
+};
+
+/* Each IIR has 5 Filter Stages */
+enum {
+ BAND1 = 0,
+ BAND2,
+ BAND3,
+ BAND4,
+ BAND5,
+ BAND_MAX,
+};
+
+enum {
+ COMPANDER_1, /* HPH_L */
+ COMPANDER_2, /* HPH_R */
+ COMPANDER_3, /* LO1_DIFF */
+ COMPANDER_4, /* LO2_DIFF */
+ COMPANDER_5, /* LO3_SE - not used in Tavil */
+ COMPANDER_6, /* LO4_SE - not used in Tavil */
+ COMPANDER_7, /* SWR SPK CH1 */
+ COMPANDER_8, /* SWR SPK CH2 */
+ COMPANDER_MAX,
+};
+
+enum {
+ ASRC_IN_HPHL,
+ ASRC_IN_LO1,
+ ASRC_IN_HPHR,
+ ASRC_IN_LO2,
+ ASRC_IN_SPKR1,
+ ASRC_IN_SPKR2,
+ ASRC_INVALID,
+};
+
+enum {
+ ASRC0,
+ ASRC1,
+ ASRC2,
+ ASRC3,
+ ASRC_MAX,
+};
+
+enum {
+ CONV_88P2K_TO_384K,
+ CONV_96K_TO_352P8K,
+ CONV_352P8K_TO_384K,
+ CONV_384K_TO_352P8K,
+ CONV_384K_TO_384K,
+ CONV_96K_TO_384K,
+};
+
+static struct afe_param_slimbus_slave_port_cfg tavil_slimbus_slave_port_cfg = {
+ .minor_version = 1,
+ .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1,
+ .slave_dev_pgd_la = 0,
+ .slave_dev_intfdev_la = 0,
+ .bit_width = 16,
+ .data_format = 0,
+ .num_channels = 1
+};
+
+static struct afe_param_cdc_reg_page_cfg tavil_cdc_reg_page_cfg = {
+ .minor_version = AFE_API_VERSION_CDC_REG_PAGE_CFG,
+ .enable = 1,
+ .proc_id = AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1,
+};
+
+static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = {
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_MAIN_CTL_1),
+ HW_MAD_AUDIO_ENABLE, 0x1, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_AUDIO_CTL_3),
+ HW_MAD_AUDIO_SLEEP_TIME, 0xF, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_AUDIO_CTL_4),
+ HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_CFG),
+ MAD_AUDIO_INT_DEST_SELECT_REG, 0x2, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_MASK3),
+ MAD_AUDIO_INT_MASK_REG, 0x1, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_STATUS3),
+ MAD_AUDIO_INT_STATUS_REG, 0x1, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_CLEAR3),
+ MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_TX_BASE),
+ SB_PGD_PORT_TX_WATERMARK_N, 0x1E, WCD934X_REG_BITS, 0x1
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_TX_BASE),
+ SB_PGD_PORT_TX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE),
+ SB_PGD_PORT_RX_WATERMARK_N, 0x1E, WCD934X_REG_BITS, 0x1
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE),
+ SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ WCD934X_CDC_ANC0_IIR_ADAPT_CTL),
+ AANC_FF_GAIN_ADAPTIVE, 0x4, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ WCD934X_CDC_ANC0_IIR_ADAPT_CTL),
+ AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ WCD934X_CDC_ANC0_FF_A_GAIN_CTL),
+ AANC_GAIN_CONTROL, 0xFF, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ SB_PGD_TX_PORT_MULTI_CHANNEL_0(0)),
+ SB_PGD_TX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ SB_PGD_TX_PORT_MULTI_CHANNEL_1(0)),
+ SB_PGD_TX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x180, 0)),
+ SB_PGD_RX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x181, 0)),
+ SB_PGD_RX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4
+ },
+};
+
+static struct afe_param_cdc_reg_cfg_data tavil_audio_reg_cfg = {
+ .num_registers = ARRAY_SIZE(audio_reg_cfg),
+ .reg_data = audio_reg_cfg,
+};
+
+static struct afe_param_id_cdc_aanc_version tavil_cdc_aanc_version = {
+ .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION,
+ .aanc_hw_version = AANC_HW_BLOCK_VERSION_2,
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+#define WCD934X_TX_UNMUTE_DELAY_MS 40
+
+static int tx_unmute_delay = WCD934X_TX_UNMUTE_DELAY_MS;
+module_param(tx_unmute_delay, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path");
+
+static void tavil_codec_set_tx_hold(struct snd_soc_codec *, u16, bool);
+
+/* Hold instance to soundwire platform device */
+struct tavil_swr_ctrl_data {
+ struct platform_device *swr_pdev;
+};
+
+struct wcd_swr_ctrl_platform_data {
+ void *handle; /* holds codec private data */
+ int (*read)(void *handle, int reg);
+ int (*write)(void *handle, int reg, int val);
+ int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
+ int (*clk)(void *handle, bool enable);
+ int (*handle_irq)(void *handle,
+ irqreturn_t (*swrm_irq_handler)(int irq, void *data),
+ void *swrm_handle, int action);
+};
+
+/* Holds all Soundwire and speaker related information */
+struct wcd934x_swr {
+ struct tavil_swr_ctrl_data *ctrl_data;
+ struct wcd_swr_ctrl_platform_data plat_data;
+ struct mutex read_mutex;
+ struct mutex write_mutex;
+ struct mutex clk_mutex;
+ int spkr_gain_offset;
+ int spkr_mode;
+ int clk_users;
+ int rx_7_count;
+ int rx_8_count;
+};
+
+struct tx_mute_work {
+ struct tavil_priv *tavil;
+ u8 decimator;
+ struct delayed_work dwork;
+};
+
+#define WCD934X_SPK_ANC_EN_DELAY_MS 350
+static int spk_anc_en_delay = WCD934X_SPK_ANC_EN_DELAY_MS;
+module_param(spk_anc_en_delay, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path");
+
+struct spk_anc_work {
+ struct tavil_priv *tavil;
+ struct delayed_work dwork;
+};
+
+struct hpf_work {
+ struct tavil_priv *tavil;
+ u8 decimator;
+ u8 hpf_cut_off_freq;
+ struct delayed_work dwork;
+};
+
+struct tavil_priv {
+ struct device *dev;
+ struct wcd9xxx *wcd9xxx;
+ struct snd_soc_codec *codec;
+ u32 rx_bias_count;
+ s32 dmic_0_1_clk_cnt;
+ s32 dmic_2_3_clk_cnt;
+ s32 dmic_4_5_clk_cnt;
+ s32 micb_ref[TAVIL_MAX_MICBIAS];
+ s32 pullup_ref[TAVIL_MAX_MICBIAS];
+
+ /* ANC related */
+ u32 anc_slot;
+ bool anc_func;
+
+ /* compander */
+ int comp_enabled[COMPANDER_MAX];
+ int ear_spkr_gain;
+
+ /* class h specific data */
+ struct wcd_clsh_cdc_data clsh_d;
+ /* Tavil Interpolator Mode Select for EAR, HPH_L and HPH_R */
+ u32 hph_mode;
+
+ /* Mad switch reference count */
+ int mad_switch_cnt;
+
+ /* track tavil interface type */
+ u8 intf_type;
+
+ /* to track the status */
+ unsigned long status_mask;
+
+ struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg;
+
+ /* num of slim ports required */
+ struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
+ /* Port values for Rx and Tx codec_dai */
+ unsigned int rx_port_value[WCD934X_RX_MAX];
+ unsigned int tx_port_value;
+
+ struct wcd9xxx_resmgr_v2 *resmgr;
+ struct wcd934x_swr swr;
+ struct mutex micb_lock;
+
+ struct delayed_work power_gate_work;
+ struct mutex power_lock;
+
+ struct clk *wcd_ext_clk;
+
+ /* mbhc module */
+ struct wcd934x_mbhc *mbhc;
+
+ struct mutex codec_mutex;
+ struct work_struct tavil_add_child_devices_work;
+ struct hpf_work tx_hpf_work[WCD934X_NUM_DECIMATORS];
+ struct tx_mute_work tx_mute_dwork[WCD934X_NUM_DECIMATORS];
+ struct spk_anc_work spk_anc_dwork;
+
+ unsigned int vi_feed_value;
+
+ /* DSP control */
+ struct wcd_dsp_cntl *wdsp_cntl;
+
+ /* cal info for codec */
+ struct fw_info *fw_data;
+
+ /* Entry for version info */
+ struct snd_info_entry *entry;
+ struct snd_info_entry *version_entry;
+
+ /* SVS voting related */
+ struct mutex svs_mutex;
+ int svs_ref_cnt;
+
+ int native_clk_users;
+ /* ASRC users count */
+ int asrc_users[ASRC_MAX];
+ int asrc_output_mode[ASRC_MAX];
+ /* Main path clock users count */
+ int main_clk_users[WCD934X_NUM_INTERPOLATORS];
+ struct tavil_dsd_config *dsd_config;
+ struct tavil_idle_detect_config idle_det_cfg;
+
+ int power_active_ref;
+ int sidetone_coeff_array[IIR_MAX][BAND_MAX]
+ [WCD934X_CDC_SIDETONE_IIR_COEFF_MAX];
+};
+
+static const struct tavil_reg_mask_val tavil_spkr_default[] = {
+ {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80},
+ {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80},
+ {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01},
+ {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01},
+ {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50},
+ {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50},
+};
+
+static const struct tavil_reg_mask_val tavil_spkr_mode1[] = {
+ {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x00},
+ {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x00},
+ {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x00},
+ {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x00},
+ {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44},
+ {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44},
+};
+
+static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil);
+
+/*
+ * wcd934x_get_codec_info: Get codec specific information
+ *
+ * @wcd9xxx: pointer to wcd9xxx structure
+ * @wcd_type: pointer to wcd9xxx_codec_type structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd934x_get_codec_info(struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_codec_type *wcd_type)
+{
+ u16 id_minor, id_major;
+ struct regmap *wcd_regmap;
+ int rc, version = -1;
+
+ if (!wcd9xxx || !wcd_type)
+ return -EINVAL;
+
+ if (!wcd9xxx->regmap) {
+ dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null\n", __func__);
+ return -EINVAL;
+ }
+ wcd_regmap = wcd9xxx->regmap;
+
+ rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0,
+ (u8 *)&id_minor, sizeof(u16));
+ if (rc)
+ return -EINVAL;
+
+ rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2,
+ (u8 *)&id_major, sizeof(u16));
+ if (rc)
+ return -EINVAL;
+
+ dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n",
+ __func__, id_major, id_minor);
+
+ if (id_major != TAVIL_MAJOR)
+ goto version_unknown;
+
+ /*
+ * As fine version info cannot be retrieved before tavil probe.
+ * Assign coarse versions for possible future use before tavil probe.
+ */
+ if (id_minor == cpu_to_le16(0))
+ version = TAVIL_VERSION_1_0;
+ else if (id_minor == cpu_to_le16(0x01))
+ version = TAVIL_VERSION_1_1;
+
+version_unknown:
+ if (version < 0)
+ dev_err(wcd9xxx->dev, "%s: wcd934x version unknown\n",
+ __func__);
+
+ /* Fill codec type info */
+ wcd_type->id_major = id_major;
+ wcd_type->id_minor = id_minor;
+ wcd_type->num_irqs = WCD934X_NUM_IRQS;
+ wcd_type->version = version;
+ wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1;
+ wcd_type->i2c_chip_status = 0x01;
+ wcd_type->intr_tbl = wcd934x_intr_table;
+ wcd_type->intr_tbl_size = ARRAY_SIZE(wcd934x_intr_table);
+
+ wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] =
+ WCD934X_INTR_PIN1_STATUS0;
+ wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] =
+ WCD934X_INTR_PIN1_CLEAR0;
+ wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] =
+ WCD934X_INTR_PIN1_MASK0;
+ wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] =
+ WCD934X_INTR_LEVEL0;
+ wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] =
+ WCD934X_INTR_CLR_COMMIT;
+
+ return rc;
+}
+EXPORT_SYMBOL(wcd934x_get_codec_info);
+
+/*
+ * wcd934x_bringdown: Bringdown WCD Codec
+ *
+ * @wcd9xxx: Pointer to wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd934x_bringdown(struct wcd9xxx *wcd9xxx)
+{
+ if (!wcd9xxx || !wcd9xxx->regmap)
+ return -EINVAL;
+
+ regmap_write(wcd9xxx->regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x04);
+
+ return 0;
+}
+EXPORT_SYMBOL(wcd934x_bringdown);
+
+/*
+ * wcd934x_bringup: Bringup WCD Codec
+ *
+ * @wcd9xxx: Pointer to the wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd934x_bringup(struct wcd9xxx *wcd9xxx)
+{
+ struct regmap *wcd_regmap;
+
+ if (!wcd9xxx)
+ return -EINVAL;
+
+ if (!wcd9xxx->regmap) {
+ dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n",
+ __func__);
+ return -EINVAL;
+ }
+ wcd_regmap = wcd9xxx->regmap;
+
+ regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x01);
+ regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x19);
+ regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x15);
+ /* Add 1msec delay for VOUT to settle */
+ usleep_range(1000, 1100);
+ regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5);
+ regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7);
+ regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x3);
+ regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x7);
+ regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3);
+
+ return 0;
+}
+EXPORT_SYMBOL(wcd934x_bringup);
+
+/**
+ * tavil_set_spkr_gain_offset - offset the speaker path
+ * gain with the given offset value.
+ *
+ * @codec: codec instance
+ * @offset: Indicates speaker path gain offset value.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset)
+{
+ struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (!priv)
+ return -EINVAL;
+
+ priv->swr.spkr_gain_offset = offset;
+ return 0;
+}
+EXPORT_SYMBOL(tavil_set_spkr_gain_offset);
+
+/**
+ * tavil_set_spkr_mode - Configures speaker compander and smartboost
+ * settings based on speaker mode.
+ *
+ * @codec: codec instance
+ * @mode: Indicates speaker configuration mode.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode)
+{
+ struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int i;
+ const struct tavil_reg_mask_val *regs;
+ int size;
+
+ if (!priv)
+ return -EINVAL;
+
+ switch (mode) {
+ case WCD934X_SPKR_MODE_1:
+ regs = tavil_spkr_mode1;
+ size = ARRAY_SIZE(tavil_spkr_mode1);
+ break;
+ default:
+ regs = tavil_spkr_default;
+ size = ARRAY_SIZE(tavil_spkr_default);
+ break;
+ }
+
+ priv->swr.spkr_mode = mode;
+ for (i = 0; i < size; i++)
+ snd_soc_update_bits(codec, regs[i].reg,
+ regs[i].mask, regs[i].val);
+ return 0;
+}
+EXPORT_SYMBOL(tavil_set_spkr_mode);
+
+/**
+ * tavil_get_afe_config - returns specific codec configuration to afe to write
+ *
+ * @codec: codec instance
+ * @config_type: Indicates type of configuration to write.
+ */
+void *tavil_get_afe_config(struct snd_soc_codec *codec,
+ enum afe_config_type config_type)
+{
+ struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (config_type) {
+ case AFE_SLIMBUS_SLAVE_CONFIG:
+ return &priv->slimbus_slave_cfg;
+ case AFE_CDC_REGISTERS_CONFIG:
+ return &tavil_audio_reg_cfg;
+ case AFE_SLIMBUS_SLAVE_PORT_CONFIG:
+ return &tavil_slimbus_slave_port_cfg;
+ case AFE_AANC_VERSION:
+ return &tavil_cdc_aanc_version;
+ case AFE_CDC_REGISTER_PAGE_CONFIG:
+ return &tavil_cdc_reg_page_cfg;
+ default:
+ dev_info(codec->dev, "%s: Unknown config_type 0x%x\n",
+ __func__, config_type);
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(tavil_get_afe_config);
+
+static bool is_tavil_playback_dai(int dai_id)
+{
+ if ((dai_id == AIF1_PB) || (dai_id == AIF2_PB) ||
+ (dai_id == AIF3_PB) || (dai_id == AIF4_PB))
+ return true;
+
+ return false;
+}
+
+static int tavil_find_playback_dai_id_for_port(int port_id,
+ struct tavil_priv *tavil)
+{
+ struct wcd9xxx_codec_dai_data *dai;
+ struct wcd9xxx_ch *ch;
+ int i, slv_port_id;
+
+ for (i = AIF1_PB; i < NUM_CODEC_DAIS; i++) {
+ if (!is_tavil_playback_dai(i))
+ continue;
+
+ dai = &tavil->dai[i];
+ list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+ slv_port_id = wcd9xxx_get_slave_port(ch->ch_num);
+ if ((slv_port_id > 0) && (slv_port_id == port_id))
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static void tavil_vote_svs(struct tavil_priv *tavil, bool vote)
+{
+ struct wcd9xxx *wcd9xxx;
+
+ wcd9xxx = tavil->wcd9xxx;
+
+ mutex_lock(&tavil->svs_mutex);
+ if (vote) {
+ tavil->svs_ref_cnt++;
+ if (tavil->svs_ref_cnt == 1)
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0,
+ 0x01, 0x01);
+ } else {
+ /* Do not decrement ref count if it is already 0 */
+ if (tavil->svs_ref_cnt == 0)
+ goto done;
+
+ tavil->svs_ref_cnt--;
+ if (tavil->svs_ref_cnt == 0)
+ regmap_update_bits(wcd9xxx->regmap,
+ WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0,
+ 0x01, 0x00);
+ }
+done:
+ dev_dbg(tavil->dev, "%s: vote = %s, updated ref cnt = %u\n", __func__,
+ vote ? "vote" : "Unvote", tavil->svs_ref_cnt);
+ mutex_unlock(&tavil->svs_mutex);
+}
+
+static int tavil_get_anc_slot(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tavil->anc_slot;
+ return 0;
+}
+
+static int tavil_put_anc_slot(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ tavil->anc_slot = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int tavil_get_anc_func(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = (tavil->anc_func == true ? 1 : 0);
+ return 0;
+}
+
+static int tavil_put_anc_func(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ mutex_lock(&tavil->codec_mutex);
+ tavil->anc_func = (!ucontrol->value.integer.value[0] ? false : true);
+ dev_dbg(codec->dev, "%s: anc_func %x", __func__, tavil->anc_func);
+
+ if (tavil->anc_func == true) {
+ snd_soc_dapm_enable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_enable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA");
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHL PA");
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHR PA");
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_disable_pin(dapm, "EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "EAR");
+ snd_soc_dapm_disable_pin(dapm, "HPHL PA");
+ snd_soc_dapm_disable_pin(dapm, "HPHR PA");
+ snd_soc_dapm_disable_pin(dapm, "HPHL");
+ snd_soc_dapm_disable_pin(dapm, "HPHR");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_enable_pin(dapm, "EAR PA");
+ snd_soc_dapm_enable_pin(dapm, "EAR");
+ snd_soc_dapm_enable_pin(dapm, "HPHL");
+ snd_soc_dapm_enable_pin(dapm, "HPHR");
+ snd_soc_dapm_enable_pin(dapm, "HPHL PA");
+ snd_soc_dapm_enable_pin(dapm, "HPHR PA");
+ }
+ mutex_unlock(&tavil->codec_mutex);
+
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static int tavil_codec_enable_anc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ const char *filename;
+ const struct firmware *fw;
+ int i;
+ int ret = 0;
+ int num_anc_slots;
+ struct wcd9xxx_anc_header *anc_head;
+ struct firmware_cal *hwdep_cal = NULL;
+ u32 anc_writes_size = 0;
+ u32 anc_cal_size = 0;
+ int anc_size_remaining;
+ u32 *anc_ptr;
+ u16 reg;
+ u8 mask, val;
+ size_t cal_size;
+ const void *data;
+
+ if (!tavil->anc_func)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_ANC_CAL);
+ if (hwdep_cal) {
+ data = hwdep_cal->data;
+ cal_size = hwdep_cal->size;
+ dev_dbg(codec->dev, "%s: using hwdep calibration, cal_size %zd",
+ __func__, cal_size);
+ } else {
+ filename = "WCD934X/WCD934X_anc.bin";
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev, "%s: Failed to acquire ANC data: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ if (!fw) {
+ dev_err(codec->dev, "%s: Failed to get anc fw\n",
+ __func__);
+ return -ENODEV;
+ }
+ data = fw->data;
+ cal_size = fw->size;
+ dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+ __func__);
+ }
+ if (cal_size < sizeof(struct wcd9xxx_anc_header)) {
+ dev_err(codec->dev, "%s: Invalid cal_size %zd\n",
+ __func__, cal_size);
+ ret = -EINVAL;
+ goto err;
+ }
+ /* First number is the number of register writes */
+ anc_head = (struct wcd9xxx_anc_header *)(data);
+ anc_ptr = (u32 *)(data + sizeof(struct wcd9xxx_anc_header));
+ anc_size_remaining = cal_size -
+ sizeof(struct wcd9xxx_anc_header);
+ num_anc_slots = anc_head->num_anc_slots;
+
+ if (tavil->anc_slot >= num_anc_slots) {
+ dev_err(codec->dev, "%s: Invalid ANC slot selected\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ for (i = 0; i < num_anc_slots; i++) {
+ if (anc_size_remaining < WCD934X_PACKED_REG_SIZE) {
+ dev_err(codec->dev, "%s: Invalid register format\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ anc_writes_size = (u32)(*anc_ptr);
+ anc_size_remaining -= sizeof(u32);
+ anc_ptr += 1;
+
+ if ((anc_writes_size * WCD934X_PACKED_REG_SIZE) >
+ anc_size_remaining) {
+ dev_err(codec->dev, "%s: Invalid register format\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (tavil->anc_slot == i)
+ break;
+
+ anc_size_remaining -= (anc_writes_size *
+ WCD934X_PACKED_REG_SIZE);
+ anc_ptr += anc_writes_size;
+ }
+ if (i == num_anc_slots) {
+ dev_err(codec->dev, "%s: Selected ANC slot not present\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ anc_cal_size = anc_writes_size;
+ for (i = 0; i < anc_writes_size; i++) {
+ WCD934X_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val);
+ snd_soc_write(codec, reg, (val & mask));
+ }
+
+ /* Rate converter clk enable and set bypass mode */
+ if (!strcmp(w->name, "RX INT0 DAC") ||
+ !strcmp(w->name, "RX INT1 DAC") ||
+ !strcmp(w->name, "ANC SPK1 PA")) {
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC0_RC_COMMON_CTL,
+ 0x05, 0x05);
+ if (!strcmp(w->name, "RX INT1 DAC")) {
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC0_FIFO_COMMON_CTL,
+ 0x66, 0x66);
+ }
+ } else if (!strcmp(w->name, "RX INT2 DAC")) {
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC1_RC_COMMON_CTL,
+ 0x05, 0x05);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC1_FIFO_COMMON_CTL,
+ 0x66, 0x66);
+ }
+ if (!strcmp(w->name, "RX INT1 DAC"))
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC0_CLK_RESET_CTL, 0x08, 0x08);
+ else if (!strcmp(w->name, "RX INT2 DAC"))
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC1_CLK_RESET_CTL, 0x08, 0x08);
+
+ if (!hwdep_cal)
+ release_firmware(fw);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ if (!strcmp(w->name, "ANC HPHL PA") ||
+ !strcmp(w->name, "ANC HPHR PA")) {
+ /* Remove ANC Rx from reset */
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC0_CLK_RESET_CTL,
+ 0x08, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC1_CLK_RESET_CTL,
+ 0x08, 0x00);
+ }
+
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC0_RC_COMMON_CTL,
+ 0x05, 0x00);
+ if (!strcmp(w->name, "ANC EAR PA") ||
+ !strcmp(w->name, "ANC SPK1 PA") ||
+ !strcmp(w->name, "ANC HPHL PA")) {
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL,
+ 0x30, 0x00);
+ msleep(50);
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC0_CLK_RESET_CTL,
+ 0x38, 0x38);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC0_CLK_RESET_CTL,
+ 0x07, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC0_CLK_RESET_CTL,
+ 0x38, 0x00);
+ } else if (!strcmp(w->name, "ANC HPHR PA")) {
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_1_CTL,
+ 0x30, 0x00);
+ msleep(50);
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_1_CTL,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC1_CLK_RESET_CTL,
+ 0x38, 0x38);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC1_CLK_RESET_CTL,
+ 0x07, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC1_CLK_RESET_CTL,
+ 0x38, 0x00);
+ }
+ break;
+ }
+
+ return 0;
+err:
+ if (!hwdep_cal)
+ release_firmware(fw);
+ return ret;
+}
+
+static int tavil_vi_feed_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tavil_p->vi_feed_value;
+
+ return 0;
+}
+
+static int tavil_vi_feed_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n",
+ __func__, enable, port_id, dai_id);
+
+ tavil_p->vi_feed_value = ucontrol->value.integer.value[0];
+
+ mutex_lock(&tavil_p->codec_mutex);
+ if (enable) {
+ if (port_id == WCD934X_TX14 && !test_bit(VI_SENSE_1,
+ &tavil_p->status_mask)) {
+ list_add_tail(&core->tx_chs[WCD934X_TX14].list,
+ &tavil_p->dai[dai_id].wcd9xxx_ch_list);
+ set_bit(VI_SENSE_1, &tavil_p->status_mask);
+ }
+ if (port_id == WCD934X_TX15 && !test_bit(VI_SENSE_2,
+ &tavil_p->status_mask)) {
+ list_add_tail(&core->tx_chs[WCD934X_TX15].list,
+ &tavil_p->dai[dai_id].wcd9xxx_ch_list);
+ set_bit(VI_SENSE_2, &tavil_p->status_mask);
+ }
+ } else {
+ if (port_id == WCD934X_TX14 && test_bit(VI_SENSE_1,
+ &tavil_p->status_mask)) {
+ list_del_init(&core->tx_chs[WCD934X_TX14].list);
+ clear_bit(VI_SENSE_1, &tavil_p->status_mask);
+ }
+ if (port_id == WCD934X_TX15 && test_bit(VI_SENSE_2,
+ &tavil_p->status_mask)) {
+ list_del_init(&core->tx_chs[WCD934X_TX15].list);
+ clear_bit(VI_SENSE_2, &tavil_p->status_mask);
+ }
+ }
+ mutex_unlock(&tavil_p->codec_mutex);
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL);
+
+ return 0;
+}
+
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tavil_p->tx_port_value;
+ return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct snd_soc_dapm_update *update = NULL;
+ struct soc_multi_mixer_control *mixer =
+ ((struct soc_multi_mixer_control *)kcontrol->private_value);
+ u32 dai_id = widget->shift;
+ u32 port_id = mixer->shift;
+ u32 enable = ucontrol->value.integer.value[0];
+ u32 vtable;
+
+ dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n",
+ __func__,
+ widget->name, ucontrol->id.name, tavil_p->tx_port_value,
+ widget->shift, ucontrol->value.integer.value[0]);
+
+ mutex_lock(&tavil_p->codec_mutex);
+ if (dai_id >= ARRAY_SIZE(vport_slim_check_table)) {
+ dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n",
+ __func__, dai_id);
+ mutex_unlock(&tavil_p->codec_mutex);
+ return -EINVAL;
+ }
+ vtable = vport_slim_check_table[dai_id];
+
+ switch (dai_id) {
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ /* only add to the list if value not set */
+ if (enable && !(tavil_p->tx_port_value & 1 << port_id)) {
+ if (wcd9xxx_tx_vport_validation(vtable, port_id,
+ tavil_p->dai, NUM_CODEC_DAIS)) {
+ dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
+ __func__, port_id);
+ mutex_unlock(&tavil_p->codec_mutex);
+ return 0;
+ }
+ tavil_p->tx_port_value |= 1 << port_id;
+ list_add_tail(&core->tx_chs[port_id].list,
+ &tavil_p->dai[dai_id].wcd9xxx_ch_list);
+ } else if (!enable && (tavil_p->tx_port_value &
+ 1 << port_id)) {
+ tavil_p->tx_port_value &= ~(1 << port_id);
+ list_del_init(&core->tx_chs[port_id].list);
+ } else {
+ if (enable)
+ dev_dbg(codec->dev, "%s: TX%u port is used by\n"
+ "this virtual port\n",
+ __func__, port_id);
+ else
+ dev_dbg(codec->dev, "%s: TX%u port is not used by\n"
+ "this virtual port\n",
+ __func__, port_id);
+ /* avoid update power function */
+ mutex_unlock(&tavil_p->codec_mutex);
+ return 0;
+ }
+ break;
+ case AIF4_MAD_TX:
+ break;
+ default:
+ dev_err(codec->dev, "Unknown AIF %d\n", dai_id);
+ mutex_unlock(&tavil_p->codec_mutex);
+ return -EINVAL;
+ }
+ dev_dbg(codec->dev, "%s: name %s sname %s updated value %u shift %d\n",
+ __func__, widget->name, widget->sname, tavil_p->tx_port_value,
+ widget->shift);
+
+ mutex_unlock(&tavil_p->codec_mutex);
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
+
+ return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] =
+ tavil_p->rx_port_value[widget->shift];
+ return 0;
+}
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+ unsigned int rx_port_value;
+ u32 port_id = widget->shift;
+
+ tavil_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0];
+ rx_port_value = tavil_p->rx_port_value[port_id];
+
+ mutex_lock(&tavil_p->codec_mutex);
+ dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n",
+ __func__, widget->name, ucontrol->id.name,
+ rx_port_value, widget->shift,
+ ucontrol->value.integer.value[0]);
+
+ /* value need to match the Virtual port and AIF number */
+ switch (rx_port_value) {
+ case 0:
+ list_del_init(&core->rx_chs[port_id].list);
+ break;
+ case 1:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ WCD934X_RX_PORT_START_NUMBER,
+ &tavil_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tavil_p->dai[AIF1_PB].wcd9xxx_ch_list);
+ break;
+ case 2:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ WCD934X_RX_PORT_START_NUMBER,
+ &tavil_p->dai[AIF2_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tavil_p->dai[AIF2_PB].wcd9xxx_ch_list);
+ break;
+ case 3:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ WCD934X_RX_PORT_START_NUMBER,
+ &tavil_p->dai[AIF3_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tavil_p->dai[AIF3_PB].wcd9xxx_ch_list);
+ break;
+ case 4:
+ if (wcd9xxx_rx_vport_validation(port_id +
+ WCD934X_RX_PORT_START_NUMBER,
+ &tavil_p->dai[AIF4_PB].wcd9xxx_ch_list)) {
+ dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id);
+ goto rtn;
+ }
+ list_add_tail(&core->rx_chs[port_id].list,
+ &tavil_p->dai[AIF4_PB].wcd9xxx_ch_list);
+ break;
+ default:
+ dev_err(codec->dev, "Unknown AIF %d\n", rx_port_value);
+ goto err;
+ }
+rtn:
+ mutex_unlock(&tavil_p->codec_mutex);
+ snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+ rx_port_value, e, update);
+
+ return 0;
+err:
+ mutex_unlock(&tavil_p->codec_mutex);
+ return -EINVAL;
+}
+
+static void tavil_codec_enable_slim_port_intr(
+ struct wcd9xxx_codec_dai_data *dai,
+ struct snd_soc_codec *codec)
+{
+ struct wcd9xxx_ch *ch;
+ int port_num = 0;
+ unsigned short reg = 0;
+ u8 val = 0;
+ struct tavil_priv *tavil_p;
+
+ if (!dai || !codec) {
+ pr_err("%s: Invalid params\n", __func__);
+ return;
+ }
+
+ tavil_p = snd_soc_codec_get_drvdata(codec);
+ list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+ if (ch->port >= WCD934X_RX_PORT_START_NUMBER) {
+ port_num = ch->port - WCD934X_RX_PORT_START_NUMBER;
+ reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + (port_num / 8);
+ val = wcd9xxx_interface_reg_read(tavil_p->wcd9xxx,
+ reg);
+ if (!(val & BYTE_BIT_MASK(port_num))) {
+ val |= BYTE_BIT_MASK(port_num);
+ wcd9xxx_interface_reg_write(
+ tavil_p->wcd9xxx, reg, val);
+ val = wcd9xxx_interface_reg_read(
+ tavil_p->wcd9xxx, reg);
+ }
+ } else {
+ port_num = ch->port;
+ reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8);
+ val = wcd9xxx_interface_reg_read(tavil_p->wcd9xxx,
+ reg);
+ if (!(val & BYTE_BIT_MASK(port_num))) {
+ val |= BYTE_BIT_MASK(port_num);
+ wcd9xxx_interface_reg_write(tavil_p->wcd9xxx,
+ reg, val);
+ val = wcd9xxx_interface_reg_read(
+ tavil_p->wcd9xxx, reg);
+ }
+ }
+ }
+}
+
+static int tavil_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai,
+ bool up)
+{
+ int ret = 0;
+ struct wcd9xxx_ch *ch;
+
+ if (up) {
+ list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+ ret = wcd9xxx_get_slave_port(ch->ch_num);
+ if (ret < 0) {
+ pr_err("%s: Invalid slave port ID: %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ } else {
+ set_bit(ret, &dai->ch_mask);
+ }
+ }
+ } else {
+ ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0),
+ msecs_to_jiffies(
+ WCD934X_SLIM_CLOSE_TIMEOUT));
+ if (!ret) {
+ pr_err("%s: Slim close tx/rx wait timeout, ch_mask:0x%lx\n",
+ __func__, dai->ch_mask);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static void tavil_codec_mute_dsd(struct snd_soc_codec *codec,
+ struct list_head *ch_list)
+{
+ u8 dsd0_in;
+ u8 dsd1_in;
+ struct wcd9xxx_ch *ch;
+
+ /* Read DSD Input Ports */
+ dsd0_in = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x3C) >> 2;
+ dsd1_in = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x3C) >> 2;
+
+ if ((dsd0_in == 0) && (dsd1_in == 0))
+ return;
+
+ /*
+ * Check if the ports getting disabled are connected to DSD inputs.
+ * If connected, enable DSD mute to avoid DC entering into DSD Filter
+ */
+ list_for_each_entry(ch, ch_list, list) {
+ if (ch->port == (dsd0_in + WCD934X_RX_PORT_START_NUMBER - 1))
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x04);
+ if (ch->port == (dsd1_in + WCD934X_RX_PORT_START_NUMBER - 1))
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+ 0x04, 0x04);
+ }
+}
+
+static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct wcd9xxx *core;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ struct wcd9xxx_codec_dai_data *dai;
+ struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config;
+
+ core = dev_get_drvdata(codec->dev->parent);
+
+ dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d\n"
+ "stream name %s event %d\n",
+ __func__, codec->component.name,
+ codec->component.num_dai, w->sname, event);
+
+ dai = &tavil_p->dai[w->shift];
+ dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n",
+ __func__, w->name, w->shift, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dai->bus_down_in_recovery = false;
+ tavil_codec_enable_slim_port_intr(dai, codec);
+ (void) tavil_codec_enable_slim_chmask(dai, true);
+ ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (dsd_conf)
+ tavil_codec_mute_dsd(codec, &dai->wcd9xxx_ch_list);
+
+ ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n",
+ __func__, ret);
+
+ if (!dai->bus_down_in_recovery)
+ ret = tavil_codec_enable_slim_chmask(dai, false);
+ else
+ dev_dbg(codec->dev,
+ "%s: bus in recovery skip enable slim_chmask",
+ __func__);
+ ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ break;
+ }
+ return ret;
+}
+
+static int tavil_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_codec_dai_data *dai;
+ struct wcd9xxx *core;
+ int ret = 0;
+
+ dev_dbg(codec->dev,
+ "%s: w->name %s, w->shift = %d, num_dai %d stream name %s\n",
+ __func__, w->name, w->shift,
+ codec->component.num_dai, w->sname);
+
+ dai = &tavil_p->dai[w->shift];
+ core = dev_get_drvdata(codec->dev->parent);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dai->bus_down_in_recovery = false;
+ tavil_codec_enable_slim_port_intr(dai, codec);
+ (void) tavil_codec_enable_slim_chmask(dai, true);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ if (!dai->bus_down_in_recovery)
+ ret = tavil_codec_enable_slim_chmask(dai, false);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n",
+ __func__, ret);
+ }
+ break;
+ }
+ return ret;
+}
+
+static int tavil_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct wcd9xxx *core = NULL;
+ struct snd_soc_codec *codec = NULL;
+ struct tavil_priv *tavil_p = NULL;
+ int ret = 0;
+ struct wcd9xxx_codec_dai_data *dai = NULL;
+
+ codec = snd_soc_dapm_to_codec(w->dapm);
+ tavil_p = snd_soc_codec_get_drvdata(codec);
+ core = dev_get_drvdata(codec->dev->parent);
+
+ dev_dbg(codec->dev,
+ "%s: num_dai %d stream name %s w->name %s event %d shift %d\n",
+ __func__, codec->component.num_dai, w->sname,
+ w->name, event, w->shift);
+
+ if (w->shift != AIF4_VIFEED) {
+ pr_err("%s Error in enabling the tx path\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ dai = &tavil_p->dai[w->shift];
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) {
+ dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__);
+ /* Enable V&I sensing */
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F,
+ 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+ 0x00);
+ }
+ if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) {
+ pr_debug("%s: spkr2 enabled\n", __func__);
+ /* Enable V&I sensing */
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F,
+ 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F,
+ 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+ 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+ 0x00);
+ }
+ dai->bus_down_in_recovery = false;
+ tavil_codec_enable_slim_port_intr(dai, codec);
+ (void) tavil_codec_enable_slim_chmask(dai, true);
+ ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->rate, dai->bit_width,
+ &dai->grph);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+ dai->grph);
+ if (ret)
+ dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n",
+ __func__, ret);
+ if (!dai->bus_down_in_recovery)
+ ret = tavil_codec_enable_slim_chmask(dai, false);
+ if (ret < 0) {
+ ret = wcd9xxx_disconnect_port(core,
+ &dai->wcd9xxx_ch_list,
+ dai->grph);
+ dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n",
+ __func__, ret);
+ }
+ if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) {
+ /* Disable V&I sensing */
+ dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10,
+ 0x00);
+ }
+ if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) {
+ /* Disable V&I sensing */
+ dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+ 0x20);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10,
+ 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10,
+ 0x00);
+ }
+ break;
+ }
+done:
+ return ret;
+}
+
+static int tavil_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tavil->rx_bias_count++;
+ if (tavil->rx_bias_count == 1) {
+ snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+ 0x01, 0x01);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tavil->rx_bias_count--;
+ if (!tavil->rx_bias_count)
+ snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+ 0x01, 0x00);
+ break;
+ };
+ dev_dbg(codec->dev, "%s: Current RX BIAS user count: %d\n", __func__,
+ tavil->rx_bias_count);
+
+ return 0;
+}
+
+static void tavil_spk_anc_update_callback(struct work_struct *work)
+{
+ struct spk_anc_work *spk_anc_dwork;
+ struct tavil_priv *tavil;
+ struct delayed_work *delayed_work;
+ struct snd_soc_codec *codec;
+
+ delayed_work = to_delayed_work(work);
+ spk_anc_dwork = container_of(delayed_work, struct spk_anc_work, dwork);
+ tavil = spk_anc_dwork->tavil;
+ codec = tavil->codec;
+
+ snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0, 0x10, 0x10);
+}
+
+static int tavil_codec_enable_spkr_anc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ if (!tavil->anc_func)
+ return 0;
+
+ dev_dbg(codec->dev, "%s: w: %s event: %d anc: %d\n", __func__,
+ w->name, event, tavil->anc_func);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ schedule_delayed_work(&tavil->spk_anc_dwork.dwork,
+ msecs_to_jiffies(spk_anc_en_delay));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cancel_delayed_work_sync(&tavil->spk_anc_dwork.dwork);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0,
+ 0x10, 0x00);
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ break;
+ }
+ return ret;
+}
+
+static int tavil_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * 5ms sleep is required after PA is enabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CTL,
+ 0x10, 0x00);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec, WCD934X_CDC_RX0_RX_PATH_MIX_CTL)) &
+ 0x10)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX0_RX_PATH_MIX_CTL,
+ 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * 5ms sleep is required after PA is disabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+
+ if (!(strcmp(w->name, "ANC EAR PA"))) {
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0,
+ 0x10, 0x00);
+ }
+ break;
+ };
+
+ return ret;
+}
+
+static void tavil_codec_override(struct snd_soc_codec *codec, int mode,
+ int event)
+{
+ if (mode == CLS_AB || mode == CLS_AB_HIFI) {
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00);
+ break;
+ }
+ }
+}
+
+static void tavil_codec_clear_anc_tx_hold(struct tavil_priv *tavil)
+{
+ if (test_and_clear_bit(ANC_MIC_AMIC1, &tavil->status_mask))
+ tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC1, false);
+ if (test_and_clear_bit(ANC_MIC_AMIC2, &tavil->status_mask))
+ tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC2, false);
+ if (test_and_clear_bit(ANC_MIC_AMIC3, &tavil->status_mask))
+ tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC3, false);
+ if (test_and_clear_bit(ANC_MIC_AMIC4, &tavil->status_mask))
+ tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC4, false);
+}
+
+static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, (0x03 << 1));
+
+ if ((!(strcmp(w->name, "ANC HPHR PA"))) &&
+ (test_bit(HPH_PA_DELAY, &tavil->status_mask)))
+ snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0xC0, 0xC0);
+
+ set_bit(HPH_PA_DELAY, &tavil->status_mask);
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) {
+ /* Set regulator mode to AB if DSD is enabled */
+ snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
+ }
+ if (!(strcmp(w->name, "HPHR PA")))
+ snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0x40, 0x40);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if ((!(strcmp(w->name, "ANC HPHR PA")))) {
+ if ((snd_soc_read(codec, WCD934X_ANA_HPH) & 0xC0)
+ != 0xC0)
+ /*
+ * If PA_EN is not set (potentially in ANC case)
+ * then do nothing for POST_PMU and let left
+ * channel handle everything.
+ */
+ break;
+ }
+ /*
+ * 7ms sleep is required after PA is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is needed.
+ */
+ if (test_bit(HPH_PA_DELAY, &tavil->status_mask)) {
+ if (!tavil->comp_enabled[COMPANDER_2])
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+ clear_bit(HPH_PA_DELAY, &tavil->status_mask);
+ }
+ if (tavil->anc_func) {
+ /* Clear Tx FE HOLD if both PAs are enabled */
+ if ((snd_soc_read(tavil->codec, WCD934X_ANA_HPH) &
+ 0xC0) == 0xC0)
+ tavil_codec_clear_anc_tx_hold(tavil);
+ }
+
+ snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x01);
+
+ /* Remove mute */
+ snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL,
+ 0x10, 0x00);
+ /* Enable GM3 boost */
+ snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL,
+ 0x80, 0x80);
+ /* Enable AutoChop timer at the end of power up */
+ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ 0x02, 0x02);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec, WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) &
+ 0x10)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX2_RX_PATH_MIX_CTL,
+ 0x10, 0x00);
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+ 0x04, 0x00);
+ if (!(strcmp(w->name, "ANC HPHR PA"))) {
+ pr_debug("%s:Do everything needed for left channel\n",
+ __func__);
+ /* Do everything needed for left channel */
+ snd_soc_update_bits(codec, WCD934X_HPH_L_TEST,
+ 0x01, 0x01);
+
+ /* Remove mute */
+ snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL,
+ 0x10, 0x00);
+
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec,
+ WCD934X_CDC_RX1_RX_PATH_MIX_CTL)) &
+ 0x10)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX1_RX_PATH_MIX_CTL,
+ 0x10, 0x00);
+
+ if (dsd_conf && (snd_soc_read(codec,
+ WCD934X_CDC_DSD0_PATH_CTL) &
+ 0x01))
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x00);
+ /* Remove ANC Rx from reset */
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ }
+ tavil_codec_override(codec, tavil->hph_mode, event);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ WCD_EVENT_PRE_HPHR_PA_OFF,
+ &tavil->mbhc->wcd_mbhc);
+ /* Enable DSD Mute before PA disable */
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL,
+ 0x10, 0x10);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_MIX_CTL,
+ 0x10, 0x10);
+ if (!(strcmp(w->name, "ANC HPHR PA")))
+ snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0x40, 0x00);
+ if (!(strcmp(w->name, "HPHR PA")))
+ snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0x40, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * 5ms sleep is required after PA disable. If compander is
+ * disabled, then 20ms delay is needed after PA disable.
+ */
+ if (!tavil->comp_enabled[COMPANDER_2])
+ usleep_range(20000, 20100);
+ else
+ usleep_range(5000, 5100);
+ tavil_codec_override(codec, tavil->hph_mode, event);
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ WCD_EVENT_POST_HPHR_PA_OFF,
+ &tavil->mbhc->wcd_mbhc);
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, 0x0);
+ if (!(strcmp(w->name, "ANC HPHR PA"))) {
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX2_RX_PATH_CFG0,
+ 0x10, 0x00);
+ }
+ break;
+ };
+
+ return ret;
+}
+
+static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, (0x03 << 1));
+ if ((!(strcmp(w->name, "ANC HPHL PA"))) &&
+ (test_bit(HPH_PA_DELAY, &tavil->status_mask)))
+ snd_soc_update_bits(codec, WCD934X_ANA_HPH,
+ 0xC0, 0xC0);
+ if (!(strcmp(w->name, "HPHL PA")))
+ snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0x80, 0x80);
+ set_bit(HPH_PA_DELAY, &tavil->status_mask);
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) {
+ /* Set regulator mode to AB if DSD is enabled */
+ snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (!(strcmp(w->name, "ANC HPHL PA"))) {
+ if ((snd_soc_read(codec, WCD934X_ANA_HPH) & 0xC0)
+ != 0xC0)
+ /*
+ * If PA_EN is not set (potentially in ANC
+ * case) then do nothing for POST_PMU and
+ * let right channel handle everything.
+ */
+ break;
+ }
+ /*
+ * 7ms sleep is required after PA is enabled as per
+ * HW requirement. If compander is disabled, then
+ * 20ms delay is needed.
+ */
+ if (test_bit(HPH_PA_DELAY, &tavil->status_mask)) {
+ if (!tavil->comp_enabled[COMPANDER_1])
+ usleep_range(20000, 20100);
+ else
+ usleep_range(7000, 7100);
+ clear_bit(HPH_PA_DELAY, &tavil->status_mask);
+ }
+ if (tavil->anc_func) {
+ /* Clear Tx FE HOLD if both PAs are enabled */
+ if ((snd_soc_read(tavil->codec, WCD934X_ANA_HPH) &
+ 0xC0) == 0xC0)
+ tavil_codec_clear_anc_tx_hold(tavil);
+ }
+
+ snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x01);
+ /* Remove Mute on primary path */
+ snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL,
+ 0x10, 0x00);
+ /* Enable GM3 boost */
+ snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL,
+ 0x80, 0x80);
+ /* Enable AutoChop timer at the end of power up */
+ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ 0x02, 0x02);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec, WCD934X_CDC_RX1_RX_PATH_MIX_CTL)) &
+ 0x10)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX1_RX_PATH_MIX_CTL,
+ 0x10, 0x00);
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x00);
+ if (!(strcmp(w->name, "ANC HPHL PA"))) {
+ pr_debug("%s:Do everything needed for right channel\n",
+ __func__);
+
+ /* Do everything needed for right channel */
+ snd_soc_update_bits(codec, WCD934X_HPH_R_TEST,
+ 0x01, 0x01);
+
+ /* Remove mute */
+ snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL,
+ 0x10, 0x00);
+
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec,
+ WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) &
+ 0x10)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX2_RX_PATH_MIX_CTL,
+ 0x10, 0x00);
+ if (dsd_conf && (snd_soc_read(codec,
+ WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_DSD1_CFG2,
+ 0x04, 0x00);
+ /* Remove ANC Rx from reset */
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ }
+ tavil_codec_override(codec, tavil->hph_mode, event);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ WCD_EVENT_PRE_HPHL_PA_OFF,
+ &tavil->mbhc->wcd_mbhc);
+ /* Enable DSD Mute before PA disable */
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x04);
+
+ snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL,
+ 0x10, 0x10);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_MIX_CTL,
+ 0x10, 0x10);
+ if (!(strcmp(w->name, "ANC HPHL PA")))
+ snd_soc_update_bits(codec, WCD934X_ANA_HPH,
+ 0x80, 0x00);
+ if (!(strcmp(w->name, "HPHL PA")))
+ snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0x80, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * 5ms sleep is required after PA disable. If compander is
+ * disabled, then 20ms delay is needed after PA disable.
+ */
+ if (!tavil->comp_enabled[COMPANDER_1])
+ usleep_range(20000, 20100);
+ else
+ usleep_range(5000, 5100);
+ tavil_codec_override(codec, tavil->hph_mode, event);
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ WCD_EVENT_POST_HPHL_PA_OFF,
+ &tavil->mbhc->wcd_mbhc);
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, 0x0);
+ if (!(strcmp(w->name, "ANC HPHL PA"))) {
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX1_RX_PATH_CFG0, 0x10, 0x00);
+ }
+ break;
+ };
+
+ return ret;
+}
+
+static int tavil_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 lineout_vol_reg = 0, lineout_mix_vol_reg = 0;
+ u16 dsd_mute_reg = 0, dsd_clk_reg = 0;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ if (w->reg == WCD934X_ANA_LO_1_2) {
+ if (w->shift == 7) {
+ lineout_vol_reg = WCD934X_CDC_RX3_RX_PATH_CTL;
+ lineout_mix_vol_reg = WCD934X_CDC_RX3_RX_PATH_MIX_CTL;
+ dsd_mute_reg = WCD934X_CDC_DSD0_CFG2;
+ dsd_clk_reg = WCD934X_CDC_DSD0_PATH_CTL;
+ } else if (w->shift == 6) {
+ lineout_vol_reg = WCD934X_CDC_RX4_RX_PATH_CTL;
+ lineout_mix_vol_reg = WCD934X_CDC_RX4_RX_PATH_MIX_CTL;
+ dsd_mute_reg = WCD934X_CDC_DSD1_CFG2;
+ dsd_clk_reg = WCD934X_CDC_DSD1_PATH_CTL;
+ }
+ } else {
+ dev_err(codec->dev, "%s: Error enabling lineout PA\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tavil_codec_override(codec, CLS_AB, event);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * 5ms sleep is required after PA is enabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ snd_soc_update_bits(codec, lineout_vol_reg,
+ 0x10, 0x00);
+ /* Remove mix path mute if it is enabled */
+ if ((snd_soc_read(codec, lineout_mix_vol_reg)) & 0x10)
+ snd_soc_update_bits(codec,
+ lineout_mix_vol_reg,
+ 0x10, 0x00);
+ if (dsd_conf && (snd_soc_read(codec, dsd_clk_reg) & 0x01))
+ snd_soc_update_bits(codec, dsd_mute_reg, 0x04, 0x00);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (dsd_conf && (snd_soc_read(codec, dsd_clk_reg) & 0x01))
+ snd_soc_update_bits(codec, dsd_mute_reg, 0x04, 0x04);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * 5ms sleep is required after PA is disabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+ tavil_codec_override(codec, CLS_AB, event);
+ default:
+ break;
+ };
+
+ return 0;
+}
+
+static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disable AutoChop timer during power up */
+ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ 0x02, 0x00);
+
+ if (tavil->anc_func)
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+
+ wcd_clsh_fsm(codec, &tavil->clsh_d,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_EAR,
+ CLS_H_NORMAL);
+ if (tavil->anc_func)
+ snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0,
+ 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd_clsh_fsm(codec, &tavil->clsh_d,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_EAR,
+ CLS_H_NORMAL);
+ break;
+ default:
+ break;
+ };
+
+ return ret;
+}
+
+static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int hph_mode = tavil->hph_mode;
+ u8 dem_inp;
+ struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__,
+ w->name, event, hph_mode);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (!(strcmp(w->name, "RX INT2 DAC"))) {
+ snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0x20, 0x20);
+ snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0x10, 0x10);
+ }
+ if (tavil->anc_func) {
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ /* 40 msec delay is needed to avoid click and pop */
+ msleep(40);
+ }
+ /* Read DEM INP Select */
+ dem_inp = snd_soc_read(codec, WCD934X_CDC_RX2_RX_PATH_SEC0) &
+ 0x03;
+ if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+ (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+ dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n",
+ __func__, hph_mode);
+ return -EINVAL;
+ }
+ if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+ /* Ripple freq control enable */
+ snd_soc_update_bits(codec,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ 0x01, 0x01);
+ /* Disable AutoChop timer during power up */
+ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ 0x02, 0x00);
+ /* Set RDAC gain */
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec,
+ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ 0xF0, 0x40);
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
+ hph_mode = CLS_H_HIFI;
+
+ wcd_clsh_fsm(codec, &tavil->clsh_d,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHR,
+ hph_mode);
+ if (tavil->anc_func)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX2_RX_PATH_CFG0,
+ 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (!(strcmp(w->name, "RX INT2 DAC")))
+ snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0x30, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 1000us required as per HW requirement */
+ usleep_range(1000, 1100);
+ wcd_clsh_fsm(codec, &tavil->clsh_d,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHR,
+ hph_mode);
+ if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+ /* Ripple freq control disable */
+ snd_soc_update_bits(codec,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ 0x01, 0x0);
+ /* Re-set RDAC gain */
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec,
+ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ 0xF0, 0x0);
+ break;
+ default:
+ break;
+ };
+
+ return 0;
+}
+
+static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int hph_mode = tavil->hph_mode;
+ u8 dem_inp;
+ int ret = 0;
+ struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
+ uint32_t impedl = 0, impedr = 0;
+
+ dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__,
+ w->name, event, hph_mode);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (tavil->anc_func) {
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ /* 40 msec delay is needed to avoid click and pop */
+ msleep(40);
+ }
+ /* Read DEM INP Select */
+ dem_inp = snd_soc_read(codec, WCD934X_CDC_RX1_RX_PATH_SEC0) &
+ 0x03;
+ if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+ (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+ dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n",
+ __func__, hph_mode);
+ return -EINVAL;
+ }
+ if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+ /* Ripple freq control enable */
+ snd_soc_update_bits(codec,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ 0x01, 0x01);
+ /* Disable AutoChop timer during power up */
+ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ 0x02, 0x00);
+ /* Set RDAC gain */
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec,
+ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ 0xF0, 0x40);
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
+ hph_mode = CLS_H_HIFI;
+
+ wcd_clsh_fsm(codec, &tavil->clsh_d,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_HPHL,
+ hph_mode);
+
+ if (tavil->anc_func)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX1_RX_PATH_CFG0,
+ 0x10, 0x10);
+
+ ret = tavil_mbhc_get_impedance(tavil->mbhc,
+ &impedl, &impedr);
+ if (!ret) {
+ wcd_clsh_imped_config(codec, impedl, false);
+ set_bit(CLSH_Z_CONFIG, &tavil->status_mask);
+ } else {
+ dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n",
+ __func__, ret);
+ ret = 0;
+ }
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* 1000us required as per HW requirement */
+ usleep_range(1000, 1100);
+ wcd_clsh_fsm(codec, &tavil->clsh_d,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_HPHL,
+ hph_mode);
+ if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+ /* Ripple freq control disable */
+ snd_soc_update_bits(codec,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ 0x01, 0x0);
+ /* Re-set RDAC gain */
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec,
+ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ 0xF0, 0x0);
+
+ if (test_bit(CLSH_Z_CONFIG, &tavil->status_mask)) {
+ wcd_clsh_imped_config(codec, impedl, true);
+ clear_bit(CLSH_Z_CONFIG, &tavil->status_mask);
+ }
+ break;
+ default:
+ break;
+ };
+
+ return ret;
+}
+
+static int tavil_codec_lineout_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd_clsh_fsm(codec, &tavil->clsh_d,
+ WCD_CLSH_EVENT_PRE_DAC,
+ WCD_CLSH_STATE_LO,
+ CLS_AB);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd_clsh_fsm(codec, &tavil->clsh_d,
+ WCD_CLSH_EVENT_POST_PA,
+ WCD_CLSH_STATE_LO,
+ CLS_AB);
+ break;
+ }
+
+ return 0;
+}
+
+static int tavil_codec_spk_boost_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 boost_path_ctl, boost_path_cfg1;
+ u16 reg, reg_mix;
+
+ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+ if (!strcmp(w->name, "RX INT7 CHAIN")) {
+ boost_path_ctl = WCD934X_CDC_BOOST0_BOOST_PATH_CTL;
+ boost_path_cfg1 = WCD934X_CDC_RX7_RX_PATH_CFG1;
+ reg = WCD934X_CDC_RX7_RX_PATH_CTL;
+ reg_mix = WCD934X_CDC_RX7_RX_PATH_MIX_CTL;
+ } else if (!strcmp(w->name, "RX INT8 CHAIN")) {
+ boost_path_ctl = WCD934X_CDC_BOOST1_BOOST_PATH_CTL;
+ boost_path_cfg1 = WCD934X_CDC_RX8_RX_PATH_CFG1;
+ reg = WCD934X_CDC_RX8_RX_PATH_CTL;
+ reg_mix = WCD934X_CDC_RX8_RX_PATH_MIX_CTL;
+ } else {
+ dev_err(codec->dev, "%s: unknown widget: %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01);
+ snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10);
+ snd_soc_update_bits(codec, reg, 0x10, 0x00);
+ if ((snd_soc_read(codec, reg_mix)) & 0x10)
+ snd_soc_update_bits(codec, reg_mix, 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00);
+ snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00);
+ break;
+ };
+
+ return 0;
+}
+
+static int __tavil_codec_enable_swr(struct snd_soc_dapm_widget *w, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil;
+ int ch_cnt = 0;
+
+ tavil = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) ||
+ (strnstr(w->name, "INT7 MIX2",
+ sizeof("RX INT7 MIX2")))))
+ tavil->swr.rx_7_count++;
+ if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) &&
+ !tavil->swr.rx_8_count)
+ tavil->swr.rx_8_count++;
+ ch_cnt = !!(tavil->swr.rx_7_count) + tavil->swr.rx_8_count;
+
+ swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev,
+ SWR_DEVICE_UP, NULL);
+ swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev,
+ SWR_SET_NUM_RX_CH, &ch_cnt);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) ||
+ (strnstr(w->name, "INT7 MIX2",
+ sizeof("RX INT7 MIX2"))))
+ tavil->swr.rx_7_count--;
+ if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) &&
+ tavil->swr.rx_8_count)
+ tavil->swr.rx_8_count--;
+ ch_cnt = !!(tavil->swr.rx_7_count) + tavil->swr.rx_8_count;
+
+ swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev,
+ SWR_SET_NUM_RX_CH, &ch_cnt);
+
+ break;
+ }
+ dev_dbg(tavil->dev, "%s: %s: current swr ch cnt: %d\n",
+ __func__, w->name, ch_cnt);
+
+ return 0;
+}
+
+static int tavil_codec_enable_swr(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return __tavil_codec_enable_swr(w, event);
+}
+
+static int tavil_codec_config_mad(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ int idx;
+ const struct firmware *fw;
+ struct firmware_cal *hwdep_cal = NULL;
+ struct wcd_mad_audio_cal *mad_cal = NULL;
+ const void *data;
+ const char *filename = WCD934X_MAD_AUDIO_FIRMWARE_PATH;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ size_t cal_size;
+
+ hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_MAD_CAL);
+ if (hwdep_cal) {
+ data = hwdep_cal->data;
+ cal_size = hwdep_cal->size;
+ dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+ __func__);
+ } else {
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (ret || !fw) {
+ dev_err(codec->dev,
+ "%s: MAD firmware acquire failed, err = %d\n",
+ __func__, ret);
+ return -ENODEV;
+ }
+ data = fw->data;
+ cal_size = fw->size;
+ dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+ __func__);
+ }
+
+ if (cal_size < sizeof(*mad_cal)) {
+ dev_err(codec->dev,
+ "%s: Incorrect size %zd for MAD Cal, expected %zd\n",
+ __func__, cal_size, sizeof(*mad_cal));
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ mad_cal = (struct wcd_mad_audio_cal *) (data);
+ if (!mad_cal) {
+ dev_err(codec->dev,
+ "%s: Invalid calibration data\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ snd_soc_write(codec, WCD934X_SOC_MAD_MAIN_CTL_2,
+ mad_cal->microphone_info.cycle_time);
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_MAIN_CTL_1, 0xFF << 3,
+ ((uint16_t)mad_cal->microphone_info.settle_time)
+ << 3);
+
+ /* Audio */
+ snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_8,
+ mad_cal->audio_info.rms_omit_samples);
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_1,
+ 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4);
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, 0x03 << 2,
+ mad_cal->audio_info.detection_mechanism << 2);
+ snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_7,
+ mad_cal->audio_info.rms_diff_threshold & 0x3F);
+ snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_5,
+ mad_cal->audio_info.rms_threshold_lsb);
+ snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_6,
+ mad_cal->audio_info.rms_threshold_msb);
+
+ for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients);
+ idx++) {
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR,
+ 0x3F, idx);
+ snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL,
+ mad_cal->audio_info.iir_coefficients[idx]);
+ dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x",
+ __func__, idx,
+ mad_cal->audio_info.iir_coefficients[idx]);
+ }
+
+ /* Beacon */
+ snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_8,
+ mad_cal->beacon_info.rms_omit_samples);
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_1,
+ 0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4);
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_2, 0x03 << 2,
+ mad_cal->beacon_info.detection_mechanism << 2);
+ snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_7,
+ mad_cal->beacon_info.rms_diff_threshold & 0x1F);
+ snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_5,
+ mad_cal->beacon_info.rms_threshold_lsb);
+ snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_6,
+ mad_cal->beacon_info.rms_threshold_msb);
+
+ for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients);
+ idx++) {
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR,
+ 0x3F, idx);
+ snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL,
+ mad_cal->beacon_info.iir_coefficients[idx]);
+ dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x",
+ __func__, idx,
+ mad_cal->beacon_info.iir_coefficients[idx]);
+ }
+
+ /* Ultrasound */
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_1,
+ 0x07 << 4,
+ mad_cal->ultrasound_info.rms_comp_time << 4);
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_2, 0x03 << 2,
+ mad_cal->ultrasound_info.detection_mechanism << 2);
+ snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_7,
+ mad_cal->ultrasound_info.rms_diff_threshold & 0x1F);
+ snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_5,
+ mad_cal->ultrasound_info.rms_threshold_lsb);
+ snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_6,
+ mad_cal->ultrasound_info.rms_threshold_msb);
+
+done:
+ if (!hwdep_cal)
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int __tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable)
+{
+ int rc = 0;
+
+ /* Return if CPE INPUT is DEC1 */
+ if (snd_soc_read(codec, WCD934X_CPE_SS_SVA_CFG) & 0x04) {
+ dev_dbg(codec->dev, "%s: MAD is bypassed, skip mad %s\n",
+ __func__, enable ? "enable" : "disable");
+ return rc;
+ }
+
+ dev_dbg(codec->dev, "%s: enable = %s\n", __func__,
+ enable ? "enable" : "disable");
+
+ if (enable) {
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2,
+ 0x03, 0x03);
+ rc = tavil_codec_config_mad(codec);
+ if (IS_ERR_VALUE(rc)) {
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2,
+ 0x03, 0x00);
+ goto done;
+ }
+
+ /* Turn on MAD clk */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL,
+ 0x01, 0x01);
+
+ /* Undo reset for MAD */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+ 0x04, 0x04);
+ } else {
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2,
+ 0x03, 0x00);
+ /* Reset the MAD block */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL,
+ 0x02, 0x02);
+ /* Turn off MAD clk */
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+ 0x04, 0x00);
+ }
+done:
+ return rc;
+}
+
+static int tavil_codec_ape_enable_mad(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int rc = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x40);
+ rc = __tavil_codec_enable_mad(codec, true);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x00);
+ __tavil_codec_enable_mad(codec, false);
+ break;
+ }
+
+ dev_dbg(tavil->dev, "%s: event = %d\n", __func__, event);
+ return rc;
+}
+
+static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int rc = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tavil->mad_switch_cnt++;
+ if (tavil->mad_switch_cnt != 1)
+ goto done;
+
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x20);
+ rc = __tavil_codec_enable_mad(codec, true);
+ if (IS_ERR_VALUE(rc)) {
+ tavil->mad_switch_cnt--;
+ goto done;
+ }
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ tavil->mad_switch_cnt--;
+ if (tavil->mad_switch_cnt != 0)
+ goto done;
+
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x00);
+ __tavil_codec_enable_mad(codec, false);
+ break;
+ }
+done:
+ dev_dbg(tavil->dev, "%s: event = %d, mad_switch_cnt = %d\n",
+ __func__, event, tavil->mad_switch_cnt);
+ return rc;
+}
+
+static int tavil_get_asrc_mode(struct tavil_priv *tavil, int asrc,
+ u8 main_sr, u8 mix_sr)
+{
+ u8 asrc_output_mode;
+ int asrc_mode = CONV_88P2K_TO_384K;
+
+ if ((asrc < 0) || (asrc >= ASRC_MAX))
+ return 0;
+
+ asrc_output_mode = tavil->asrc_output_mode[asrc];
+
+ if (asrc_output_mode) {
+ /*
+ * If Mix sample rate is < 96KHz, use 96K to 352.8K
+ * conversion, or else use 384K to 352.8K conversion
+ */
+ if (mix_sr < 5)
+ asrc_mode = CONV_96K_TO_352P8K;
+ else
+ asrc_mode = CONV_384K_TO_352P8K;
+ } else {
+ /* Integer main and Fractional mix path */
+ if (main_sr < 8 && mix_sr > 9) {
+ asrc_mode = CONV_352P8K_TO_384K;
+ } else if (main_sr > 8 && mix_sr < 8) {
+ /* Fractional main and Integer mix path */
+ if (mix_sr < 5)
+ asrc_mode = CONV_96K_TO_352P8K;
+ else
+ asrc_mode = CONV_384K_TO_352P8K;
+ } else if (main_sr < 8 && mix_sr < 8) {
+ /* Integer main and Integer mix path */
+ asrc_mode = CONV_96K_TO_384K;
+ }
+ }
+
+ return asrc_mode;
+}
+
+static int tavil_codec_enable_asrc(struct snd_soc_codec *codec,
+ int asrc_in, int event)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ u16 cfg_reg, ctl_reg, clk_reg, asrc_ctl, mix_ctl_reg, paired_reg;
+ int asrc, ret = 0;
+ u8 main_sr, mix_sr, asrc_mode = 0;
+
+ switch (asrc_in) {
+ case ASRC_IN_HPHL:
+ cfg_reg = WCD934X_CDC_RX1_RX_PATH_CFG0;
+ ctl_reg = WCD934X_CDC_RX1_RX_PATH_CTL;
+ clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL;
+ paired_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL;
+ asrc_ctl = WCD934X_MIXING_ASRC0_CTL1;
+ asrc = ASRC0;
+ break;
+ case ASRC_IN_LO1:
+ cfg_reg = WCD934X_CDC_RX3_RX_PATH_CFG0;
+ ctl_reg = WCD934X_CDC_RX3_RX_PATH_CTL;
+ clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL;
+ paired_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL;
+ asrc_ctl = WCD934X_MIXING_ASRC0_CTL1;
+ asrc = ASRC0;
+ break;
+ case ASRC_IN_HPHR:
+ cfg_reg = WCD934X_CDC_RX2_RX_PATH_CFG0;
+ ctl_reg = WCD934X_CDC_RX2_RX_PATH_CTL;
+ clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL;
+ paired_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL;
+ asrc_ctl = WCD934X_MIXING_ASRC1_CTL1;
+ asrc = ASRC1;
+ break;
+ case ASRC_IN_LO2:
+ cfg_reg = WCD934X_CDC_RX4_RX_PATH_CFG0;
+ ctl_reg = WCD934X_CDC_RX4_RX_PATH_CTL;
+ clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL;
+ paired_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL;
+ asrc_ctl = WCD934X_MIXING_ASRC1_CTL1;
+ asrc = ASRC1;
+ break;
+ case ASRC_IN_SPKR1:
+ cfg_reg = WCD934X_CDC_RX7_RX_PATH_CFG0;
+ ctl_reg = WCD934X_CDC_RX7_RX_PATH_CTL;
+ clk_reg = WCD934X_MIXING_ASRC2_CLK_RST_CTL;
+ paired_reg = WCD934X_MIXING_ASRC3_CLK_RST_CTL;
+ asrc_ctl = WCD934X_MIXING_ASRC2_CTL1;
+ asrc = ASRC2;
+ break;
+ case ASRC_IN_SPKR2:
+ cfg_reg = WCD934X_CDC_RX8_RX_PATH_CFG0;
+ ctl_reg = WCD934X_CDC_RX8_RX_PATH_CTL;
+ clk_reg = WCD934X_MIXING_ASRC3_CLK_RST_CTL;
+ paired_reg = WCD934X_MIXING_ASRC2_CLK_RST_CTL;
+ asrc_ctl = WCD934X_MIXING_ASRC3_CTL1;
+ asrc = ASRC3;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid asrc input :%d\n", __func__,
+ asrc_in);
+ ret = -EINVAL;
+ goto done;
+ };
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (tavil->asrc_users[asrc] == 0) {
+ if ((snd_soc_read(codec, clk_reg) & 0x02) ||
+ (snd_soc_read(codec, paired_reg) & 0x02)) {
+ snd_soc_update_bits(codec, clk_reg,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec, paired_reg,
+ 0x02, 0x00);
+ }
+ snd_soc_update_bits(codec, cfg_reg, 0x80, 0x80);
+ snd_soc_update_bits(codec, clk_reg, 0x01, 0x01);
+ main_sr = snd_soc_read(codec, ctl_reg) & 0x0F;
+ mix_ctl_reg = ctl_reg + 5;
+ mix_sr = snd_soc_read(codec, mix_ctl_reg) & 0x0F;
+ asrc_mode = tavil_get_asrc_mode(tavil, asrc,
+ main_sr, mix_sr);
+ dev_dbg(codec->dev, "%s: main_sr:%d mix_sr:%d asrc_mode %d\n",
+ __func__, main_sr, mix_sr, asrc_mode);
+ snd_soc_update_bits(codec, asrc_ctl, 0x07, asrc_mode);
+ }
+ tavil->asrc_users[asrc]++;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tavil->asrc_users[asrc]--;
+ if (tavil->asrc_users[asrc] <= 0) {
+ tavil->asrc_users[asrc] = 0;
+ snd_soc_update_bits(codec, asrc_ctl, 0x07, 0x00);
+ snd_soc_update_bits(codec, cfg_reg, 0x80, 0x00);
+ snd_soc_update_bits(codec, clk_reg, 0x03, 0x02);
+ }
+ break;
+ };
+
+ dev_dbg(codec->dev, "%s: ASRC%d, users: %d\n",
+ __func__, asrc, tavil->asrc_users[asrc]);
+
+done:
+ return ret;
+}
+
+static int tavil_codec_enable_asrc_resampler(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int ret = 0;
+ u8 cfg, asrc_in;
+
+ cfg = snd_soc_read(codec, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0);
+ if (!(cfg & 0xFF)) {
+ dev_err(codec->dev, "%s: ASRC%u input not selected\n",
+ __func__, w->shift);
+ return -EINVAL;
+ }
+
+ switch (w->shift) {
+ case ASRC0:
+ asrc_in = ((cfg & 0x03) == 1) ? ASRC_IN_HPHL : ASRC_IN_LO1;
+ ret = tavil_codec_enable_asrc(codec, asrc_in, event);
+ break;
+ case ASRC1:
+ asrc_in = ((cfg & 0x0C) == 4) ? ASRC_IN_HPHR : ASRC_IN_LO2;
+ ret = tavil_codec_enable_asrc(codec, asrc_in, event);
+ break;
+ case ASRC2:
+ asrc_in = ((cfg & 0x30) == 0x20) ? ASRC_IN_SPKR1 : ASRC_INVALID;
+ ret = tavil_codec_enable_asrc(codec, asrc_in, event);
+ break;
+ case ASRC3:
+ asrc_in = ((cfg & 0xC0) == 0x80) ? ASRC_IN_SPKR2 : ASRC_INVALID;
+ ret = tavil_codec_enable_asrc(codec, asrc_in, event);
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid asrc:%u\n", __func__,
+ w->shift);
+ ret = -EINVAL;
+ break;
+ };
+
+ return ret;
+}
+
+static int tavil_enable_native_supply(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (++tavil->native_clk_users == 1) {
+ snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES,
+ 0x01, 0x01);
+ usleep_range(100, 120);
+ snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1,
+ 0x06, 0x02);
+ snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE,
+ 0x04, 0x00);
+ usleep_range(30, 50);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+ 0x10, 0x10);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (tavil->native_clk_users &&
+ (--tavil->native_clk_users == 0)) {
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+ 0x10, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1,
+ 0x06, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES,
+ 0x01, 0x00);
+ }
+ break;
+ }
+
+ dev_dbg(codec->dev, "%s: native_clk_users: %d, event: %d\n",
+ __func__, tavil->native_clk_users, event);
+
+ return 0;
+}
+
+static void tavil_codec_hphdelay_lutbypass(struct snd_soc_codec *codec,
+ u16 interp_idx, int event)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ u8 hph_dly_mask;
+ u16 hph_lut_bypass_reg = 0;
+ u16 hph_comp_ctrl7 = 0;
+
+
+ switch (interp_idx) {
+ case INTERP_HPHL:
+ hph_dly_mask = 1;
+ hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHL_COMP_LUT;
+ hph_comp_ctrl7 = WCD934X_CDC_COMPANDER1_CTL7;
+ break;
+ case INTERP_HPHR:
+ hph_dly_mask = 2;
+ hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHR_COMP_LUT;
+ hph_comp_ctrl7 = WCD934X_CDC_COMPANDER2_CTL7;
+ break;
+ default:
+ break;
+ }
+
+ if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0,
+ hph_dly_mask, 0x0);
+ snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x80);
+ if (tavil->hph_mode == CLS_H_ULP)
+ snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x20);
+ }
+
+ if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0,
+ hph_dly_mask, hph_dly_mask);
+ snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00);
+ snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x0);
+ }
+}
+
+static void tavil_codec_hd2_control(struct tavil_priv *priv,
+ u16 interp_idx, int event)
+{
+ u16 hd2_scale_reg;
+ u16 hd2_enable_reg = 0;
+ struct snd_soc_codec *codec = priv->codec;
+
+ if (TAVIL_IS_1_1(priv->wcd9xxx))
+ return;
+
+ switch (interp_idx) {
+ case INTERP_HPHL:
+ hd2_scale_reg = WCD934X_CDC_RX1_RX_PATH_SEC3;
+ hd2_enable_reg = WCD934X_CDC_RX1_RX_PATH_CFG0;
+ break;
+ case INTERP_HPHR:
+ hd2_scale_reg = WCD934X_CDC_RX2_RX_PATH_SEC3;
+ hd2_enable_reg = WCD934X_CDC_RX2_RX_PATH_CFG0;
+ break;
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x14);
+ snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04);
+ }
+
+ if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00);
+ snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00);
+ }
+}
+
+static int tavil_codec_config_ear_spkr_gain(struct snd_soc_codec *codec,
+ int event, int gain_reg)
+{
+ int comp_gain_offset, val;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ switch (tavil->swr.spkr_mode) {
+ /* Compander gain in SPKR_MODE1 case is 12 dB */
+ case WCD934X_SPKR_MODE_1:
+ comp_gain_offset = -12;
+ break;
+ /* Default case compander gain is 15 dB */
+ default:
+ comp_gain_offset = -15;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Apply ear spkr gain only if compander is enabled */
+ if (tavil->comp_enabled[COMPANDER_7] &&
+ (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) &&
+ (tavil->ear_spkr_gain != 0)) {
+ /* For example, val is -8(-12+5-1) for 4dB of gain */
+ val = comp_gain_offset + tavil->ear_spkr_gain - 1;
+ snd_soc_write(codec, gain_reg, val);
+
+ dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n",
+ __func__, val);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * Reset RX7 volume to 0 dB if compander is enabled and
+ * ear_spkr_gain is non-zero.
+ */
+ if (tavil->comp_enabled[COMPANDER_7] &&
+ (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) &&
+ (tavil->ear_spkr_gain != 0)) {
+ snd_soc_write(codec, gain_reg, 0x0);
+
+ dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n",
+ __func__);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n,
+ int event)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int comp;
+ u16 comp_ctl0_reg, rx_path_cfg0_reg;
+
+ /* EAR does not have compander */
+ if (!interp_n)
+ return 0;
+
+ comp = interp_n - 1;
+ dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n",
+ __func__, event, comp + 1, tavil->comp_enabled[comp]);
+
+ if (!tavil->comp_enabled[comp])
+ return 0;
+
+ comp_ctl0_reg = WCD934X_CDC_COMPANDER1_CTL0 + (comp * 8);
+ rx_path_cfg0_reg = WCD934X_CDC_RX1_RX_PATH_CFG0 + (comp * 20);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Enable Compander Clock */
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00);
+ snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00);
+ }
+
+ return 0;
+}
+
+static void tavil_codec_idle_detect_control(struct snd_soc_codec *codec,
+ int interp, int event)
+{
+ int reg = 0, mask, val;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ if (!tavil->idle_det_cfg.hph_idle_detect_en)
+ return;
+
+ if (interp == INTERP_HPHL) {
+ reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL;
+ mask = 0x01;
+ val = 0x01;
+ }
+ if (interp == INTERP_HPHR) {
+ reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL;
+ mask = 0x02;
+ val = 0x02;
+ }
+
+ if (reg && SND_SOC_DAPM_EVENT_ON(event))
+ snd_soc_update_bits(codec, reg, mask, val);
+
+ if (reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, reg, mask, 0x00);
+ tavil->idle_det_cfg.hph_idle_thr = 0;
+ snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, 0x0);
+ }
+}
+
+/**
+ * tavil_codec_enable_interp_clk - Enable main path Interpolator
+ * clock.
+ *
+ * @codec: Codec instance
+ * @event: Indicates speaker path gain offset value
+ * @intp_idx: Interpolator index
+ * Returns number of main clock users
+ */
+int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec,
+ int event, int interp_idx)
+{
+ struct tavil_priv *tavil;
+ u16 main_reg;
+
+ if (!codec) {
+ pr_err("%s: codec is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ tavil = snd_soc_codec_get_drvdata(codec);
+ main_reg = WCD934X_CDC_RX0_RX_PATH_CTL + (interp_idx * 20);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (tavil->main_clk_users[interp_idx] == 0) {
+ /* Main path PGA mute enable */
+ snd_soc_update_bits(codec, main_reg, 0x10, 0x10);
+ /* Clk enable */
+ snd_soc_update_bits(codec, main_reg, 0x20, 0x20);
+ tavil_codec_idle_detect_control(codec, interp_idx,
+ event);
+ tavil_codec_hd2_control(tavil, interp_idx, event);
+ tavil_codec_hphdelay_lutbypass(codec, interp_idx,
+ event);
+ tavil_config_compander(codec, interp_idx, event);
+ }
+ tavil->main_clk_users[interp_idx]++;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ tavil->main_clk_users[interp_idx]--;
+ if (tavil->main_clk_users[interp_idx] <= 0) {
+ tavil->main_clk_users[interp_idx] = 0;
+ tavil_config_compander(codec, interp_idx, event);
+ tavil_codec_hphdelay_lutbypass(codec, interp_idx,
+ event);
+ tavil_codec_hd2_control(tavil, interp_idx, event);
+ tavil_codec_idle_detect_control(codec, interp_idx,
+ event);
+ /* Clk Disable */
+ snd_soc_update_bits(codec, main_reg, 0x20, 0x00);
+ /* Reset enable and disable */
+ snd_soc_update_bits(codec, main_reg, 0x40, 0x40);
+ snd_soc_update_bits(codec, main_reg, 0x40, 0x00);
+ /* Reset rate to 48K*/
+ snd_soc_update_bits(codec, main_reg, 0x0F, 0x04);
+ }
+ }
+
+ dev_dbg(codec->dev, "%s event %d main_clk_users %d\n",
+ __func__, event, tavil->main_clk_users[interp_idx]);
+
+ return tavil->main_clk_users[interp_idx];
+}
+EXPORT_SYMBOL(tavil_codec_enable_interp_clk);
+
+static int tavil_anc_out_switch_cb(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ tavil_codec_enable_interp_clk(codec, event, w->shift);
+
+ return 0;
+}
+static int tavil_codec_set_idle_detect_thr(struct snd_soc_codec *codec,
+ int interp, int path_type)
+{
+ int port_id[4] = { 0, 0, 0, 0 };
+ int *port_ptr, num_ports;
+ int bit_width = 0, i;
+ int mux_reg, mux_reg_val;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int dai_id, idle_thr;
+
+ if ((interp != INTERP_HPHL) && (interp != INTERP_HPHR))
+ return 0;
+
+ if (!tavil->idle_det_cfg.hph_idle_detect_en)
+ return 0;
+
+ port_ptr = &port_id[0];
+ num_ports = 0;
+
+ /*
+ * Read interpolator MUX input registers and find
+ * which slimbus port is connected and store the port
+ * numbers in port_id array.
+ */
+ if (path_type == INTERP_MIX_PATH) {
+ mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1 +
+ 2 * (interp - 1);
+ mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f;
+
+ if ((mux_reg_val >= INTn_2_INP_SEL_RX0) &&
+ (mux_reg_val < INTn_2_INP_SEL_PROXIMITY)) {
+ *port_ptr++ = mux_reg_val +
+ WCD934X_RX_PORT_START_NUMBER - 1;
+ num_ports++;
+ }
+ }
+
+ if (path_type == INTERP_MAIN_PATH) {
+ mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0 +
+ 2 * (interp - 1);
+ mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f;
+ i = WCD934X_INTERP_MUX_NUM_INPUTS;
+
+ while (i) {
+ if ((mux_reg_val >= INTn_1_INP_SEL_RX0) &&
+ (mux_reg_val <= INTn_1_INP_SEL_RX7)) {
+ *port_ptr++ = mux_reg_val +
+ WCD934X_RX_PORT_START_NUMBER -
+ INTn_1_INP_SEL_RX0;
+ num_ports++;
+ }
+ mux_reg_val = (snd_soc_read(codec, mux_reg) &
+ 0xf0) >> 4;
+ mux_reg += 1;
+ i--;
+ }
+ }
+
+ dev_dbg(codec->dev, "%s: num_ports: %d, ports[%d %d %d %d]\n",
+ __func__, num_ports, port_id[0], port_id[1],
+ port_id[2], port_id[3]);
+
+ i = 0;
+ while (num_ports) {
+ dai_id = tavil_find_playback_dai_id_for_port(port_id[i++],
+ tavil);
+
+ if ((dai_id >= 0) && (dai_id < NUM_CODEC_DAIS)) {
+ dev_dbg(codec->dev, "%s: dai_id: %d bit_width: %d\n",
+ __func__, dai_id,
+ tavil->dai[dai_id].bit_width);
+
+ if (tavil->dai[dai_id].bit_width > bit_width)
+ bit_width = tavil->dai[dai_id].bit_width;
+ }
+
+ num_ports--;
+ }
+
+ switch (bit_width) {
+ case 16:
+ idle_thr = 0xff; /* F16 */
+ break;
+ case 24:
+ case 32:
+ idle_thr = 0x03; /* F22 */
+ break;
+ default:
+ idle_thr = 0x00;
+ break;
+ }
+
+ dev_dbg(codec->dev, "%s: (new) idle_thr: %d, (cur) idle_thr: %d\n",
+ __func__, idle_thr, tavil->idle_det_cfg.hph_idle_thr);
+
+ if ((tavil->idle_det_cfg.hph_idle_thr == 0) ||
+ (idle_thr < tavil->idle_det_cfg.hph_idle_thr)) {
+ snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, idle_thr);
+ tavil->idle_det_cfg.hph_idle_thr = idle_thr;
+ }
+
+ return 0;
+}
+
+static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ u16 gain_reg, mix_reg;
+ int offset_val = 0;
+ int val = 0;
+
+ if (w->shift >= WCD934X_NUM_INTERPOLATORS ||
+ w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) {
+ dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n",
+ __func__, w->shift, w->name);
+ return -EINVAL;
+ };
+
+ gain_reg = WCD934X_CDC_RX0_RX_VOL_MIX_CTL +
+ (w->shift * WCD934X_RX_PATH_CTL_OFFSET);
+ mix_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL +
+ (w->shift * WCD934X_RX_PATH_CTL_OFFSET);
+
+ if (w->shift == INTERP_SPKR1 || w->shift == INTERP_SPKR2)
+ __tavil_codec_enable_swr(w, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tavil_codec_set_idle_detect_thr(codec, w->shift,
+ INTERP_MIX_PATH);
+ tavil_codec_enable_interp_clk(codec, event, w->shift);
+ /* Clk enable */
+ snd_soc_update_bits(codec, mix_reg, 0x20, 0x20);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if ((tavil->swr.spkr_gain_offset ==
+ WCD934X_RX_GAIN_OFFSET_M1P5_DB) &&
+ (tavil->comp_enabled[COMPANDER_7] ||
+ tavil->comp_enabled[COMPANDER_8]) &&
+ (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL ||
+ gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) {
+ snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX7_RX_PATH_MIX_SEC0,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX8_RX_PATH_MIX_SEC0,
+ 0x01, 0x01);
+ offset_val = -2;
+ }
+ val = snd_soc_read(codec, gain_reg);
+ val += offset_val;
+ snd_soc_write(codec, gain_reg, val);
+ tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Clk Disable */
+ snd_soc_update_bits(codec, mix_reg, 0x20, 0x00);
+ tavil_codec_enable_interp_clk(codec, event, w->shift);
+ /* Reset enable and disable */
+ snd_soc_update_bits(codec, mix_reg, 0x40, 0x40);
+ snd_soc_update_bits(codec, mix_reg, 0x40, 0x00);
+
+ if ((tavil->swr.spkr_gain_offset ==
+ WCD934X_RX_GAIN_OFFSET_M1P5_DB) &&
+ (tavil->comp_enabled[COMPANDER_7] ||
+ tavil->comp_enabled[COMPANDER_8]) &&
+ (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL ||
+ gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) {
+ snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX7_RX_PATH_MIX_SEC0,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX8_RX_PATH_MIX_SEC0,
+ 0x01, 0x00);
+ offset_val = 2;
+ val = snd_soc_read(codec, gain_reg);
+ val += offset_val;
+ snd_soc_write(codec, gain_reg, val);
+ }
+ tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
+ break;
+ };
+ dev_dbg(codec->dev, "%s event %d name %s\n", __func__, event, w->name);
+
+ return 0;
+}
+
+/**
+ * tavil_get_dsd_config - Get pointer to dsd config structure
+ *
+ * @codec: pointer to snd_soc_codec structure
+ *
+ * Returns pointer to tavil_dsd_config structure
+ */
+struct tavil_dsd_config *tavil_get_dsd_config(struct snd_soc_codec *codec)
+{
+ struct tavil_priv *tavil;
+
+ if (!codec)
+ return NULL;
+
+ tavil = snd_soc_codec_get_drvdata(codec);
+
+ if (!tavil)
+ return NULL;
+
+ return tavil->dsd_config;
+}
+EXPORT_SYMBOL(tavil_get_dsd_config);
+
+static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ u16 gain_reg;
+ u16 reg;
+ int val;
+ int offset_val = 0;
+
+ dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
+
+ if (w->shift >= WCD934X_NUM_INTERPOLATORS ||
+ w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) {
+ dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n",
+ __func__, w->shift, w->name);
+ return -EINVAL;
+ };
+
+ reg = WCD934X_CDC_RX0_RX_PATH_CTL + (w->shift *
+ WCD934X_RX_PATH_CTL_OFFSET);
+ gain_reg = WCD934X_CDC_RX0_RX_VOL_CTL + (w->shift *
+ WCD934X_RX_PATH_CTL_OFFSET);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tavil_codec_set_idle_detect_thr(codec, w->shift,
+ INTERP_MAIN_PATH);
+ tavil_codec_enable_interp_clk(codec, event, w->shift);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* apply gain after int clk is enabled */
+ if ((tavil->swr.spkr_gain_offset ==
+ WCD934X_RX_GAIN_OFFSET_M1P5_DB) &&
+ (tavil->comp_enabled[COMPANDER_7] ||
+ tavil->comp_enabled[COMPANDER_8]) &&
+ (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD934X_CDC_RX8_RX_VOL_CTL)) {
+ snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX7_RX_PATH_MIX_SEC0,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX8_RX_PATH_MIX_SEC0,
+ 0x01, 0x01);
+ offset_val = -2;
+ }
+ val = snd_soc_read(codec, gain_reg);
+ val += offset_val;
+ snd_soc_write(codec, gain_reg, val);
+ tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tavil_codec_enable_interp_clk(codec, event, w->shift);
+
+ if ((tavil->swr.spkr_gain_offset ==
+ WCD934X_RX_GAIN_OFFSET_M1P5_DB) &&
+ (tavil->comp_enabled[COMPANDER_7] ||
+ tavil->comp_enabled[COMPANDER_8]) &&
+ (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD934X_CDC_RX8_RX_VOL_CTL)) {
+ snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX7_RX_PATH_MIX_SEC0,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_RX8_RX_PATH_MIX_SEC0,
+ 0x01, 0x00);
+ offset_val = 2;
+ val = snd_soc_read(codec, gain_reg);
+ val += offset_val;
+ snd_soc_write(codec, gain_reg, val);
+ }
+ tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
+ break;
+ };
+
+ return 0;
+}
+
+static int tavil_codec_set_iir_gain(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ dev_dbg(codec->dev, "%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU: /* fall through */
+ case SND_SOC_DAPM_PRE_PMD:
+ if (strnstr(w->name, "IIR0", sizeof("IIR0"))) {
+ snd_soc_write(codec,
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL,
+ snd_soc_read(codec,
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL));
+ snd_soc_write(codec,
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL,
+ snd_soc_read(codec,
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL));
+ snd_soc_write(codec,
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL,
+ snd_soc_read(codec,
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL));
+ snd_soc_write(codec,
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL,
+ snd_soc_read(codec,
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL));
+ } else {
+ snd_soc_write(codec,
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL,
+ snd_soc_read(codec,
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL));
+ snd_soc_write(codec,
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL,
+ snd_soc_read(codec,
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL));
+ snd_soc_write(codec,
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL,
+ snd_soc_read(codec,
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL));
+ }
+ break;
+ }
+ return 0;
+}
+
+static int tavil_codec_find_amic_input(struct snd_soc_codec *codec,
+ int adc_mux_n)
+{
+ u16 mask, shift, adc_mux_in_reg;
+ u16 amic_mux_sel_reg;
+ bool is_amic;
+
+ if (adc_mux_n < 0 || adc_mux_n > WCD934X_MAX_VALID_ADC_MUX ||
+ adc_mux_n == WCD934X_INVALID_ADC_MUX)
+ return 0;
+
+ if (adc_mux_n < 3) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 +
+ adc_mux_n;
+ mask = 0x03;
+ shift = 0;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+ 2 * adc_mux_n;
+ } else if (adc_mux_n < 4) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1;
+ mask = 0x03;
+ shift = 0;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+ 2 * adc_mux_n;
+ } else if (adc_mux_n < 7) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 +
+ (adc_mux_n - 4);
+ mask = 0x0C;
+ shift = 2;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_n - 4;
+ } else if (adc_mux_n < 8) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1;
+ mask = 0x0C;
+ shift = 2;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_n - 4;
+ } else if (adc_mux_n < 12) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 +
+ ((adc_mux_n == 8) ? (adc_mux_n - 8) :
+ (adc_mux_n - 9));
+ mask = 0x30;
+ shift = 4;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_n - 4;
+ } else if (adc_mux_n < 13) {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1;
+ mask = 0x30;
+ shift = 4;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_n - 4;
+ } else {
+ adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1;
+ mask = 0xC0;
+ shift = 6;
+ amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_n - 4;
+ }
+
+ is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask) >> shift)
+ == 1);
+ if (!is_amic)
+ return 0;
+
+ return snd_soc_read(codec, amic_mux_sel_reg) & 0x07;
+}
+
+static void tavil_codec_set_tx_hold(struct snd_soc_codec *codec,
+ u16 amic_reg, bool set)
+{
+ u8 mask = 0x20;
+ u8 val;
+
+ if (amic_reg == WCD934X_ANA_AMIC1 ||
+ amic_reg == WCD934X_ANA_AMIC3)
+ mask = 0x40;
+
+ val = set ? mask : 0x00;
+
+ switch (amic_reg) {
+ case WCD934X_ANA_AMIC1:
+ case WCD934X_ANA_AMIC2:
+ snd_soc_update_bits(codec, WCD934X_ANA_AMIC2, mask, val);
+ break;
+ case WCD934X_ANA_AMIC3:
+ case WCD934X_ANA_AMIC4:
+ snd_soc_update_bits(codec, WCD934X_ANA_AMIC4, mask, val);
+ break;
+ default:
+ dev_dbg(codec->dev, "%s: invalid amic: %d\n",
+ __func__, amic_reg);
+ break;
+ }
+}
+
+static int tavil_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ int adc_mux_n = w->shift;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int amic_n;
+
+ dev_dbg(codec->dev, "%s: event: %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ amic_n = tavil_codec_find_amic_input(codec, adc_mux_n);
+ if (amic_n) {
+ /*
+ * Prevent ANC Rx pop by leaving Tx FE in HOLD
+ * state until PA is up. Track AMIC being used
+ * so we can release the HOLD later.
+ */
+ set_bit(ANC_MIC_AMIC1 + amic_n - 1,
+ &tavil->status_mask);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static u16 tavil_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, int amic)
+{
+ u16 pwr_level_reg = 0;
+
+ switch (amic) {
+ case 1:
+ case 2:
+ pwr_level_reg = WCD934X_ANA_AMIC1;
+ break;
+
+ case 3:
+ case 4:
+ pwr_level_reg = WCD934X_ANA_AMIC3;
+ break;
+ default:
+ dev_dbg(codec->dev, "%s: invalid amic: %d\n",
+ __func__, amic);
+ break;
+ }
+
+ return pwr_level_reg;
+}
+
+#define TX_HPF_CUT_OFF_FREQ_MASK 0x60
+#define CF_MIN_3DB_4HZ 0x0
+#define CF_MIN_3DB_75HZ 0x1
+#define CF_MIN_3DB_150HZ 0x2
+
+static void tavil_tx_hpf_corner_freq_callback(struct work_struct *work)
+{
+ struct delayed_work *hpf_delayed_work;
+ struct hpf_work *hpf_work;
+ struct tavil_priv *tavil;
+ struct snd_soc_codec *codec;
+ u16 dec_cfg_reg, amic_reg, go_bit_reg;
+ u8 hpf_cut_off_freq;
+ int amic_n;
+
+ hpf_delayed_work = to_delayed_work(work);
+ hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
+ tavil = hpf_work->tavil;
+ codec = tavil->codec;
+ hpf_cut_off_freq = hpf_work->hpf_cut_off_freq;
+
+ dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator;
+ go_bit_reg = dec_cfg_reg + 7;
+
+ dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n",
+ __func__, hpf_work->decimator, hpf_cut_off_freq);
+
+ amic_n = tavil_codec_find_amic_input(codec, hpf_work->decimator);
+ if (amic_n) {
+ amic_reg = WCD934X_ANA_AMIC1 + amic_n - 1;
+ tavil_codec_set_tx_hold(codec, amic_reg, false);
+ }
+ snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK,
+ hpf_cut_off_freq << 5);
+ snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x02);
+ /* Minimum 1 clk cycle delay is required as per HW spec */
+ usleep_range(1000, 1010);
+ snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x00);
+}
+
+static void tavil_tx_mute_update_callback(struct work_struct *work)
+{
+ struct tx_mute_work *tx_mute_dwork;
+ struct tavil_priv *tavil;
+ struct delayed_work *delayed_work;
+ struct snd_soc_codec *codec;
+ u16 tx_vol_ctl_reg, hpf_gate_reg;
+
+ delayed_work = to_delayed_work(work);
+ tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork);
+ tavil = tx_mute_dwork->tavil;
+ codec = tavil->codec;
+
+ tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL +
+ 16 * tx_mute_dwork->decimator;
+ hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 +
+ 16 * tx_mute_dwork->decimator;
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00);
+}
+
+static int tavil_codec_enable_rx_path_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u16 sidetone_reg;
+
+ dev_dbg(codec->dev, "%s %d %d\n", __func__, event, w->shift);
+ sidetone_reg = WCD934X_CDC_RX0_RX_PATH_CFG1 + 0x14*(w->shift);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (!strcmp(w->name, "RX INT7 MIX2 INP"))
+ __tavil_codec_enable_swr(w, event);
+ tavil_codec_enable_interp_clk(codec, event, w->shift);
+ snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x00);
+ tavil_codec_enable_interp_clk(codec, event, w->shift);
+ if (!strcmp(w->name, "RX INT7 MIX2 INP"))
+ __tavil_codec_enable_swr(w, event);
+ break;
+ default:
+ break;
+ };
+ return 0;
+}
+
+static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ unsigned int decimator;
+ char *dec_adc_mux_name = NULL;
+ char *widget_name = NULL;
+ char *wname;
+ int ret = 0, amic_n;
+ u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg;
+ u16 tx_gain_ctl_reg;
+ char *dec;
+ u8 hpf_cut_off_freq;
+
+ dev_dbg(codec->dev, "%s %d\n", __func__, event);
+
+ widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+ if (!widget_name)
+ return -ENOMEM;
+
+ wname = widget_name;
+ dec_adc_mux_name = strsep(&widget_name, " ");
+ if (!dec_adc_mux_name) {
+ dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+ __func__, w->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ dec_adc_mux_name = widget_name;
+
+ dec = strpbrk(dec_adc_mux_name, "012345678");
+ if (!dec) {
+ dev_err(codec->dev, "%s: decimator index not found\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = kstrtouint(dec, 10, &decimator);
+ if (ret < 0) {
+ dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+ __func__, wname);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__,
+ w->name, decimator);
+
+ tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + 16 * decimator;
+ hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + 16 * decimator;
+ dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * decimator;
+ tx_gain_ctl_reg = WCD934X_CDC_TX0_TX_VOL_CTL + 16 * decimator;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ amic_n = tavil_codec_find_amic_input(codec, decimator);
+ if (amic_n)
+ pwr_level_reg = tavil_codec_get_amic_pwlvl_reg(codec,
+ amic_n);
+
+ if (pwr_level_reg) {
+ switch ((snd_soc_read(codec, pwr_level_reg) &
+ WCD934X_AMIC_PWR_LVL_MASK) >>
+ WCD934X_AMIC_PWR_LVL_SHIFT) {
+ case WCD934X_AMIC_PWR_LEVEL_LP:
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ WCD934X_DEC_PWR_LVL_MASK,
+ WCD934X_DEC_PWR_LVL_LP);
+ break;
+
+ case WCD934X_AMIC_PWR_LEVEL_HP:
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ WCD934X_DEC_PWR_LVL_MASK,
+ WCD934X_DEC_PWR_LVL_HP);
+ break;
+ case WCD934X_AMIC_PWR_LEVEL_DEFAULT:
+ default:
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ WCD934X_DEC_PWR_LVL_MASK,
+ WCD934X_DEC_PWR_LVL_DF);
+ break;
+ }
+ }
+ /* Enable TX PGA Mute */
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) &
+ TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
+
+ tavil->tx_hpf_work[decimator].hpf_cut_off_freq =
+ hpf_cut_off_freq;
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ CF_MIN_3DB_150HZ << 5);
+ snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x02);
+ /*
+ * Minimum 1 clk cycle delay is required as per
+ * HW spec.
+ */
+ usleep_range(1000, 1010);
+ snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x00);
+ }
+ /* schedule work queue to Remove Mute */
+ schedule_delayed_work(&tavil->tx_mute_dwork[decimator].dwork,
+ msecs_to_jiffies(tx_unmute_delay));
+ if (tavil->tx_hpf_work[decimator].hpf_cut_off_freq !=
+ CF_MIN_3DB_150HZ)
+ schedule_delayed_work(
+ &tavil->tx_hpf_work[decimator].dwork,
+ msecs_to_jiffies(300));
+ /* apply gain after decimator is enabled */
+ snd_soc_write(codec, tx_gain_ctl_reg,
+ snd_soc_read(codec, tx_gain_ctl_reg));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ hpf_cut_off_freq =
+ tavil->tx_hpf_work[decimator].hpf_cut_off_freq;
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
+ if (cancel_delayed_work_sync(
+ &tavil->tx_hpf_work[decimator].dwork)) {
+ if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ TX_HPF_CUT_OFF_FREQ_MASK,
+ hpf_cut_off_freq << 5);
+ snd_soc_update_bits(codec, hpf_gate_reg,
+ 0x02, 0x02);
+ /*
+ * Minimum 1 clk cycle delay is required as per
+ * HW spec.
+ */
+ usleep_range(1000, 1010);
+ snd_soc_update_bits(codec, hpf_gate_reg,
+ 0x02, 0x00);
+ }
+ }
+ cancel_delayed_work_sync(
+ &tavil->tx_mute_dwork[decimator].dwork);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00);
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ WCD934X_DEC_PWR_LVL_MASK,
+ WCD934X_DEC_PWR_LVL_DF);
+ break;
+ };
+out:
+ kfree(wname);
+ return ret;
+}
+
+static u32 tavil_get_dmic_sample_rate(struct snd_soc_codec *codec,
+ unsigned int dmic,
+ struct wcd9xxx_pdata *pdata)
+{
+ u8 tx_stream_fs;
+ u8 adc_mux_index = 0, adc_mux_sel = 0;
+ bool dec_found = false;
+ u16 adc_mux_ctl_reg, tx_fs_reg;
+ u32 dmic_fs;
+
+ while (dec_found == 0 && adc_mux_index < WCD934X_MAX_VALID_ADC_MUX) {
+ if (adc_mux_index < 4) {
+ adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+ (adc_mux_index * 2);
+ } else if (adc_mux_index < WCD934X_INVALID_ADC_MUX) {
+ adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_index - 4;
+ } else if (adc_mux_index == WCD934X_INVALID_ADC_MUX) {
+ ++adc_mux_index;
+ continue;
+ }
+ adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) &
+ 0xF8) >> 3) - 1;
+
+ if (adc_mux_sel == dmic) {
+ dec_found = true;
+ break;
+ }
+
+ ++adc_mux_index;
+ }
+
+ if (dec_found && adc_mux_index <= 8) {
+ tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index);
+ tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F;
+ if (tx_stream_fs <= 4) {
+ if (pdata->dmic_sample_rate <=
+ WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ)
+ dmic_fs = pdata->dmic_sample_rate;
+ else
+ dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ;
+ } else
+ dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+ } else {
+ dmic_fs = pdata->dmic_sample_rate;
+ }
+
+ return dmic_fs;
+}
+
+static u8 tavil_get_dmic_clk_val(struct snd_soc_codec *codec,
+ u32 mclk_rate, u32 dmic_clk_rate)
+{
+ u32 div_factor;
+ u8 dmic_ctl_val;
+
+ dev_dbg(codec->dev,
+ "%s: mclk_rate = %d, dmic_sample_rate = %d\n",
+ __func__, mclk_rate, dmic_clk_rate);
+
+ /* Default value to return in case of error */
+ if (mclk_rate == WCD934X_MCLK_CLK_9P6MHZ)
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2;
+ else
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3;
+
+ if (dmic_clk_rate == 0) {
+ dev_err(codec->dev,
+ "%s: dmic_sample_rate cannot be 0\n",
+ __func__);
+ goto done;
+ }
+
+ div_factor = mclk_rate / dmic_clk_rate;
+ switch (div_factor) {
+ case 2:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2;
+ break;
+ case 3:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3;
+ break;
+ case 4:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_4;
+ break;
+ case 6:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_6;
+ break;
+ case 8:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_8;
+ break;
+ case 16:
+ dmic_ctl_val = WCD934X_DMIC_CLK_DIV_16;
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n",
+ __func__, div_factor, mclk_rate, dmic_clk_rate);
+ break;
+ }
+
+done:
+ return dmic_ctl_val;
+}
+
+static int tavil_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ dev_dbg(codec->dev, "%s: event:%d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tavil_codec_set_tx_hold(codec, w->reg, true);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int tavil_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+ u8 dmic_clk_en = 0x01;
+ u16 dmic_clk_reg;
+ s32 *dmic_clk_cnt;
+ u8 dmic_rate_val, dmic_rate_shift = 1;
+ unsigned int dmic;
+ u32 dmic_sample_rate;
+ int ret;
+ char *wname;
+
+ wname = strpbrk(w->name, "012345");
+ if (!wname) {
+ dev_err(codec->dev, "%s: widget not found\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(wname, 10, &dmic);
+ if (ret < 0) {
+ dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (dmic) {
+ case 0:
+ case 1:
+ dmic_clk_cnt = &(tavil->dmic_0_1_clk_cnt);
+ dmic_clk_reg = WCD934X_CPE_SS_DMIC0_CTL;
+ break;
+ case 2:
+ case 3:
+ dmic_clk_cnt = &(tavil->dmic_2_3_clk_cnt);
+ dmic_clk_reg = WCD934X_CPE_SS_DMIC1_CTL;
+ break;
+ case 4:
+ case 5:
+ dmic_clk_cnt = &(tavil->dmic_4_5_clk_cnt);
+ dmic_clk_reg = WCD934X_CPE_SS_DMIC2_CTL;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid DMIC Selection\n",
+ __func__);
+ return -EINVAL;
+ };
+ dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n",
+ __func__, event, dmic, *dmic_clk_cnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dmic_sample_rate = tavil_get_dmic_sample_rate(codec, dmic,
+ pdata);
+ dmic_rate_val =
+ tavil_get_dmic_clk_val(codec,
+ pdata->mclk_rate,
+ dmic_sample_rate);
+
+ (*dmic_clk_cnt)++;
+ if (*dmic_clk_cnt == 1) {
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ 0x07 << dmic_rate_shift,
+ dmic_rate_val << dmic_rate_shift);
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ dmic_clk_en, dmic_clk_en);
+ }
+
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dmic_rate_val =
+ tavil_get_dmic_clk_val(codec,
+ pdata->mclk_rate,
+ pdata->mad_dmic_sample_rate);
+ (*dmic_clk_cnt)--;
+ if (*dmic_clk_cnt == 0) {
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ dmic_clk_en, 0);
+ snd_soc_update_bits(codec, dmic_clk_reg,
+ 0x07 << dmic_rate_shift,
+ dmic_rate_val << dmic_rate_shift);
+ }
+ break;
+ };
+
+ return 0;
+}
+
+/*
+ * tavil_mbhc_micb_adjust_voltage: adjust specific micbias voltage
+ * @codec: handle to snd_soc_codec *
+ * @req_volt: micbias voltage to be set
+ * @micb_num: micbias to be set, e.g. micbias1 or micbias2
+ *
+ * return 0 if adjustment is success or error code in case of failure
+ */
+int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec,
+ int req_volt, int micb_num)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int cur_vout_ctl, req_vout_ctl;
+ int micb_reg, micb_val, micb_en;
+ int ret = 0;
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD934X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD934X_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD934X_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD934X_ANA_MICB4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&tavil->micb_lock);
+
+ /*
+ * If requested micbias voltage is same as current micbias
+ * voltage, then just return. Otherwise, adjust voltage as
+ * per requested value. If micbias is already enabled, then
+ * to avoid slow micbias ramp-up or down enable pull-up
+ * momentarily, change the micbias value and then re-enable
+ * micbias.
+ */
+ micb_val = snd_soc_read(codec, micb_reg);
+ micb_en = (micb_val & 0xC0) >> 6;
+ cur_vout_ctl = micb_val & 0x3F;
+
+ req_vout_ctl = wcd934x_get_micb_vout_ctl_val(req_volt);
+ if (IS_ERR_VALUE(req_vout_ctl)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ if (cur_vout_ctl == req_vout_ctl) {
+ ret = 0;
+ goto exit;
+ }
+
+ dev_dbg(codec->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n",
+ __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl),
+ req_volt, micb_en);
+
+ if (micb_en == 0x1)
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80);
+
+ snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl);
+
+ if (micb_en == 0x1) {
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40);
+ /*
+ * Add 2ms delay as per HW requirement after enabling
+ * micbias
+ */
+ usleep_range(2000, 2100);
+ }
+exit:
+ mutex_unlock(&tavil->micb_lock);
+ return ret;
+}
+EXPORT_SYMBOL(tavil_mbhc_micb_adjust_voltage);
+
+/*
+ * tavil_micbias_control: enable/disable micbias
+ * @codec: handle to snd_soc_codec *
+ * @micb_num: micbias to be enabled/disabled, e.g. micbias1 or micbias2
+ * @req: control requested, enable/disable or pullup enable/disable
+ * @is_dapm: triggered by dapm or not
+ *
+ * return 0 if control is success or error code in case of failure
+ */
+int tavil_micbias_control(struct snd_soc_codec *codec,
+ int micb_num, int req, bool is_dapm)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+ int pre_off_event = 0, post_off_event = 0;
+ int post_on_event = 0, post_dapm_off = 0;
+ int post_dapm_on = 0;
+
+ if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) {
+ dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+ __func__, micb_index);
+ return -EINVAL;
+ }
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD934X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD934X_ANA_MICB2;
+ pre_off_event = WCD_EVENT_PRE_MICBIAS_2_OFF;
+ post_off_event = WCD_EVENT_POST_MICBIAS_2_OFF;
+ post_on_event = WCD_EVENT_POST_MICBIAS_2_ON;
+ post_dapm_on = WCD_EVENT_POST_DAPM_MICBIAS_2_ON;
+ post_dapm_off = WCD_EVENT_POST_DAPM_MICBIAS_2_OFF;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD934X_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD934X_ANA_MICB4;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid micbias number: %d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ }
+ mutex_lock(&tavil->micb_lock);
+
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ tavil->pullup_ref[micb_index]++;
+ if ((tavil->pullup_ref[micb_index] == 1) &&
+ (tavil->micb_ref[micb_index] == 0))
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80);
+ break;
+ case MICB_PULLUP_DISABLE:
+ if (tavil->pullup_ref[micb_index] > 0)
+ tavil->pullup_ref[micb_index]--;
+ if ((tavil->pullup_ref[micb_index] == 0) &&
+ (tavil->micb_ref[micb_index] == 0))
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00);
+ break;
+ case MICB_ENABLE:
+ tavil->micb_ref[micb_index]++;
+ if (tavil->micb_ref[micb_index] == 1) {
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40);
+ if (post_on_event && tavil->mbhc)
+ blocking_notifier_call_chain(
+ &tavil->mbhc->notifier,
+ post_on_event,
+ &tavil->mbhc->wcd_mbhc);
+ }
+ if (is_dapm && post_dapm_on && tavil->mbhc)
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ post_dapm_on, &tavil->mbhc->wcd_mbhc);
+ break;
+ case MICB_DISABLE:
+ if (tavil->micb_ref[micb_index] > 0)
+ tavil->micb_ref[micb_index]--;
+ if ((tavil->micb_ref[micb_index] == 0) &&
+ (tavil->pullup_ref[micb_index] > 0))
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80);
+ else if ((tavil->micb_ref[micb_index] == 0) &&
+ (tavil->pullup_ref[micb_index] == 0)) {
+ if (pre_off_event && tavil->mbhc)
+ blocking_notifier_call_chain(
+ &tavil->mbhc->notifier,
+ pre_off_event,
+ &tavil->mbhc->wcd_mbhc);
+ snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00);
+ if (post_off_event && tavil->mbhc)
+ blocking_notifier_call_chain(
+ &tavil->mbhc->notifier,
+ post_off_event,
+ &tavil->mbhc->wcd_mbhc);
+ }
+ if (is_dapm && post_dapm_off && tavil->mbhc)
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ post_dapm_off, &tavil->mbhc->wcd_mbhc);
+ break;
+ };
+
+ dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n",
+ __func__, micb_num, tavil->micb_ref[micb_index],
+ tavil->pullup_ref[micb_index]);
+
+ mutex_unlock(&tavil->micb_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(tavil_micbias_control);
+
+static int __tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int micb_num;
+
+ dev_dbg(codec->dev, "%s: wname: %s, event: %d\n",
+ __func__, w->name, event);
+
+ if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1")))
+ micb_num = MIC_BIAS_1;
+ else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2")))
+ micb_num = MIC_BIAS_2;
+ else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3")))
+ micb_num = MIC_BIAS_3;
+ else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4")))
+ micb_num = MIC_BIAS_4;
+ else
+ return -EINVAL;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /*
+ * MIC BIAS can also be requested by MBHC,
+ * so use ref count to handle micbias pullup
+ * and enable requests
+ */
+ tavil_micbias_control(codec, micb_num, MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* wait for cnp time */
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tavil_micbias_control(codec, micb_num, MICB_DISABLE, true);
+ break;
+ };
+
+ return 0;
+}
+
+/*
+ * tavil_codec_enable_standalone_micbias - enable micbias standalone
+ * @codec: pointer to codec instance
+ * @micb_num: number of micbias to be enabled
+ * @enable: true to enable micbias or false to disable
+ *
+ * This function is used to enable micbias (1, 2, 3 or 4) during
+ * standalone independent of whether TX use-case is running or not
+ *
+ * Return: error code in case of failure or 0 for success
+ */
+int tavil_codec_enable_standalone_micbias(struct snd_soc_codec *codec,
+ int micb_num,
+ bool enable)
+{
+ const char * const micb_names[] = {
+ DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE,
+ DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE
+ };
+ int micb_index = micb_num - 1;
+ int rc;
+
+ if (!codec) {
+ pr_err("%s: Codec memory is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) {
+ dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+ __func__, micb_index);
+ return -EINVAL;
+ }
+
+ if (enable)
+ rc = snd_soc_dapm_force_enable_pin(
+ snd_soc_codec_get_dapm(codec),
+ micb_names[micb_index]);
+ else
+ rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec),
+ micb_names[micb_index]);
+
+ if (!rc)
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+ else
+ dev_err(codec->dev, "%s: micbias%d force %s pin failed\n",
+ __func__, micb_num, (enable ? "enable" : "disable"));
+
+ return rc;
+}
+EXPORT_SYMBOL(tavil_codec_enable_standalone_micbias);
+
+static int tavil_codec_force_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd_resmgr_enable_master_bias(tavil->resmgr);
+ tavil_cdc_mclk_enable(codec, true);
+ ret = __tavil_codec_enable_micbias(w, SND_SOC_DAPM_PRE_PMU);
+ /* Wait for 1ms for better cnp */
+ usleep_range(1000, 1100);
+ tavil_cdc_mclk_enable(codec, false);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = __tavil_codec_enable_micbias(w, SND_SOC_DAPM_POST_PMD);
+ wcd_resmgr_disable_master_bias(tavil->resmgr);
+ break;
+ }
+
+ return ret;
+}
+
+static int tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return __tavil_codec_enable_micbias(w, event);
+}
+
+
+static const struct reg_sequence tavil_hph_reset_tbl[] = {
+ { WCD934X_HPH_CNP_EN, 0x80 },
+ { WCD934X_HPH_CNP_WG_CTL, 0x9A },
+ { WCD934X_HPH_CNP_WG_TIME, 0x14 },
+ { WCD934X_HPH_OCP_CTL, 0x28 },
+ { WCD934X_HPH_AUTO_CHOP, 0x16 },
+ { WCD934X_HPH_CHOP_CTL, 0x83 },
+ { WCD934X_HPH_PA_CTL1, 0x46 },
+ { WCD934X_HPH_PA_CTL2, 0x50 },
+ { WCD934X_HPH_L_EN, 0x80 },
+ { WCD934X_HPH_L_TEST, 0xE0 },
+ { WCD934X_HPH_L_ATEST, 0x50 },
+ { WCD934X_HPH_R_EN, 0x80 },
+ { WCD934X_HPH_R_TEST, 0xE0 },
+ { WCD934X_HPH_R_ATEST, 0x54 },
+ { WCD934X_HPH_RDAC_CLK_CTL1, 0x99 },
+ { WCD934X_HPH_RDAC_CLK_CTL2, 0x9B },
+ { WCD934X_HPH_RDAC_LDO_CTL, 0x33 },
+ { WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 },
+ { WCD934X_HPH_REFBUFF_UHQA_CTL, 0xA8 },
+};
+
+static const struct reg_sequence tavil_hph_reset_tbl_1_0[] = {
+ { WCD934X_HPH_REFBUFF_LP_CTL, 0x0A },
+ { WCD934X_HPH_L_DAC_CTL, 0x00 },
+ { WCD934X_HPH_R_DAC_CTL, 0x00 },
+ { WCD934X_HPH_NEW_ANA_HPH2, 0x00 },
+ { WCD934X_HPH_NEW_ANA_HPH3, 0x00 },
+ { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 },
+ { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0xA0 },
+ { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 },
+ { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 },
+ { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x00 },
+ { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 },
+ { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 },
+ { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 },
+ { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE },
+ { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 },
+ { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e},
+ { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 },
+ { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 },
+ { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 },
+};
+
+static const struct reg_sequence tavil_hph_reset_tbl_1_1[] = {
+ { WCD934X_HPH_REFBUFF_LP_CTL, 0x0E },
+ { WCD934X_HPH_L_DAC_CTL, 0x00 },
+ { WCD934X_HPH_R_DAC_CTL, 0x00 },
+ { WCD934X_HPH_NEW_ANA_HPH2, 0x00 },
+ { WCD934X_HPH_NEW_ANA_HPH3, 0x00 },
+ { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 },
+ { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0x81 },
+ { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 },
+ { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 },
+ { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x81 },
+ { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 },
+ { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 },
+ { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 },
+ { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE },
+ { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 },
+ { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e},
+ { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 },
+ { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 },
+ { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 },
+};
+
+static const struct tavil_reg_mask_val tavil_pa_disable[] = {
+ { WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x10 }, /* RX1 mute enable */
+ { WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x10 }, /* RX2 mute enable */
+ { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, /* GM3 boost disable */
+ { WCD934X_ANA_HPH, 0x80, 0x00 }, /* HPHL PA disable */
+ { WCD934X_ANA_HPH, 0x40, 0x00 }, /* HPHR PA disable */
+ { WCD934X_ANA_HPH, 0x20, 0x00 }, /* HPHL REF dsable */
+ { WCD934X_ANA_HPH, 0x10, 0x00 }, /* HPHR REF disable */
+};
+
+static const struct tavil_reg_mask_val tavil_ocp_en_seq[] = {
+ { WCD934X_RX_OCP_CTL, 0x0F, 0x02 }, /* OCP number of attempts is 2 */
+ { WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */
+ { WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */
+ { WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */
+};
+
+static const struct tavil_reg_mask_val tavil_ocp_en_seq_1[] = {
+ { WCD934X_RX_OCP_CTL, 0x0F, 0x02 }, /* OCP number of attempts is 2 */
+ { WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */
+};
+
+/* LO-HIFI */
+static const struct tavil_reg_mask_val tavil_pre_pa_en_lohifi[] = {
+ { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 },
+ { WCD934X_FLYBACK_VNEG_CTRL_4, 0xf0, 0x80 },
+ { WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x20 },
+ { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 },
+ { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 },
+ { WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0xc0 },
+ { WCD934X_HPH_PA_CTL1, 0x0e, 0x02 },
+ { WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 },
+};
+
+static const struct tavil_reg_mask_val tavil_pre_pa_en[] = {
+ { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 },
+ { WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x0 },
+ { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 },
+ { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 },
+ { WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0x80 },
+ { WCD934X_HPH_PA_CTL1, 0x0e, 0x06 },
+ { WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 },
+};
+
+static const struct tavil_reg_mask_val tavil_post_pa_en[] = {
+ { WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */
+ { WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */
+ { WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x20 }, /* RX1 mute disable */
+ { WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x20 }, /* RX2 mute disable */
+ { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x80 }, /* GM3 boost enable */
+ { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x02 },
+};
+
+static void tavil_codec_hph_reg_range_read(struct regmap *map, u8 *buf)
+{
+ regmap_bulk_read(map, WCD934X_HPH_CNP_EN, buf, TAVIL_HPH_REG_RANGE_1);
+ regmap_bulk_read(map, WCD934X_HPH_NEW_ANA_HPH2,
+ buf + TAVIL_HPH_REG_RANGE_1, TAVIL_HPH_REG_RANGE_2);
+ regmap_bulk_read(map, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ buf + TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2,
+ TAVIL_HPH_REG_RANGE_3);
+}
+
+static void tavil_codec_hph_reg_recover(struct tavil_priv *tavil,
+ struct regmap *map, int pa_status)
+{
+ int i;
+ unsigned int reg;
+
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ WCD_EVENT_OCP_OFF,
+ &tavil->mbhc->wcd_mbhc);
+
+ if (pa_status & 0xC0)
+ goto pa_en_restore;
+
+ dev_dbg(tavil->dev, "%s: HPH PA in disable state (0x%x)\n",
+ __func__, pa_status);
+
+ regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x10);
+ regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x10);
+ regmap_write_bits(map, WCD934X_ANA_HPH, 0xC0, 0x00);
+ regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x00);
+ regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x00);
+ regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x00);
+
+ /* Restore to HW defaults */
+ regmap_multi_reg_write(map, tavil_hph_reset_tbl,
+ ARRAY_SIZE(tavil_hph_reset_tbl));
+ if (TAVIL_IS_1_1(tavil->wcd9xxx))
+ regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_1,
+ ARRAY_SIZE(tavil_hph_reset_tbl_1_1));
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_0,
+ ARRAY_SIZE(tavil_hph_reset_tbl_1_0));
+
+ for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq); i++)
+ regmap_write_bits(map, tavil_ocp_en_seq[i].reg,
+ tavil_ocp_en_seq[i].mask,
+ tavil_ocp_en_seq[i].val);
+ goto end;
+
+
+pa_en_restore:
+ dev_dbg(tavil->dev, "%s: HPH PA in enable state (0x%x)\n",
+ __func__, pa_status);
+
+ /* Disable PA and other registers before restoring */
+ for (i = 0; i < ARRAY_SIZE(tavil_pa_disable); i++) {
+ if (TAVIL_IS_1_1(tavil->wcd9xxx) &&
+ (tavil_pa_disable[i].reg == WCD934X_HPH_CNP_WG_CTL))
+ continue;
+ regmap_write_bits(map, tavil_pa_disable[i].reg,
+ tavil_pa_disable[i].mask,
+ tavil_pa_disable[i].val);
+ }
+
+ regmap_multi_reg_write(map, tavil_hph_reset_tbl,
+ ARRAY_SIZE(tavil_hph_reset_tbl));
+ if (TAVIL_IS_1_1(tavil->wcd9xxx))
+ regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_1,
+ ARRAY_SIZE(tavil_hph_reset_tbl_1_1));
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_0,
+ ARRAY_SIZE(tavil_hph_reset_tbl_1_0));
+
+ for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq_1); i++)
+ regmap_write_bits(map, tavil_ocp_en_seq_1[i].reg,
+ tavil_ocp_en_seq_1[i].mask,
+ tavil_ocp_en_seq_1[i].val);
+
+ if (tavil->hph_mode == CLS_H_LOHIFI) {
+ for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en_lohifi); i++) {
+ reg = tavil_pre_pa_en_lohifi[i].reg;
+ if ((TAVIL_IS_1_1(tavil->wcd9xxx)) &&
+ ((reg == WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL) ||
+ (reg == WCD934X_HPH_CNP_WG_CTL) ||
+ (reg == WCD934X_HPH_REFBUFF_LP_CTL)))
+ continue;
+ regmap_write_bits(map,
+ tavil_pre_pa_en_lohifi[i].reg,
+ tavil_pre_pa_en_lohifi[i].mask,
+ tavil_pre_pa_en_lohifi[i].val);
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en); i++) {
+ reg = tavil_pre_pa_en[i].reg;
+ if ((TAVIL_IS_1_1(tavil->wcd9xxx)) &&
+ ((reg == WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL) ||
+ (reg == WCD934X_HPH_CNP_WG_CTL) ||
+ (reg == WCD934X_HPH_REFBUFF_LP_CTL)))
+ continue;
+ regmap_write_bits(map, tavil_pre_pa_en[i].reg,
+ tavil_pre_pa_en[i].mask,
+ tavil_pre_pa_en[i].val);
+ }
+ }
+
+ if (TAVIL_IS_1_1(tavil->wcd9xxx)) {
+ regmap_write(map, WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x84);
+ regmap_write(map, WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x84);
+ }
+
+ regmap_write_bits(map, WCD934X_ANA_HPH, 0x0C, pa_status & 0x0C);
+ regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x30);
+ /* wait for 100usec after HPH DAC is enabled */
+ usleep_range(100, 110);
+ regmap_write(map, WCD934X_ANA_HPH, pa_status);
+ /* Sleep for 7msec after PA is enabled */
+ usleep_range(7000, 7100);
+
+ for (i = 0; i < ARRAY_SIZE(tavil_post_pa_en); i++) {
+ if ((TAVIL_IS_1_1(tavil->wcd9xxx)) &&
+ (tavil_post_pa_en[i].reg == WCD934X_HPH_CNP_WG_CTL))
+ continue;
+ regmap_write_bits(map, tavil_post_pa_en[i].reg,
+ tavil_post_pa_en[i].mask,
+ tavil_post_pa_en[i].val);
+ }
+
+end:
+ tavil->mbhc->is_hph_recover = true;
+ blocking_notifier_call_chain(
+ &tavil->mbhc->notifier,
+ WCD_EVENT_OCP_ON,
+ &tavil->mbhc->wcd_mbhc);
+}
+
+static int tavil_codec_reset_hph_registers(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ u8 cache_val[TAVIL_HPH_TOTAL_REG];
+ u8 hw_val[TAVIL_HPH_TOTAL_REG];
+ int pa_status;
+ int ret;
+
+ dev_dbg(wcd9xxx->dev, "%s: event: %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ memset(cache_val, 0, TAVIL_HPH_TOTAL_REG);
+ memset(hw_val, 0, TAVIL_HPH_TOTAL_REG);
+
+ regmap_read(wcd9xxx->regmap, WCD934X_ANA_HPH, &pa_status);
+
+ tavil_codec_hph_reg_range_read(wcd9xxx->regmap, cache_val);
+
+ /* Read register values from HW directly */
+ regcache_cache_bypass(wcd9xxx->regmap, true);
+ tavil_codec_hph_reg_range_read(wcd9xxx->regmap, hw_val);
+ regcache_cache_bypass(wcd9xxx->regmap, false);
+
+ /* compare both the registers to know if there is corruption */
+ ret = memcmp(cache_val, hw_val, TAVIL_HPH_TOTAL_REG);
+
+ /* If both the values are same, it means no corruption */
+ if (ret) {
+ dev_dbg(codec->dev, "%s: cache and hw reg are not same\n",
+ __func__);
+ tavil_codec_hph_reg_recover(tavil, wcd9xxx->regmap,
+ pa_status);
+ } else {
+ dev_dbg(codec->dev, "%s: cache and hw reg are same\n",
+ __func__);
+ tavil->mbhc->is_hph_recover = false;
+ }
+ break;
+ default:
+ break;
+ };
+
+ return 0;
+}
+
+static int tavil_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ /* IIR filter band registers are at integer multiples of 16 */
+ u16 iir_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx;
+
+ ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) &
+ (1 << band_idx)) != 0;
+
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
+ iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int tavil_iir_enable_audio_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ bool iir_band_en_status;
+ int value = ucontrol->value.integer.value[0];
+ u16 iir_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx;
+
+ /* Mask first 5 bits, 6-8 are reserved */
+ snd_soc_update_bits(codec, iir_reg, (1 << band_idx),
+ (value << band_idx));
+
+ iir_band_en_status = ((snd_soc_read(codec, iir_reg) &
+ (1 << band_idx)) != 0);
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
+ iir_idx, band_idx, iir_band_en_status);
+ return 0;
+}
+
+static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec,
+ int iir_idx, int band_idx,
+ int coeff_idx)
+{
+ uint32_t value = 0;
+
+ /* Address does not automatically update if reading */
+ snd_soc_write(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t)) & 0x7F);
+
+ value |= snd_soc_read(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx));
+
+ snd_soc_write(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 1) & 0x7F);
+
+ value |= (snd_soc_read(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+ 16 * iir_idx)) << 8);
+
+ snd_soc_write(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 2) & 0x7F);
+
+ value |= (snd_soc_read(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+ 16 * iir_idx)) << 16);
+
+ snd_soc_write(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+ ((band_idx * BAND_MAX + coeff_idx)
+ * sizeof(uint32_t) + 3) & 0x7F);
+
+ /* Mask bits top 2 bits since they are reserved */
+ value |= ((snd_soc_read(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+ 16 * iir_idx)) & 0x3F) << 24);
+
+ return value;
+}
+
+static int tavil_iir_band_audio_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 0);
+ ucontrol->value.integer.value[1] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 1);
+ ucontrol->value.integer.value[2] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 2);
+ ucontrol->value.integer.value[3] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 3);
+ ucontrol->value.integer.value[4] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 4);
+
+ dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n"
+ "%s: IIR #%d band #%d b1 = 0x%x\n"
+ "%s: IIR #%d band #%d b2 = 0x%x\n"
+ "%s: IIR #%d band #%d a1 = 0x%x\n"
+ "%s: IIR #%d band #%d a2 = 0x%x\n",
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[0],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[1],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[2],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[3],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[4]);
+ return 0;
+}
+
+static void set_iir_band_coeff(struct snd_soc_codec *codec,
+ int iir_idx, int band_idx,
+ uint32_t value)
+{
+ snd_soc_write(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+ (value & 0xFF));
+
+ snd_soc_write(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+ (value >> 8) & 0xFF);
+
+ snd_soc_write(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+ (value >> 16) & 0xFF);
+
+ /* Mask top 2 bits, 7-8 are reserved */
+ snd_soc_write(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+ (value >> 24) & 0x3F);
+}
+
+static int tavil_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ int coeff_idx;
+
+ /*
+ * Mask top bit it is reserved
+ * Updates addr automatically for each B2 write
+ */
+ snd_soc_write(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+ (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+ /* Store the coefficients in sidetone coeff array */
+ for (coeff_idx = 0; coeff_idx < WCD934X_CDC_SIDETONE_IIR_COEFF_MAX;
+ coeff_idx++) {
+ tavil->sidetone_coeff_array[iir_idx][band_idx][coeff_idx] =
+ ucontrol->value.integer.value[coeff_idx];
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ tavil->sidetone_coeff_array[iir_idx][band_idx]
+ [coeff_idx]);
+ }
+
+ pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+ "%s: IIR #%d band #%d b1 = 0x%x\n"
+ "%s: IIR #%d band #%d b2 = 0x%x\n"
+ "%s: IIR #%d band #%d a1 = 0x%x\n"
+ "%s: IIR #%d band #%d a2 = 0x%x\n",
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 0),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 1),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 2),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 3),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 4));
+ return 0;
+}
+
+static void tavil_restore_iir_coeff(struct tavil_priv *tavil, int iir_idx)
+{
+ int band_idx = 0, coeff_idx = 0;
+ struct snd_soc_codec *codec = tavil->codec;
+
+ for (band_idx = 0; band_idx < BAND_MAX; band_idx++) {
+ snd_soc_write(codec,
+ (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+ (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+ for (coeff_idx = 0;
+ coeff_idx < WCD934X_CDC_SIDETONE_IIR_COEFF_MAX;
+ coeff_idx++) {
+ set_iir_band_coeff(codec, iir_idx, band_idx,
+ tavil->sidetone_coeff_array[iir_idx][band_idx]
+ [coeff_idx]);
+ }
+ }
+}
+
+static int tavil_compander_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int comp = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tavil->comp_enabled[comp];
+ return 0;
+}
+
+static int tavil_compander_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int comp = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n",
+ __func__, comp + 1, tavil->comp_enabled[comp], value);
+ tavil->comp_enabled[comp] = value;
+
+ /* Any specific register configuration for compander */
+ switch (comp) {
+ case COMPANDER_1:
+ /* Set Gain Source Select based on compander enable/disable */
+ snd_soc_update_bits(codec, WCD934X_HPH_L_EN, 0x20,
+ (value ? 0x00:0x20));
+ break;
+ case COMPANDER_2:
+ snd_soc_update_bits(codec, WCD934X_HPH_R_EN, 0x20,
+ (value ? 0x00:0x20));
+ break;
+ case COMPANDER_3:
+ case COMPANDER_4:
+ case COMPANDER_7:
+ case COMPANDER_8:
+ break;
+ default:
+ /*
+ * if compander is not enabled for any interpolator,
+ * it does not cause any audio failure, so do not
+ * return error in this case, but just print a log
+ */
+ dev_warn(codec->dev, "%s: unknown compander: %d\n",
+ __func__, comp);
+ };
+ return 0;
+}
+
+static int tavil_hph_asrc_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int index = -EINVAL;
+
+ if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode"))
+ index = ASRC0;
+ if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode"))
+ index = ASRC1;
+
+ if (tavil && (index >= 0) && (index < ASRC_MAX))
+ tavil->asrc_output_mode[index] =
+ ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int tavil_hph_asrc_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int val = 0;
+ int index = -EINVAL;
+
+ if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode"))
+ index = ASRC0;
+ if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode"))
+ index = ASRC1;
+
+ if (tavil && (index >= 0) && (index < ASRC_MAX))
+ val = tavil->asrc_output_mode[index];
+
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+
+static int tavil_hph_idle_detect_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int val = 0;
+
+ if (tavil)
+ val = tavil->idle_det_cfg.hph_idle_detect_en;
+
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+
+static int tavil_hph_idle_detect_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ if (tavil)
+ tavil->idle_det_cfg.hph_idle_detect_en =
+ ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int tavil_dmic_pin_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ u16 dmic_pin;
+ u8 reg_val, pinctl_position;
+
+ pinctl_position = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ dmic_pin = pinctl_position & 0x07;
+ reg_val = snd_soc_read(codec,
+ WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1);
+
+ ucontrol->value.integer.value[0] = !!reg_val;
+
+ return 0;
+}
+
+static int tavil_dmic_pin_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ u16 ctl_reg, cfg_reg, dmic_pin;
+ u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask;
+
+ /* 0- high or low; 1- high Z */
+ pinctl_mode = ucontrol->value.integer.value[0];
+ pinctl_position = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ switch (pinctl_position >> 3) {
+ case 0:
+ ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_0;
+ break;
+ case 1:
+ ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_1;
+ break;
+ case 2:
+ ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_2;
+ break;
+ case 3:
+ ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_3;
+ break;
+ default:
+ dev_err(codec->dev, "%s: Invalid pinctl position = %d\n",
+ __func__, pinctl_position);
+ return -EINVAL;
+ }
+
+ ctl_val = ~(pinctl_mode << (pinctl_position & 0x07));
+ mask = 1 << (pinctl_position & 0x07);
+ snd_soc_update_bits(codec, ctl_reg, mask, ctl_val);
+
+ dmic_pin = pinctl_position & 0x07;
+ cfg_reg = WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1;
+ if (pinctl_mode) {
+ if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ cfg_val = 0x6;
+ else
+ cfg_val = 0xD;
+ } else
+ cfg_val = 0;
+ snd_soc_update_bits(codec, cfg_reg, 0x1F, cfg_val);
+
+ dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n",
+ __func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val);
+
+ return 0;
+}
+
+static int tavil_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ u16 amic_reg = 0;
+
+ if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE"))
+ amic_reg = WCD934X_ANA_AMIC1;
+ if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE"))
+ amic_reg = WCD934X_ANA_AMIC3;
+
+ if (amic_reg)
+ ucontrol->value.integer.value[0] =
+ (snd_soc_read(codec, amic_reg) &
+ WCD934X_AMIC_PWR_LVL_MASK) >>
+ WCD934X_AMIC_PWR_LVL_SHIFT;
+ return 0;
+}
+
+static int tavil_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ u32 mode_val;
+ u16 amic_reg = 0;
+
+ mode_val = ucontrol->value.enumerated.item[0];
+
+ dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val);
+
+ if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE"))
+ amic_reg = WCD934X_ANA_AMIC1;
+ if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE"))
+ amic_reg = WCD934X_ANA_AMIC3;
+
+ if (amic_reg)
+ snd_soc_update_bits(codec, amic_reg, WCD934X_AMIC_PWR_LVL_MASK,
+ mode_val << WCD934X_AMIC_PWR_LVL_SHIFT);
+ return 0;
+}
+
+static const char *const tavil_conn_mad_text[] = {
+ "NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "NOTUSED5",
+ "NOTUSED6", "NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3",
+ "DMIC4", "DMIC5", "NOTUSED3", "NOTUSED4"
+};
+
+static const struct soc_enum tavil_conn_mad_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tavil_conn_mad_text),
+ tavil_conn_mad_text);
+
+static int tavil_mad_input_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ u8 tavil_mad_input;
+
+ tavil_mad_input = snd_soc_read(codec, WCD934X_SOC_MAD_INP_SEL) & 0x0F;
+ ucontrol->value.integer.value[0] = tavil_mad_input;
+
+ dev_dbg(codec->dev, "%s: tavil_mad_input = %s\n", __func__,
+ tavil_conn_mad_text[tavil_mad_input]);
+
+ return 0;
+}
+
+static int tavil_mad_input_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_card *card = codec->component.card;
+ u8 tavil_mad_input;
+ char mad_amic_input_widget[6];
+ const char *mad_input_widget;
+ const char *source_widget = NULL;
+ u32 adc, i, mic_bias_found = 0;
+ int ret = 0;
+ char *mad_input;
+ bool is_adc_input = false;
+
+ tavil_mad_input = ucontrol->value.integer.value[0];
+
+ if (tavil_mad_input >= sizeof(tavil_conn_mad_text)/
+ sizeof(tavil_conn_mad_text[0])) {
+ dev_err(codec->dev,
+ "%s: tavil_mad_input = %d out of bounds\n",
+ __func__, tavil_mad_input);
+ return -EINVAL;
+ }
+
+ if (strnstr(tavil_conn_mad_text[tavil_mad_input], "NOTUSED",
+ sizeof("NOTUSED"))) {
+ dev_dbg(codec->dev,
+ "%s: Unsupported tavil_mad_input = %s\n",
+ __func__, tavil_conn_mad_text[tavil_mad_input]);
+ /* Make sure the MAD register is updated */
+ snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP,
+ 0x88, 0x00);
+ return -EINVAL;
+ }
+
+ if (strnstr(tavil_conn_mad_text[tavil_mad_input],
+ "ADC", sizeof("ADC"))) {
+ mad_input = strpbrk(tavil_conn_mad_text[tavil_mad_input],
+ "1234");
+ if (!mad_input) {
+ dev_err(codec->dev, "%s: Invalid MAD input %s\n",
+ __func__, tavil_conn_mad_text[tavil_mad_input]);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(mad_input, 10, &adc);
+ if ((ret < 0) || (adc > 4)) {
+ dev_err(codec->dev, "%s: Invalid ADC = %s\n", __func__,
+ tavil_conn_mad_text[tavil_mad_input]);
+ return -EINVAL;
+ }
+
+ /*AMIC4 and AMIC5 share ADC4*/
+ if ((adc == 4) &&
+ (snd_soc_read(codec, WCD934X_TX_NEW_AMIC_4_5_SEL) & 0x10))
+ adc = 5;
+
+ snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc);
+
+ mad_input_widget = mad_amic_input_widget;
+ is_adc_input = true;
+ } else {
+ /* DMIC type input widget*/
+ mad_input_widget = tavil_conn_mad_text[tavil_mad_input];
+ }
+
+ dev_dbg(codec->dev,
+ "%s: tavil input widget = %s, adc_input = %s\n", __func__,
+ mad_input_widget, is_adc_input ? "true" : "false");
+
+ for (i = 0; i < card->num_of_dapm_routes; i++) {
+ if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) {
+ source_widget = card->of_dapm_routes[i].source;
+ if (!source_widget) {
+ dev_err(codec->dev,
+ "%s: invalid source widget\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (strnstr(source_widget,
+ "MIC BIAS1", sizeof("MIC BIAS1"))) {
+ mic_bias_found = 1;
+ break;
+ } else if (strnstr(source_widget,
+ "MIC BIAS2", sizeof("MIC BIAS2"))) {
+ mic_bias_found = 2;
+ break;
+ } else if (strnstr(source_widget,
+ "MIC BIAS3", sizeof("MIC BIAS3"))) {
+ mic_bias_found = 3;
+ break;
+ } else if (strnstr(source_widget,
+ "MIC BIAS4", sizeof("MIC BIAS4"))) {
+ mic_bias_found = 4;
+ break;
+ }
+ }
+ }
+
+ if (!mic_bias_found) {
+ dev_err(codec->dev, "%s: mic bias not found for input %s\n",
+ __func__, mad_input_widget);
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "%s: mic_bias found = %d\n", __func__,
+ mic_bias_found);
+
+ snd_soc_update_bits(codec, WCD934X_SOC_MAD_INP_SEL,
+ 0x0F, tavil_mad_input);
+ snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP,
+ 0x07, mic_bias_found);
+ /* for all adc inputs, mad should be in micbias mode with BG enabled */
+ if (is_adc_input)
+ snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP,
+ 0x88, 0x88);
+ else
+ snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP,
+ 0x88, 0x00);
+ return 0;
+}
+
+static int tavil_ear_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 ear_pa_gain;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ ear_pa_gain = snd_soc_read(codec, WCD934X_ANA_EAR);
+
+ ear_pa_gain = (ear_pa_gain & 0x70) >> 4;
+
+ ucontrol->value.integer.value[0] = ear_pa_gain;
+
+ dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__,
+ ear_pa_gain);
+
+ return 0;
+}
+
+static int tavil_ear_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 ear_pa_gain;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ ear_pa_gain = ucontrol->value.integer.value[0] << 4;
+
+ snd_soc_update_bits(codec, WCD934X_ANA_EAR, 0x70, ear_pa_gain);
+ return 0;
+}
+
+static int tavil_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tavil->ear_spkr_gain;
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int tavil_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ tavil->ear_spkr_gain = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: gain = %d\n", __func__, tavil->ear_spkr_gain);
+
+ return 0;
+}
+
+static int tavil_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tavil->hph_mode;
+ return 0;
+}
+
+static int tavil_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ u32 mode_val;
+
+ mode_val = ucontrol->value.enumerated.item[0];
+
+ dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val);
+
+ if (mode_val == 0) {
+ dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H LOHiFi\n",
+ __func__);
+ mode_val = CLS_H_LOHIFI;
+ }
+ tavil->hph_mode = mode_val;
+ return 0;
+}
+
+static const char * const rx_hph_mode_mux_text[] = {
+ "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI",
+ "CLS_H_ULP", "CLS_AB_HIFI",
+};
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
+ rx_hph_mode_mux_text);
+
+static const char *const tavil_anc_func_text[] = {"OFF", "ON"};
+static const struct soc_enum tavil_anc_func_enum =
+ SOC_ENUM_SINGLE_EXT(2, tavil_anc_func_text);
+
+/* Cutoff frequency for high pass filter */
+static const char * const cf_text[] = {
+ "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ"
+};
+
+static const char * const rx_cf_text[] = {
+ "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ",
+ "CF_NEG_3DB_0P48HZ"
+};
+
+static const char * const amic_pwr_lvl_text[] = {
+ "LOW_PWR", "DEFAULT", "HIGH_PERF"
+};
+
+static const char * const hph_idle_detect_text[] = {
+ "OFF", "ON"
+};
+
+static const char * const asrc_mode_text[] = {
+ "INT", "FRAC"
+};
+
+static const char * const tavil_ear_pa_gain_text[] = {
+ "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB",
+ "G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB"
+};
+
+static const char * const tavil_ear_spkr_pa_gain_text[] = {
+ "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB",
+ "G_4_DB", "G_5_DB", "G_6_DB"
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_pa_gain_enum, tavil_ear_pa_gain_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_spkr_pa_gain_enum,
+ tavil_ear_spkr_pa_gain_text);
+static SOC_ENUM_SINGLE_EXT_DECL(amic_pwr_lvl_enum, amic_pwr_lvl_text);
+static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text);
+static SOC_ENUM_SINGLE_EXT_DECL(asrc_mode_enum, asrc_mode_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec0_enum, WCD934X_CDC_TX0_TX_PATH_CFG0, 5,
+ cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec1_enum, WCD934X_CDC_TX1_TX_PATH_CFG0, 5,
+ cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec2_enum, WCD934X_CDC_TX2_TX_PATH_CFG0, 5,
+ cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec3_enum, WCD934X_CDC_TX3_TX_PATH_CFG0, 5,
+ cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec4_enum, WCD934X_CDC_TX4_TX_PATH_CFG0, 5,
+ cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec5_enum, WCD934X_CDC_TX5_TX_PATH_CFG0, 5,
+ cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec6_enum, WCD934X_CDC_TX6_TX_PATH_CFG0, 5,
+ cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec7_enum, WCD934X_CDC_TX7_TX_PATH_CFG0, 5,
+ cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec8_enum, WCD934X_CDC_TX8_TX_PATH_CFG0, 5,
+ cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int0_1_enum, WCD934X_CDC_RX0_RX_PATH_CFG2, 0,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int1_1_enum, WCD934X_CDC_RX1_RX_PATH_CFG2, 0,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int2_1_enum, WCD934X_CDC_RX2_RX_PATH_CFG2, 0,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int3_1_enum, WCD934X_CDC_RX3_RX_PATH_CFG2, 0,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int4_1_enum, WCD934X_CDC_RX4_RX_PATH_CFG2, 0,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int7_1_enum, WCD934X_CDC_RX7_RX_PATH_CFG2, 0,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int8_1_enum, WCD934X_CDC_RX8_RX_PATH_CFG2, 0,
+ rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 2,
+ rx_cf_text);
+
+static const struct snd_kcontrol_new tavil_snd_controls[] = {
+ SOC_ENUM_EXT("EAR PA Gain", tavil_ear_pa_gain_enum,
+ tavil_ear_pa_gain_get, tavil_ear_pa_gain_put),
+ SOC_ENUM_EXT("EAR SPKR PA Gain", tavil_ear_spkr_pa_gain_enum,
+ tavil_ear_spkr_pa_gain_get, tavil_ear_spkr_pa_gain_put),
+ SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 20, 1, line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 20, 1, line_gain),
+ SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER,
+ 3, 16, 1, line_gain),
+ SOC_SINGLE_TLV("LINEOUT2 Volume", WCD934X_DIFF_LO_LO2_COMPANDER,
+ 3, 16, 1, line_gain),
+ SOC_SINGLE_TLV("ADC1 Volume", WCD934X_ANA_AMIC1, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", WCD934X_ANA_AMIC2, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", WCD934X_ANA_AMIC3, 0, 20, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC4 Volume", WCD934X_ANA_AMIC4, 0, 20, 0, analog_gain),
+
+ SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD934X_CDC_RX0_RX_VOL_CTL,
+ 0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+ SOC_SINGLE_SX_TLV("RX1 Digital Volume", WCD934X_CDC_RX1_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX2 Digital Volume", WCD934X_CDC_RX2_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX3 Digital Volume", WCD934X_CDC_RX3_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX4 Digital Volume", WCD934X_CDC_RX4_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX7 Digital Volume", WCD934X_CDC_RX7_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX8 Digital Volume", WCD934X_CDC_RX8_RX_VOL_CTL,
+ 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX0 Mix Digital Volume",
+ WCD934X_CDC_RX0_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX1 Mix Digital Volume",
+ WCD934X_CDC_RX1_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX2 Mix Digital Volume",
+ WCD934X_CDC_RX2_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX3 Mix Digital Volume",
+ WCD934X_CDC_RX3_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX4 Mix Digital Volume",
+ WCD934X_CDC_RX4_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume",
+ WCD934X_CDC_RX7_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume",
+ WCD934X_CDC_RX8_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+
+ SOC_SINGLE_SX_TLV("DEC0 Volume", WCD934X_CDC_TX0_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC1 Volume", WCD934X_CDC_TX1_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC2 Volume", WCD934X_CDC_TX2_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC3 Volume", WCD934X_CDC_TX3_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC4 Volume", WCD934X_CDC_TX4_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC5 Volume", WCD934X_CDC_TX5_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC6 Volume", WCD934X_CDC_TX6_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC7 Volume", WCD934X_CDC_TX7_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC8 Volume", WCD934X_CDC_TX8_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+
+ SOC_SINGLE_SX_TLV("IIR0 INP0 Volume",
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, 40,
+ digital_gain),
+ SOC_SINGLE_SX_TLV("IIR0 INP1 Volume",
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, 40,
+ digital_gain),
+ SOC_SINGLE_SX_TLV("IIR0 INP2 Volume",
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, 40,
+ digital_gain),
+ SOC_SINGLE_SX_TLV("IIR0 INP3 Volume",
+ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, 40,
+ digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP0 Volume",
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84, 40,
+ digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP1 Volume",
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84, 40,
+ digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP2 Volume",
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84, 40,
+ digital_gain),
+ SOC_SINGLE_SX_TLV("IIR1 INP3 Volume",
+ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, 40,
+ digital_gain),
+
+ SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tavil_get_anc_slot,
+ tavil_put_anc_slot),
+ SOC_ENUM_EXT("ANC Function", tavil_anc_func_enum, tavil_get_anc_func,
+ tavil_put_anc_func),
+
+ SOC_ENUM("TX0 HPF cut off", cf_dec0_enum),
+ SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
+ SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
+ SOC_ENUM("TX3 HPF cut off", cf_dec3_enum),
+ SOC_ENUM("TX4 HPF cut off", cf_dec4_enum),
+ SOC_ENUM("TX5 HPF cut off", cf_dec5_enum),
+ SOC_ENUM("TX6 HPF cut off", cf_dec6_enum),
+ SOC_ENUM("TX7 HPF cut off", cf_dec7_enum),
+ SOC_ENUM("TX8 HPF cut off", cf_dec8_enum),
+
+ SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum),
+ SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum),
+ SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum),
+ SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum),
+ SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum),
+ SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum),
+ SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum),
+ SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum),
+ SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum),
+ SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum),
+ SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum),
+ SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum),
+ SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum),
+ SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum),
+
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+ tavil_rx_hph_mode_get, tavil_rx_hph_mode_put),
+
+ SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0,
+ tavil_iir_enable_audio_mixer_get,
+ tavil_iir_enable_audio_mixer_put),
+ SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0,
+ tavil_iir_enable_audio_mixer_get,
+ tavil_iir_enable_audio_mixer_put),
+ SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0,
+ tavil_iir_enable_audio_mixer_get,
+ tavil_iir_enable_audio_mixer_put),
+ SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0,
+ tavil_iir_enable_audio_mixer_get,
+ tavil_iir_enable_audio_mixer_put),
+ SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0,
+ tavil_iir_enable_audio_mixer_get,
+ tavil_iir_enable_audio_mixer_put),
+ SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0,
+ tavil_iir_enable_audio_mixer_get,
+ tavil_iir_enable_audio_mixer_put),
+ SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0,
+ tavil_iir_enable_audio_mixer_get,
+ tavil_iir_enable_audio_mixer_put),
+ SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0,
+ tavil_iir_enable_audio_mixer_get,
+ tavil_iir_enable_audio_mixer_put),
+ SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0,
+ tavil_iir_enable_audio_mixer_get,
+ tavil_iir_enable_audio_mixer_put),
+ SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0,
+ tavil_iir_enable_audio_mixer_get,
+ tavil_iir_enable_audio_mixer_put),
+
+ SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5,
+ tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+ SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5,
+ tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+ SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5,
+ tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+ SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5,
+ tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+ SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5,
+ tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5,
+ tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5,
+ tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5,
+ tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5,
+ tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5,
+ tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+
+ SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0,
+ tavil_compander_get, tavil_compander_put),
+ SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
+ tavil_compander_get, tavil_compander_put),
+ SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0,
+ tavil_compander_get, tavil_compander_put),
+ SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0,
+ tavil_compander_get, tavil_compander_put),
+ SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0,
+ tavil_compander_get, tavil_compander_put),
+ SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0,
+ tavil_compander_get, tavil_compander_put),
+
+ SOC_ENUM_EXT("ASRC0 Output Mode", asrc_mode_enum,
+ tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put),
+ SOC_ENUM_EXT("ASRC1 Output Mode", asrc_mode_enum,
+ tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put),
+
+ SOC_ENUM_EXT("HPH Idle Detect", hph_idle_detect_enum,
+ tavil_hph_idle_detect_get, tavil_hph_idle_detect_put),
+
+ SOC_ENUM_EXT("MAD Input", tavil_conn_mad_enum,
+ tavil_mad_input_get, tavil_mad_input_put),
+
+ SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0,
+ tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+
+ SOC_SINGLE_EXT("DMIC1_DATA_PIN_MODE", SND_SOC_NOPM, 18, 1, 0,
+ tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+
+ SOC_SINGLE_EXT("DMIC2_CLK_PIN_MODE", SND_SOC_NOPM, 19, 1, 0,
+ tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+
+ SOC_SINGLE_EXT("DMIC2_DATA_PIN_MODE", SND_SOC_NOPM, 20, 1, 0,
+ tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+
+ SOC_SINGLE_EXT("DMIC3_CLK_PIN_MODE", SND_SOC_NOPM, 21, 1, 0,
+ tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+
+ SOC_SINGLE_EXT("DMIC3_DATA_PIN_MODE", SND_SOC_NOPM, 22, 1, 0,
+ tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+ SOC_ENUM_EXT("AMIC_1_2 PWR MODE", amic_pwr_lvl_enum,
+ tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put),
+ SOC_ENUM_EXT("AMIC_3_4 PWR MODE", amic_pwr_lvl_enum,
+ tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put),
+ SOC_ENUM_EXT("AMIC_5_6 PWR MODE", amic_pwr_lvl_enum,
+ tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put),
+};
+
+static int tavil_dec_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ u16 mic_sel_reg = 0;
+ u8 mic_sel;
+
+ val = ucontrol->value.enumerated.item[0];
+ if (val > e->items - 1)
+ return -EINVAL;
+
+ dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__,
+ widget->name, val);
+
+ switch (e->reg) {
+ case WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1:
+ if (e->shift_l == 0)
+ mic_sel_reg = WCD934X_CDC_TX0_TX_PATH_CFG0;
+ else if (e->shift_l == 2)
+ mic_sel_reg = WCD934X_CDC_TX4_TX_PATH_CFG0;
+ else if (e->shift_l == 4)
+ mic_sel_reg = WCD934X_CDC_TX8_TX_PATH_CFG0;
+ break;
+ case WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1:
+ if (e->shift_l == 0)
+ mic_sel_reg = WCD934X_CDC_TX1_TX_PATH_CFG0;
+ else if (e->shift_l == 2)
+ mic_sel_reg = WCD934X_CDC_TX5_TX_PATH_CFG0;
+ break;
+ case WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1:
+ if (e->shift_l == 0)
+ mic_sel_reg = WCD934X_CDC_TX2_TX_PATH_CFG0;
+ else if (e->shift_l == 2)
+ mic_sel_reg = WCD934X_CDC_TX6_TX_PATH_CFG0;
+ break;
+ case WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1:
+ if (e->shift_l == 0)
+ mic_sel_reg = WCD934X_CDC_TX3_TX_PATH_CFG0;
+ else if (e->shift_l == 2)
+ mic_sel_reg = WCD934X_CDC_TX7_TX_PATH_CFG0;
+ break;
+ default:
+ dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n",
+ __func__, e->reg);
+ return -EINVAL;
+ }
+
+ /* ADC: 0, DMIC: 1 */
+ mic_sel = val ? 0x0 : 0x1;
+ if (mic_sel_reg)
+ snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, mic_sel << 7);
+
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static int tavil_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ unsigned short look_ahead_dly_reg = WCD934X_CDC_RX0_RX_PATH_CFG0;
+
+ val = ucontrol->value.enumerated.item[0];
+ if (val >= e->items)
+ return -EINVAL;
+
+ dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__,
+ widget->name, val);
+
+ if (e->reg == WCD934X_CDC_RX0_RX_PATH_SEC0)
+ look_ahead_dly_reg = WCD934X_CDC_RX0_RX_PATH_CFG0;
+ else if (e->reg == WCD934X_CDC_RX1_RX_PATH_SEC0)
+ look_ahead_dly_reg = WCD934X_CDC_RX1_RX_PATH_CFG0;
+ else if (e->reg == WCD934X_CDC_RX2_RX_PATH_SEC0)
+ look_ahead_dly_reg = WCD934X_CDC_RX2_RX_PATH_CFG0;
+
+ /* Set Look Ahead Delay */
+ snd_soc_update_bits(codec, look_ahead_dly_reg,
+ 0x08, (val ? 0x08 : 0x00));
+ /* Set DEM INP Select */
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static const char * const rx_int0_7_mix_mux_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+ "RX6", "RX7", "PROXIMITY"
+};
+
+static const char * const rx_int_mix_mux_text[] = {
+ "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+ "RX6", "RX7"
+};
+
+static const char * const rx_prim_mix_text[] = {
+ "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2",
+ "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const rx_sidetone_mix_text[] = {
+ "ZERO", "SRC0", "SRC1", "SRC_SUM"
+};
+
+static const char * const cdc_if_tx0_mux_text[] = {
+ "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192"
+};
+static const char * const cdc_if_tx1_mux_text[] = {
+ "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192"
+};
+static const char * const cdc_if_tx2_mux_text[] = {
+ "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192"
+};
+static const char * const cdc_if_tx3_mux_text[] = {
+ "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192"
+};
+static const char * const cdc_if_tx4_mux_text[] = {
+ "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192"
+};
+static const char * const cdc_if_tx5_mux_text[] = {
+ "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192"
+};
+static const char * const cdc_if_tx6_mux_text[] = {
+ "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192"
+};
+static const char * const cdc_if_tx7_mux_text[] = {
+ "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192"
+};
+static const char * const cdc_if_tx8_mux_text[] = {
+ "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192"
+};
+static const char * const cdc_if_tx9_mux_text[] = {
+ "ZERO", "DEC7", "DEC7_192"
+};
+static const char * const cdc_if_tx10_mux_text[] = {
+ "ZERO", "DEC6", "DEC6_192"
+};
+static const char * const cdc_if_tx11_mux_text[] = {
+ "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST"
+};
+static const char * const cdc_if_tx11_inp1_mux_text[] = {
+ "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4",
+ "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12"
+};
+static const char * const cdc_if_tx13_mux_text[] = {
+ "CDC_DEC_5", "MAD_BRDCST"
+};
+static const char * const cdc_if_tx13_inp1_mux_text[] = {
+ "ZERO", "DEC5", "DEC5_192"
+};
+
+static const char * const iir_inp_mux_text[] = {
+ "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6",
+ "DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const rx_int_dem_inp_mux_text[] = {
+ "NORMAL_DSM_OUT", "CLSH_DSM_OUT",
+};
+
+static const char * const rx_int0_1_interp_mux_text[] = {
+ "ZERO", "RX INT0_1 MIX1",
+};
+
+static const char * const rx_int1_1_interp_mux_text[] = {
+ "ZERO", "RX INT1_1 MIX1",
+};
+
+static const char * const rx_int2_1_interp_mux_text[] = {
+ "ZERO", "RX INT2_1 MIX1",
+};
+
+static const char * const rx_int3_1_interp_mux_text[] = {
+ "ZERO", "RX INT3_1 MIX1",
+};
+
+static const char * const rx_int4_1_interp_mux_text[] = {
+ "ZERO", "RX INT4_1 MIX1",
+};
+
+static const char * const rx_int7_1_interp_mux_text[] = {
+ "ZERO", "RX INT7_1 MIX1",
+};
+
+static const char * const rx_int8_1_interp_mux_text[] = {
+ "ZERO", "RX INT8_1 MIX1",
+};
+
+static const char * const rx_int0_2_interp_mux_text[] = {
+ "ZERO", "RX INT0_2 MUX",
+};
+
+static const char * const rx_int1_2_interp_mux_text[] = {
+ "ZERO", "RX INT1_2 MUX",
+};
+
+static const char * const rx_int2_2_interp_mux_text[] = {
+ "ZERO", "RX INT2_2 MUX",
+};
+
+static const char * const rx_int3_2_interp_mux_text[] = {
+ "ZERO", "RX INT3_2 MUX",
+};
+
+static const char * const rx_int4_2_interp_mux_text[] = {
+ "ZERO", "RX INT4_2 MUX",
+};
+
+static const char * const rx_int7_2_interp_mux_text[] = {
+ "ZERO", "RX INT7_2 MUX",
+};
+
+static const char * const rx_int8_2_interp_mux_text[] = {
+ "ZERO", "RX INT8_2 MUX",
+};
+
+static const char * const mad_sel_txt[] = {
+ "SPE", "MSM"
+};
+
+static const char * const mad_inp_mux_txt[] = {
+ "MAD", "DEC1"
+};
+
+static const char * const adc_mux_text[] = {
+ "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2"
+};
+
+static const char * const dmic_mux_text[] = {
+ "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5"
+};
+
+static const char * const amic_mux_text[] = {
+ "ZERO", "ADC1", "ADC2", "ADC3", "ADC4"
+};
+
+static const char * const amic4_5_sel_text[] = {
+ "AMIC4", "AMIC5"
+};
+
+static const char * const anc0_fb_mux_text[] = {
+ "ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR",
+ "ANC_IN_LO1"
+};
+
+static const char * const anc1_fb_mux_text[] = {
+ "ZERO", "ANC_IN_HPHR", "ANC_IN_LO2"
+};
+
+static const char * const rx_echo_mux_text[] = {
+ "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4",
+ "RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8"
+};
+
+static const char *const slim_rx_mux_text[] = {
+ "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB"
+};
+
+static const char *const cdc_if_rx0_mux_text[] = {
+ "SLIM RX0", "I2S_0 RX0"
+};
+static const char *const cdc_if_rx1_mux_text[] = {
+ "SLIM RX1", "I2S_0 RX1"
+};
+static const char *const cdc_if_rx2_mux_text[] = {
+ "SLIM RX2", "I2S_0 RX2"
+};
+static const char *const cdc_if_rx3_mux_text[] = {
+ "SLIM RX3", "I2S_0 RX3"
+};
+static const char *const cdc_if_rx4_mux_text[] = {
+ "SLIM RX4", "I2S_0 RX4"
+};
+static const char *const cdc_if_rx5_mux_text[] = {
+ "SLIM RX5", "I2S_0 RX5"
+};
+static const char *const cdc_if_rx6_mux_text[] = {
+ "SLIM RX6", "I2S_0 RX6"
+};
+static const char *const cdc_if_rx7_mux_text[] = {
+ "SLIM RX7", "I2S_0 RX7"
+};
+
+static const char * const asrc0_mux_text[] = {
+ "ZERO", "ASRC_IN_HPHL", "ASRC_IN_LO1",
+};
+
+static const char * const asrc1_mux_text[] = {
+ "ZERO", "ASRC_IN_HPHR", "ASRC_IN_LO2",
+};
+
+static const char * const asrc2_mux_text[] = {
+ "ZERO", "ASRC_IN_SPKR1",
+};
+
+static const char * const asrc3_mux_text[] = {
+ "ZERO", "ASRC_IN_SPKR2",
+};
+
+static const char * const native_mux_text[] = {
+ "OFF", "ON",
+};
+
+static const struct snd_kcontrol_new aif4_vi_mixer[] = {
+ SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, WCD934X_TX14, 1, 0,
+ tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put),
+ SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, WCD934X_TX15, 1, 0,
+ tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif1_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif2_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif3_cap_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif4_mad_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+WCD_DAPM_ENUM_EXT(slim_rx0, SND_SOC_NOPM, 0, slim_rx_mux_text,
+ slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx1, SND_SOC_NOPM, 0, slim_rx_mux_text,
+ slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx2, SND_SOC_NOPM, 0, slim_rx_mux_text,
+ slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx3, SND_SOC_NOPM, 0, slim_rx_mux_text,
+ slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx4, SND_SOC_NOPM, 0, slim_rx_mux_text,
+ slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx5, SND_SOC_NOPM, 0, slim_rx_mux_text,
+ slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx6, SND_SOC_NOPM, 0, slim_rx_mux_text,
+ slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx7, SND_SOC_NOPM, 0, slim_rx_mux_text,
+ slim_rx_mux_get, slim_rx_mux_put);
+
+WCD_DAPM_ENUM(cdc_if_rx0, SND_SOC_NOPM, 0, cdc_if_rx0_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx1, SND_SOC_NOPM, 0, cdc_if_rx1_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx2, SND_SOC_NOPM, 0, cdc_if_rx2_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx3, SND_SOC_NOPM, 0, cdc_if_rx3_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx4, SND_SOC_NOPM, 0, cdc_if_rx4_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx5, SND_SOC_NOPM, 0, cdc_if_rx5_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx6, SND_SOC_NOPM, 0, cdc_if_rx6_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx7, SND_SOC_NOPM, 0, cdc_if_rx7_mux_text);
+
+WCD_DAPM_ENUM(rx_int0_2, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0,
+ rx_int0_7_mix_mux_text);
+WCD_DAPM_ENUM(rx_int1_2, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0,
+ rx_int_mix_mux_text);
+WCD_DAPM_ENUM(rx_int2_2, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0,
+ rx_int_mix_mux_text);
+WCD_DAPM_ENUM(rx_int3_2, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0,
+ rx_int_mix_mux_text);
+WCD_DAPM_ENUM(rx_int4_2, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0,
+ rx_int_mix_mux_text);
+WCD_DAPM_ENUM(rx_int7_2, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0,
+ rx_int0_7_mix_mux_text);
+WCD_DAPM_ENUM(rx_int8_2, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0,
+ rx_int_mix_mux_text);
+
+WCD_DAPM_ENUM(rx_int0_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int0_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int0_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int1_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int1_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int1_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int2_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int2_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int2_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int3_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int3_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int3_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int4_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int4_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int4_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int7_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int7_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int7_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int8_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int8_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 4,
+ rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int8_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 4,
+ rx_prim_mix_text);
+
+WCD_DAPM_ENUM(rx_int0_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0,
+ rx_sidetone_mix_text);
+WCD_DAPM_ENUM(rx_int1_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2,
+ rx_sidetone_mix_text);
+WCD_DAPM_ENUM(rx_int2_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4,
+ rx_sidetone_mix_text);
+WCD_DAPM_ENUM(rx_int3_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6,
+ rx_sidetone_mix_text);
+WCD_DAPM_ENUM(rx_int4_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0,
+ rx_sidetone_mix_text);
+WCD_DAPM_ENUM(rx_int7_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2,
+ rx_sidetone_mix_text);
+
+WCD_DAPM_ENUM(tx_adc_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 4,
+ adc_mux_text);
+WCD_DAPM_ENUM(tx_adc_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 4,
+ adc_mux_text);
+WCD_DAPM_ENUM(tx_adc_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 4,
+ adc_mux_text);
+WCD_DAPM_ENUM(tx_adc_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 6,
+ adc_mux_text);
+
+
+WCD_DAPM_ENUM(tx_dmic_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 3,
+ dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 3,
+ dmic_mux_text);
+
+
+WCD_DAPM_ENUM(tx_amic_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0,
+ amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0,
+ amic_mux_text);
+
+WCD_DAPM_ENUM(tx_amic4_5, WCD934X_TX_NEW_AMIC_4_5_SEL, 7, amic4_5_sel_text);
+
+WCD_DAPM_ENUM(cdc_if_tx0, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0,
+ cdc_if_tx0_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 2,
+ cdc_if_tx1_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx2, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 4,
+ cdc_if_tx2_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx3, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 6,
+ cdc_if_tx3_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx4, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0,
+ cdc_if_tx4_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx5, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 2,
+ cdc_if_tx5_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx6, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 4,
+ cdc_if_tx6_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx7, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 6,
+ cdc_if_tx7_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx8, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0,
+ cdc_if_tx8_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx9, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 2,
+ cdc_if_tx9_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx10, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 4,
+ cdc_if_tx10_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx11_inp1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0,
+ cdc_if_tx11_inp1_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx11, WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0,
+ cdc_if_tx11_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx13_inp1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 4,
+ cdc_if_tx13_inp1_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx13, WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0,
+ cdc_if_tx13_mux_text);
+
+WCD_DAPM_ENUM(rx_mix_tx0, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 0,
+ rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx1, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 4,
+ rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx2, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 0,
+ rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx3, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 4,
+ rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx4, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 0,
+ rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx5, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 4,
+ rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx6, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 0,
+ rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx7, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 4,
+ rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx8, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4, 0,
+ rx_echo_mux_text);
+
+WCD_DAPM_ENUM(iir0_inp0, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0,
+ iir_inp_mux_text);
+WCD_DAPM_ENUM(iir0_inp1, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0,
+ iir_inp_mux_text);
+WCD_DAPM_ENUM(iir0_inp2, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0,
+ iir_inp_mux_text);
+WCD_DAPM_ENUM(iir0_inp3, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0,
+ iir_inp_mux_text);
+WCD_DAPM_ENUM(iir1_inp0, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0,
+ iir_inp_mux_text);
+WCD_DAPM_ENUM(iir1_inp1, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0,
+ iir_inp_mux_text);
+WCD_DAPM_ENUM(iir1_inp2, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0,
+ iir_inp_mux_text);
+WCD_DAPM_ENUM(iir1_inp3, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0,
+ iir_inp_mux_text);
+
+WCD_DAPM_ENUM(rx_int0_1_interp, SND_SOC_NOPM, 0, rx_int0_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int1_1_interp, SND_SOC_NOPM, 0, rx_int1_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int2_1_interp, SND_SOC_NOPM, 0, rx_int2_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int3_1_interp, SND_SOC_NOPM, 0, rx_int3_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int4_1_interp, SND_SOC_NOPM, 0, rx_int4_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int7_1_interp, SND_SOC_NOPM, 0, rx_int7_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int8_1_interp, SND_SOC_NOPM, 0, rx_int8_1_interp_mux_text);
+
+WCD_DAPM_ENUM(rx_int0_2_interp, SND_SOC_NOPM, 0, rx_int0_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int1_2_interp, SND_SOC_NOPM, 0, rx_int1_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int2_2_interp, SND_SOC_NOPM, 0, rx_int2_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int3_2_interp, SND_SOC_NOPM, 0, rx_int3_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int4_2_interp, SND_SOC_NOPM, 0, rx_int4_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int7_2_interp, SND_SOC_NOPM, 0, rx_int7_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int8_2_interp, SND_SOC_NOPM, 0, rx_int8_2_interp_mux_text);
+
+WCD_DAPM_ENUM(mad_sel, WCD934X_CPE_SS_SVA_CFG, 0,
+ mad_sel_txt);
+
+WCD_DAPM_ENUM(mad_inp_mux, WCD934X_CPE_SS_SVA_CFG, 2,
+ mad_inp_mux_txt);
+
+WCD_DAPM_ENUM_EXT(rx_int0_dem_inp, WCD934X_CDC_RX0_RX_PATH_SEC0, 0,
+ rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double,
+ tavil_int_dem_inp_mux_put);
+WCD_DAPM_ENUM_EXT(rx_int1_dem_inp, WCD934X_CDC_RX1_RX_PATH_SEC0, 0,
+ rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double,
+ tavil_int_dem_inp_mux_put);
+WCD_DAPM_ENUM_EXT(rx_int2_dem_inp, WCD934X_CDC_RX2_RX_PATH_SEC0, 0,
+ rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double,
+ tavil_int_dem_inp_mux_put);
+
+WCD_DAPM_ENUM_EXT(tx_adc_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0,
+ adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0,
+ adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0,
+ adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0,
+ adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 2,
+ adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 2,
+ adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 2,
+ adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2,
+ adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 4,
+ adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+
+WCD_DAPM_ENUM(asrc0, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0,
+ asrc0_mux_text);
+WCD_DAPM_ENUM(asrc1, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 2,
+ asrc1_mux_text);
+WCD_DAPM_ENUM(asrc2, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 4,
+ asrc2_mux_text);
+WCD_DAPM_ENUM(asrc3, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 6,
+ asrc3_mux_text);
+
+WCD_DAPM_ENUM(int1_1_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int2_1_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int3_1_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int4_1_native, SND_SOC_NOPM, 0, native_mux_text);
+
+WCD_DAPM_ENUM(int1_2_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int2_2_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int3_2_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int4_2_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int7_2_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text);
+
+WCD_DAPM_ENUM(anc0_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text);
+WCD_DAPM_ENUM(anc1_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 3, anc1_fb_mux_text);
+
+static const struct snd_kcontrol_new anc_ear_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_ear_spkr_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_spkr_pa_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_hphl_pa_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_hphr_pa_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new mad_cpe1_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new mad_cpe2_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new mad_brdcst_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux0_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux1_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux2_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux3_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux4_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux5_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux6_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux7_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux8_switch =
+ SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new rx_int1_asrc_switch[] = {
+ SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new rx_int2_asrc_switch[] = {
+ SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new rx_int3_asrc_switch[] = {
+ SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new rx_int4_asrc_switch[] = {
+ SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static int tavil_dsd_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config;
+ int val;
+
+ val = tavil_dsd_get_current_mixer_value(dsd_conf, mc->shift);
+
+ ucontrol->value.integer.value[0] = ((val < 0) ? 0 : val);
+
+ return 0;
+}
+
+static int tavil_dsd_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+ unsigned int wval = ucontrol->value.integer.value[0];
+ struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config;
+
+ if (!dsd_conf)
+ return 0;
+
+ mutex_lock(&tavil_p->codec_mutex);
+
+ tavil_dsd_set_out_select(dsd_conf, mc->shift);
+ tavil_dsd_set_mixer_value(dsd_conf, mc->shift, wval);
+
+ mutex_unlock(&tavil_p->codec_mutex);
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, wval, NULL);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new hphl_mixer[] = {
+ SOC_SINGLE_EXT("DSD HPHL Switch", SND_SOC_NOPM, INTERP_HPHL, 1, 0,
+ tavil_dsd_mixer_get, tavil_dsd_mixer_put),
+};
+
+static const struct snd_kcontrol_new hphr_mixer[] = {
+ SOC_SINGLE_EXT("DSD HPHR Switch", SND_SOC_NOPM, INTERP_HPHR, 1, 0,
+ tavil_dsd_mixer_get, tavil_dsd_mixer_put),
+};
+
+static const struct snd_kcontrol_new lo1_mixer[] = {
+ SOC_SINGLE_EXT("DSD LO1 Switch", SND_SOC_NOPM, INTERP_LO1, 1, 0,
+ tavil_dsd_mixer_get, tavil_dsd_mixer_put),
+};
+
+static const struct snd_kcontrol_new lo2_mixer[] = {
+ SOC_SINGLE_EXT("DSD LO2 Switch", SND_SOC_NOPM, INTERP_LO2, 1, 0,
+ tavil_dsd_mixer_get, tavil_dsd_mixer_put),
+};
+
+static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+ AIF1_PB, 0, tavil_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+ AIF2_PB, 0, tavil_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+ AIF3_PB, 0, tavil_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM,
+ AIF4_PB, 0, tavil_codec_enable_slimrx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ WCD_DAPM_MUX("SLIM RX0 MUX", WCD934X_RX0, slim_rx0),
+ WCD_DAPM_MUX("SLIM RX1 MUX", WCD934X_RX1, slim_rx1),
+ WCD_DAPM_MUX("SLIM RX2 MUX", WCD934X_RX2, slim_rx2),
+ WCD_DAPM_MUX("SLIM RX3 MUX", WCD934X_RX3, slim_rx3),
+ WCD_DAPM_MUX("SLIM RX4 MUX", WCD934X_RX4, slim_rx4),
+ WCD_DAPM_MUX("SLIM RX5 MUX", WCD934X_RX5, slim_rx5),
+ WCD_DAPM_MUX("SLIM RX6 MUX", WCD934X_RX6, slim_rx6),
+ WCD_DAPM_MUX("SLIM RX7 MUX", WCD934X_RX7, slim_rx7),
+
+ SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ WCD_DAPM_MUX("CDC_IF RX0 MUX", WCD934X_RX0, cdc_if_rx0),
+ WCD_DAPM_MUX("CDC_IF RX1 MUX", WCD934X_RX1, cdc_if_rx1),
+ WCD_DAPM_MUX("CDC_IF RX2 MUX", WCD934X_RX2, cdc_if_rx2),
+ WCD_DAPM_MUX("CDC_IF RX3 MUX", WCD934X_RX3, cdc_if_rx3),
+ WCD_DAPM_MUX("CDC_IF RX4 MUX", WCD934X_RX4, cdc_if_rx4),
+ WCD_DAPM_MUX("CDC_IF RX5 MUX", WCD934X_RX5, cdc_if_rx5),
+ WCD_DAPM_MUX("CDC_IF RX6 MUX", WCD934X_RX6, cdc_if_rx6),
+ WCD_DAPM_MUX("CDC_IF RX7 MUX", WCD934X_RX7, cdc_if_rx7),
+
+ SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_EAR, 0,
+ &rx_int0_2_mux, tavil_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0,
+ &rx_int1_2_mux, tavil_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0,
+ &rx_int2_2_mux, tavil_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", SND_SOC_NOPM, INTERP_LO1, 0,
+ &rx_int3_2_mux, tavil_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", SND_SOC_NOPM, INTERP_LO2, 0,
+ &rx_int4_2_mux, tavil_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", SND_SOC_NOPM, INTERP_SPKR1, 0,
+ &rx_int7_2_mux, tavil_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", SND_SOC_NOPM, INTERP_SPKR2, 0,
+ &rx_int8_2_mux, tavil_codec_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ WCD_DAPM_MUX("RX INT0_1 MIX1 INP0", 0, rx_int0_1_mix_inp0),
+ WCD_DAPM_MUX("RX INT0_1 MIX1 INP1", 0, rx_int0_1_mix_inp1),
+ WCD_DAPM_MUX("RX INT0_1 MIX1 INP2", 0, rx_int0_1_mix_inp2),
+ WCD_DAPM_MUX("RX INT1_1 MIX1 INP0", 0, rx_int1_1_mix_inp0),
+ WCD_DAPM_MUX("RX INT1_1 MIX1 INP1", 0, rx_int1_1_mix_inp1),
+ WCD_DAPM_MUX("RX INT1_1 MIX1 INP2", 0, rx_int1_1_mix_inp2),
+ WCD_DAPM_MUX("RX INT2_1 MIX1 INP0", 0, rx_int2_1_mix_inp0),
+ WCD_DAPM_MUX("RX INT2_1 MIX1 INP1", 0, rx_int2_1_mix_inp1),
+ WCD_DAPM_MUX("RX INT2_1 MIX1 INP2", 0, rx_int2_1_mix_inp2),
+ WCD_DAPM_MUX("RX INT3_1 MIX1 INP0", 0, rx_int3_1_mix_inp0),
+ WCD_DAPM_MUX("RX INT3_1 MIX1 INP1", 0, rx_int3_1_mix_inp1),
+ WCD_DAPM_MUX("RX INT3_1 MIX1 INP2", 0, rx_int3_1_mix_inp2),
+ WCD_DAPM_MUX("RX INT4_1 MIX1 INP0", 0, rx_int4_1_mix_inp0),
+ WCD_DAPM_MUX("RX INT4_1 MIX1 INP1", 0, rx_int4_1_mix_inp1),
+ WCD_DAPM_MUX("RX INT4_1 MIX1 INP2", 0, rx_int4_1_mix_inp2),
+
+ SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp0_mux, tavil_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp1_mux, tavil_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int7_1_mix_inp2_mux, tavil_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp0_mux, tavil_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp1_mux, tavil_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+ &rx_int8_1_mix_inp2_mux, tavil_codec_enable_swr,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0,
+ rx_int1_asrc_switch, ARRAY_SIZE(rx_int1_asrc_switch)),
+ SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0,
+ rx_int2_asrc_switch, ARRAY_SIZE(rx_int2_asrc_switch)),
+ SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0,
+ rx_int3_asrc_switch, ARRAY_SIZE(rx_int3_asrc_switch)),
+ SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0,
+ rx_int4_asrc_switch, ARRAY_SIZE(rx_int4_asrc_switch)),
+ SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT1 MIX3", SND_SOC_NOPM, 0, 0, hphl_mixer,
+ ARRAY_SIZE(hphl_mixer)),
+ SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT2 MIX3", SND_SOC_NOPM, 0, 0, hphr_mixer,
+ ARRAY_SIZE(hphr_mixer)),
+ SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT3 MIX3", SND_SOC_NOPM, 0, 0, lo1_mixer,
+ ARRAY_SIZE(lo1_mixer)),
+ SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX INT4 MIX3", SND_SOC_NOPM, 0, 0, lo2_mixer,
+ ARRAY_SIZE(lo2_mixer)),
+ SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, tavil_codec_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0,
+ NULL, 0, tavil_codec_spk_boost_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", SND_SOC_NOPM, INTERP_EAR,
+ 0, &rx_int0_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", SND_SOC_NOPM, INTERP_HPHL,
+ 0, &rx_int1_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", SND_SOC_NOPM, INTERP_HPHR,
+ 0, &rx_int2_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT3 MIX2 INP", SND_SOC_NOPM, INTERP_LO1,
+ 0, &rx_int3_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT4 MIX2 INP", SND_SOC_NOPM, INTERP_LO2,
+ 0, &rx_int4_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7 MIX2 INP", SND_SOC_NOPM, INTERP_SPKR1,
+ 0, &rx_int7_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ WCD_DAPM_MUX("CDC_IF TX0 MUX", WCD934X_TX0, cdc_if_tx0),
+ WCD_DAPM_MUX("CDC_IF TX1 MUX", WCD934X_TX1, cdc_if_tx1),
+ WCD_DAPM_MUX("CDC_IF TX2 MUX", WCD934X_TX2, cdc_if_tx2),
+ WCD_DAPM_MUX("CDC_IF TX3 MUX", WCD934X_TX3, cdc_if_tx3),
+ WCD_DAPM_MUX("CDC_IF TX4 MUX", WCD934X_TX4, cdc_if_tx4),
+ WCD_DAPM_MUX("CDC_IF TX5 MUX", WCD934X_TX5, cdc_if_tx5),
+ WCD_DAPM_MUX("CDC_IF TX6 MUX", WCD934X_TX6, cdc_if_tx6),
+ WCD_DAPM_MUX("CDC_IF TX7 MUX", WCD934X_TX7, cdc_if_tx7),
+ WCD_DAPM_MUX("CDC_IF TX8 MUX", WCD934X_TX8, cdc_if_tx8),
+ WCD_DAPM_MUX("CDC_IF TX9 MUX", WCD934X_TX9, cdc_if_tx9),
+ WCD_DAPM_MUX("CDC_IF TX10 MUX", WCD934X_TX10, cdc_if_tx10),
+ WCD_DAPM_MUX("CDC_IF TX11 MUX", WCD934X_TX11, cdc_if_tx11),
+ WCD_DAPM_MUX("CDC_IF TX11 INP1 MUX", WCD934X_TX11, cdc_if_tx11_inp1),
+ WCD_DAPM_MUX("CDC_IF TX13 MUX", WCD934X_TX13, cdc_if_tx13),
+ WCD_DAPM_MUX("CDC_IF TX13 INP1 MUX", WCD934X_TX13, cdc_if_tx13_inp1),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX0", WCD934X_CDC_TX0_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux0_mux, tavil_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX1", WCD934X_CDC_TX1_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux1_mux, tavil_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX2", WCD934X_CDC_TX2_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux2_mux, tavil_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX3", WCD934X_CDC_TX3_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux3_mux, tavil_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX4", WCD934X_CDC_TX4_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux4_mux, tavil_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX5", WCD934X_CDC_TX5_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux5_mux, tavil_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX6", WCD934X_CDC_TX6_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux6_mux, tavil_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX7", WCD934X_CDC_TX7_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux7_mux, tavil_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX8", WCD934X_CDC_TX8_TX_PATH_CTL, 5, 0,
+ &tx_adc_mux8_mux, tavil_codec_enable_dec,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX10", SND_SOC_NOPM, 10, 0, &tx_adc_mux10_mux,
+ tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX11", SND_SOC_NOPM, 11, 0, &tx_adc_mux11_mux,
+ tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX12", SND_SOC_NOPM, 12, 0, &tx_adc_mux12_mux,
+ tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX_E("ADC MUX13", SND_SOC_NOPM, 13, 0, &tx_adc_mux13_mux,
+ tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU),
+
+ WCD_DAPM_MUX("DMIC MUX0", 0, tx_dmic_mux0),
+ WCD_DAPM_MUX("DMIC MUX1", 0, tx_dmic_mux1),
+ WCD_DAPM_MUX("DMIC MUX2", 0, tx_dmic_mux2),
+ WCD_DAPM_MUX("DMIC MUX3", 0, tx_dmic_mux3),
+ WCD_DAPM_MUX("DMIC MUX4", 0, tx_dmic_mux4),
+ WCD_DAPM_MUX("DMIC MUX5", 0, tx_dmic_mux5),
+ WCD_DAPM_MUX("DMIC MUX6", 0, tx_dmic_mux6),
+ WCD_DAPM_MUX("DMIC MUX7", 0, tx_dmic_mux7),
+ WCD_DAPM_MUX("DMIC MUX8", 0, tx_dmic_mux8),
+ WCD_DAPM_MUX("DMIC MUX10", 0, tx_dmic_mux10),
+ WCD_DAPM_MUX("DMIC MUX11", 0, tx_dmic_mux11),
+ WCD_DAPM_MUX("DMIC MUX12", 0, tx_dmic_mux12),
+ WCD_DAPM_MUX("DMIC MUX13", 0, tx_dmic_mux13),
+
+ WCD_DAPM_MUX("AMIC MUX0", 0, tx_amic_mux0),
+ WCD_DAPM_MUX("AMIC MUX1", 0, tx_amic_mux1),
+ WCD_DAPM_MUX("AMIC MUX2", 0, tx_amic_mux2),
+ WCD_DAPM_MUX("AMIC MUX3", 0, tx_amic_mux3),
+ WCD_DAPM_MUX("AMIC MUX4", 0, tx_amic_mux4),
+ WCD_DAPM_MUX("AMIC MUX5", 0, tx_amic_mux5),
+ WCD_DAPM_MUX("AMIC MUX6", 0, tx_amic_mux6),
+ WCD_DAPM_MUX("AMIC MUX7", 0, tx_amic_mux7),
+ WCD_DAPM_MUX("AMIC MUX8", 0, tx_amic_mux8),
+ WCD_DAPM_MUX("AMIC MUX10", 0, tx_amic_mux10),
+ WCD_DAPM_MUX("AMIC MUX11", 0, tx_amic_mux11),
+ WCD_DAPM_MUX("AMIC MUX12", 0, tx_amic_mux12),
+ WCD_DAPM_MUX("AMIC MUX13", 0, tx_amic_mux13),
+
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD934X_ANA_AMIC1, 7, 0,
+ tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD934X_ANA_AMIC2, 7, 0,
+ tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD934X_ANA_AMIC3, 7, 0,
+ tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD934X_ANA_AMIC4, 7, 0,
+ tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+
+ WCD_DAPM_MUX("AMIC4_5 SEL", 0, tx_amic4_5),
+
+ WCD_DAPM_MUX("ANC0 FB MUX", 0, anc0_fb),
+ WCD_DAPM_MUX("ANC1 FB MUX", 0, anc1_fb),
+
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("AMIC4"),
+ SND_SOC_DAPM_INPUT("AMIC5"),
+
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0,
+ tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS2", SND_SOC_NOPM, 0, 0,
+ tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS3", SND_SOC_NOPM, 0, 0,
+ tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS4", SND_SOC_NOPM, 0, 0,
+ tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /*
+ * Not supply widget, this is used to recover HPH registers.
+ * It is not connected to any other widgets
+ */
+ SND_SOC_DAPM_SUPPLY("RESET_HPH_REGISTERS", SND_SOC_NOPM,
+ 0, 0, tavil_codec_reset_hph_registers,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0,
+ tavil_codec_force_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_STANDALONE, SND_SOC_NOPM, 0, 0,
+ tavil_codec_force_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_STANDALONE, SND_SOC_NOPM, 0, 0,
+ tavil_codec_force_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS4_STANDALONE, SND_SOC_NOPM, 0, 0,
+ tavil_codec_force_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+ AIF1_CAP, 0, tavil_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+ AIF2_CAP, 0, tavil_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+ AIF3_CAP, 0, tavil_codec_enable_slimtx,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+ aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)),
+ SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+ aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)),
+ SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+ aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)),
+ SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0,
+ aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)),
+
+ SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM,
+ AIF4_VIFEED, 0, tavil_codec_enable_slimvi_feedback,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0,
+ aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)),
+ SND_SOC_DAPM_INPUT("VIINPUT"),
+
+ SND_SOC_DAPM_MIXER("SLIM TX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX7", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX8", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX9", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX10", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX11", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SLIM TX13", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Mic Inputs */
+ SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0,
+ tavil_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+ tavil_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+ tavil_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0,
+ tavil_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0,
+ tavil_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0,
+ tavil_codec_enable_dmic,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ WCD_DAPM_MUX("IIR0 INP0 MUX", 0, iir0_inp0),
+ WCD_DAPM_MUX("IIR0 INP1 MUX", 0, iir0_inp1),
+ WCD_DAPM_MUX("IIR0 INP2 MUX", 0, iir0_inp2),
+ WCD_DAPM_MUX("IIR0 INP3 MUX", 0, iir0_inp3),
+ WCD_DAPM_MUX("IIR1 INP0 MUX", 0, iir1_inp0),
+ WCD_DAPM_MUX("IIR1 INP1 MUX", 0, iir1_inp1),
+ WCD_DAPM_MUX("IIR1 INP2 MUX", 0, iir1_inp2),
+ WCD_DAPM_MUX("IIR1 INP3 MUX", 0, iir1_inp3),
+
+ SND_SOC_DAPM_MIXER_E("IIR0", WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL,
+ 4, 0, NULL, 0, tavil_codec_set_iir_gain,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER_E("IIR1", WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL,
+ 4, 0, NULL, 0, tavil_codec_set_iir_gain,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER("SRC0", WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SRC1", WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL,
+ 4, 0, NULL, 0),
+
+ WCD_DAPM_MUX("RX MIX TX0 MUX", 0, rx_mix_tx0),
+ WCD_DAPM_MUX("RX MIX TX1 MUX", 0, rx_mix_tx1),
+ WCD_DAPM_MUX("RX MIX TX2 MUX", 0, rx_mix_tx2),
+ WCD_DAPM_MUX("RX MIX TX3 MUX", 0, rx_mix_tx3),
+ WCD_DAPM_MUX("RX MIX TX4 MUX", 0, rx_mix_tx4),
+ WCD_DAPM_MUX("RX MIX TX5 MUX", 0, rx_mix_tx5),
+ WCD_DAPM_MUX("RX MIX TX6 MUX", 0, rx_mix_tx6),
+ WCD_DAPM_MUX("RX MIX TX7 MUX", 0, rx_mix_tx7),
+ WCD_DAPM_MUX("RX MIX TX8 MUX", 0, rx_mix_tx8),
+ WCD_DAPM_MUX("RX INT0 DEM MUX", 0, rx_int0_dem_inp),
+ WCD_DAPM_MUX("RX INT1 DEM MUX", 0, rx_int1_dem_inp),
+ WCD_DAPM_MUX("RX INT2 DEM MUX", 0, rx_int2_dem_inp),
+
+ SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_EAR, 0,
+ &rx_int0_1_interp_mux, tavil_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0,
+ &rx_int1_1_interp_mux, tavil_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0,
+ &rx_int2_1_interp_mux, tavil_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT3_1 INTERP", SND_SOC_NOPM, INTERP_LO1, 0,
+ &rx_int3_1_interp_mux, tavil_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT4_1 INTERP", SND_SOC_NOPM, INTERP_LO2, 0,
+ &rx_int4_1_interp_mux, tavil_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT7_1 INTERP", SND_SOC_NOPM, INTERP_SPKR1, 0,
+ &rx_int7_1_interp_mux, tavil_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("RX INT8_1 INTERP", SND_SOC_NOPM, INTERP_SPKR2, 0,
+ &rx_int8_1_interp_mux, tavil_codec_enable_main_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ WCD_DAPM_MUX("RX INT0_2 INTERP", 0, rx_int0_2_interp),
+ WCD_DAPM_MUX("RX INT1_2 INTERP", 0, rx_int1_2_interp),
+ WCD_DAPM_MUX("RX INT2_2 INTERP", 0, rx_int2_2_interp),
+ WCD_DAPM_MUX("RX INT3_2 INTERP", 0, rx_int3_2_interp),
+ WCD_DAPM_MUX("RX INT4_2 INTERP", 0, rx_int4_2_interp),
+ WCD_DAPM_MUX("RX INT7_2 INTERP", 0, rx_int7_2_interp),
+ WCD_DAPM_MUX("RX INT8_2 INTERP", 0, rx_int8_2_interp),
+
+ SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD934X_CDC_TX0_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux0_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD934X_CDC_TX1_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux1_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX2", WCD934X_CDC_TX2_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux2_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX3", WCD934X_CDC_TX3_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux3_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX4", WCD934X_CDC_TX4_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux4_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX5", WCD934X_CDC_TX5_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux5_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX6", WCD934X_CDC_TX6_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux6_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX7", WCD934X_CDC_TX7_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux7_switch),
+ SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD934X_CDC_TX8_TX_PATH_192_CTL, 0,
+ 0, &adc_us_mux8_switch),
+
+ /* MAD related widgets */
+ SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"),
+ SND_SOC_DAPM_INPUT("MADINPUT"),
+
+ WCD_DAPM_MUX("MAD_SEL MUX", 0, mad_sel),
+ WCD_DAPM_MUX("MAD_INP MUX", 0, mad_inp_mux),
+
+ SND_SOC_DAPM_SWITCH_E("MAD_BROADCAST", SND_SOC_NOPM, 0, 0,
+ &mad_brdcst_switch, tavil_codec_ape_enable_mad,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SWITCH_E("MAD_CPE1", SND_SOC_NOPM, 0, 0,
+ &mad_cpe1_switch, tavil_codec_cpe_mad_ctl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SWITCH_E("MAD_CPE2", SND_SOC_NOPM, 0, 0,
+ &mad_cpe2_switch, tavil_codec_cpe_mad_ctl,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT1"),
+ SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT2"),
+
+ SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tavil_codec_ear_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tavil_codec_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tavil_codec_hphr_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tavil_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM,
+ 0, 0, tavil_codec_lineout_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0,
+ tavil_codec_enable_ear_pa,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tavil_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tavil_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD934X_ANA_LO_1_2, 7, 0, NULL, 0,
+ tavil_codec_enable_lineout_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD934X_ANA_LO_1_2, 6, 0, NULL, 0,
+ tavil_codec_enable_lineout_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0,
+ tavil_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tavil_codec_enable_spkr_anc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tavil_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tavil_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("HPHL"),
+ SND_SOC_DAPM_OUTPUT("HPHR"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+ SND_SOC_DAPM_OUTPUT("SPK1 OUT"),
+ SND_SOC_DAPM_OUTPUT("SPK2 OUT"),
+ SND_SOC_DAPM_OUTPUT("ANC EAR"),
+ SND_SOC_DAPM_OUTPUT("ANC HPHL"),
+ SND_SOC_DAPM_OUTPUT("ANC HPHR"),
+
+ SND_SOC_DAPM_SWITCH("ANC OUT EAR Enable", SND_SOC_NOPM, 0, 0,
+ &anc_ear_switch),
+ SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0,
+ &anc_ear_spkr_switch),
+ SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0,
+ &anc_spkr_pa_switch),
+
+ SND_SOC_DAPM_SWITCH_E("ANC OUT HPHL Enable", SND_SOC_NOPM, INTERP_HPHL,
+ 0, &anc_hphl_pa_switch, tavil_anc_out_switch_cb,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SWITCH_E("ANC OUT HPHR Enable", SND_SOC_NOPM, INTERP_HPHR,
+ 0, &anc_hphr_pa_switch, tavil_anc_out_switch_cb,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0,
+ tavil_codec_enable_rx_bias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("RX INT1 NATIVE SUPPLY", SND_SOC_NOPM,
+ INTERP_HPHL, 0, tavil_enable_native_supply,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("RX INT2 NATIVE SUPPLY", SND_SOC_NOPM,
+ INTERP_HPHR, 0, tavil_enable_native_supply,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("RX INT3 NATIVE SUPPLY", SND_SOC_NOPM,
+ INTERP_LO1, 0, tavil_enable_native_supply,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("RX INT4 NATIVE SUPPLY", SND_SOC_NOPM,
+ INTERP_LO2, 0, tavil_enable_native_supply,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("RX INT7 NATIVE SUPPLY", SND_SOC_NOPM,
+ INTERP_SPKR1, 0, tavil_enable_native_supply,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("RX INT8 NATIVE SUPPLY", SND_SOC_NOPM,
+ INTERP_SPKR2, 0, tavil_enable_native_supply,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ WCD_DAPM_MUX("RX INT1_1 NATIVE MUX", 0, int1_1_native),
+ WCD_DAPM_MUX("RX INT2_1 NATIVE MUX", 0, int2_1_native),
+ WCD_DAPM_MUX("RX INT3_1 NATIVE MUX", 0, int3_1_native),
+ WCD_DAPM_MUX("RX INT4_1 NATIVE MUX", 0, int4_1_native),
+
+ WCD_DAPM_MUX("RX INT1_2 NATIVE MUX", 0, int1_2_native),
+ WCD_DAPM_MUX("RX INT2_2 NATIVE MUX", 0, int2_2_native),
+ WCD_DAPM_MUX("RX INT3_2 NATIVE MUX", 0, int3_2_native),
+ WCD_DAPM_MUX("RX INT4_2 NATIVE MUX", 0, int4_2_native),
+ WCD_DAPM_MUX("RX INT7_2 NATIVE MUX", 0, int7_2_native),
+ WCD_DAPM_MUX("RX INT8_2 NATIVE MUX", 0, int8_2_native),
+
+ SND_SOC_DAPM_MUX_E("ASRC0 MUX", SND_SOC_NOPM, ASRC0, 0,
+ &asrc0_mux, tavil_codec_enable_asrc_resampler,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("ASRC1 MUX", SND_SOC_NOPM, ASRC1, 0,
+ &asrc1_mux, tavil_codec_enable_asrc_resampler,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("ASRC2 MUX", SND_SOC_NOPM, ASRC2, 0,
+ &asrc2_mux, tavil_codec_enable_asrc_resampler,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("ASRC3 MUX", SND_SOC_NOPM, ASRC3, 0,
+ &asrc3_mux, tavil_codec_enable_asrc_resampler,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static int tavil_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec);
+ u32 i = 0;
+ struct wcd9xxx_ch *ch;
+ int ret = 0;
+
+ switch (dai->id) {
+ case AIF1_PB:
+ case AIF2_PB:
+ case AIF3_PB:
+ case AIF4_PB:
+ if (!rx_slot || !rx_num) {
+ dev_err(tavil->dev, "%s: Invalid rx_slot 0x%pK or rx_num 0x%pK\n",
+ __func__, rx_slot, rx_num);
+ ret = -EINVAL;
+ break;
+ }
+ list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ dev_dbg(tavil->dev, "%s: slot_num %u ch->ch_num %d\n",
+ __func__, i, ch->ch_num);
+ rx_slot[i++] = ch->ch_num;
+ }
+ *rx_num = i;
+ dev_dbg(tavil->dev, "%s: dai_name = %s dai_id = %x rx_num = %d\n",
+ __func__, dai->name, dai->id, i);
+ if (*rx_num == 0) {
+ dev_err(tavil->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n",
+ __func__, dai->name, dai->id);
+ ret = -EINVAL;
+ }
+ break;
+ case AIF1_CAP:
+ case AIF2_CAP:
+ case AIF3_CAP:
+ case AIF4_MAD_TX:
+ case AIF4_VIFEED:
+ if (!tx_slot || !tx_num) {
+ dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK or tx_num 0x%pK\n",
+ __func__, tx_slot, tx_num);
+ ret = -EINVAL;
+ break;
+ }
+ list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list,
+ list) {
+ dev_dbg(tavil->dev, "%s: slot_num %u ch->ch_num %d\n",
+ __func__, i, ch->ch_num);
+ tx_slot[i++] = ch->ch_num;
+ }
+ *tx_num = i;
+ dev_dbg(tavil->dev, "%s: dai_name = %s dai_id = %x tx_num = %d\n",
+ __func__, dai->name, dai->id, i);
+ if (*tx_num == 0) {
+ dev_err(tavil->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n",
+ __func__, dai->name, dai->id);
+ ret = -EINVAL;
+ }
+ break;
+ default:
+ dev_err(tavil->dev, "%s: Invalid DAI ID %x\n",
+ __func__, dai->id);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int tavil_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct tavil_priv *tavil;
+ struct wcd9xxx *core;
+ struct wcd9xxx_codec_dai_data *dai_data = NULL;
+
+ tavil = snd_soc_codec_get_drvdata(dai->codec);
+ core = dev_get_drvdata(dai->codec->dev->parent);
+
+ if (!tx_slot || !rx_slot) {
+ dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK, rx_slot 0x%pK\n",
+ __func__, tx_slot, rx_slot);
+ return -EINVAL;
+ }
+ dev_dbg(tavil->dev, "%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
+ __func__, dai->name, dai->id, tx_num, rx_num);
+
+ wcd9xxx_init_slimslave(core, core->slim->laddr,
+ tx_num, tx_slot, rx_num, rx_slot);
+ /* Reserve TX13 for MAD data channel */
+ dai_data = &tavil->dai[AIF4_MAD_TX];
+ if (dai_data)
+ list_add_tail(&core->tx_chs[WCD934X_TX13].list,
+ &dai_data->wcd9xxx_ch_list);
+
+ return 0;
+}
+
+static int tavil_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ return 0;
+}
+
+static void tavil_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+}
+
+static int tavil_set_decimator_rate(struct snd_soc_dai *dai,
+ u32 sample_rate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ u32 tx_port = 0, tx_fs_rate = 0;
+ u8 shift = 0, shift_val = 0, tx_mux_sel = 0;
+ int decimator = -1;
+ u16 tx_port_reg = 0, tx_fs_reg = 0;
+
+ switch (sample_rate) {
+ case 8000:
+ tx_fs_rate = 0;
+ break;
+ case 16000:
+ tx_fs_rate = 1;
+ break;
+ case 32000:
+ tx_fs_rate = 3;
+ break;
+ case 48000:
+ tx_fs_rate = 4;
+ break;
+ case 96000:
+ tx_fs_rate = 5;
+ break;
+ case 192000:
+ tx_fs_rate = 6;
+ break;
+ default:
+ dev_err(tavil->dev, "%s: Invalid TX sample rate: %d\n",
+ __func__, sample_rate);
+ return -EINVAL;
+
+ };
+
+ list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) {
+ tx_port = ch->port;
+ dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d",
+ __func__, dai->id, tx_port);
+
+ if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) {
+ dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n",
+ __func__, tx_port, dai->id);
+ return -EINVAL;
+ }
+ /* Find the SB TX MUX input - which decimator is connected */
+ if (tx_port < 4) {
+ tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0;
+ shift = (tx_port << 1);
+ shift_val = 0x03;
+ } else if ((tx_port >= 4) && (tx_port < 8)) {
+ tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1;
+ shift = ((tx_port - 4) << 1);
+ shift_val = 0x03;
+ } else if ((tx_port >= 8) && (tx_port < 11)) {
+ tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2;
+ shift = ((tx_port - 8) << 1);
+ shift_val = 0x03;
+ } else if (tx_port == 11) {
+ tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3;
+ shift = 0;
+ shift_val = 0x0F;
+ } else if (tx_port == 13) {
+ tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3;
+ shift = 4;
+ shift_val = 0x03;
+ }
+ tx_mux_sel = snd_soc_read(codec, tx_port_reg) &
+ (shift_val << shift);
+ tx_mux_sel = tx_mux_sel >> shift;
+
+ if (tx_port <= 8) {
+ if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3))
+ decimator = tx_port;
+ } else if (tx_port <= 10) {
+ if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+ decimator = ((tx_port == 9) ? 7 : 6);
+ } else if (tx_port == 11) {
+ if ((tx_mux_sel >= 1) && (tx_mux_sel < 7))
+ decimator = tx_mux_sel - 1;
+ } else if (tx_port == 13) {
+ if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+ decimator = 5;
+ }
+
+ if (decimator >= 0) {
+ tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL +
+ 16 * decimator;
+ dev_dbg(codec->dev, "%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
+ __func__, decimator, tx_port, sample_rate);
+ snd_soc_update_bits(codec, tx_fs_reg, 0x0F, tx_fs_rate);
+ } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) {
+ /* Check if the TX Mux input is RX MIX TXn */
+ dev_dbg(codec->dev, "%s: RX_MIX_TX%u going to CDC_IF TX%u\n",
+ __func__, tx_port, tx_port);
+ } else {
+ dev_err(codec->dev, "%s: ERROR: Invalid decimator: %d\n",
+ __func__, decimator);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int tavil_set_mix_interpolator_rate(struct snd_soc_dai *dai,
+ u8 rate_reg_val,
+ u32 sample_rate)
+{
+ u8 int_2_inp;
+ u32 j;
+ u16 int_mux_cfg1, int_fs_reg;
+ u8 int_mux_cfg1_val;
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) {
+ int_2_inp = INTn_2_INP_SEL_RX0 + ch->port -
+ WCD934X_RX_PORT_START_NUMBER;
+ if ((int_2_inp < INTn_2_INP_SEL_RX0) ||
+ (int_2_inp > INTn_2_INP_SEL_RX7)) {
+ dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n",
+ __func__,
+ (ch->port - WCD934X_RX_PORT_START_NUMBER),
+ dai->id);
+ return -EINVAL;
+ }
+
+ int_mux_cfg1 = WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1;
+ for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) {
+ /* Interpolators 5 and 6 are not aviliable in Tavil */
+ if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) {
+ int_mux_cfg1 += 2;
+ continue;
+ }
+ int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) &
+ 0x0F;
+ if (int_mux_cfg1_val == int_2_inp) {
+ /*
+ * Ear mix path supports only 48, 96, 192,
+ * 384KHz only
+ */
+ if ((j == INTERP_EAR) &&
+ (rate_reg_val < 0x4 ||
+ rate_reg_val > 0x7)) {
+ dev_err_ratelimited(codec->dev,
+ "%s: Invalid rate for AIF_PB DAI(%d)\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ int_fs_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL +
+ 20 * j;
+ dev_dbg(codec->dev, "%s: AIF_PB DAI(%d) connected to INT%u_2\n",
+ __func__, dai->id, j);
+ dev_dbg(codec->dev, "%s: set INT%u_2 sample rate to %u\n",
+ __func__, j, sample_rate);
+ snd_soc_update_bits(codec, int_fs_reg, 0x0F,
+ rate_reg_val);
+ }
+ int_mux_cfg1 += 2;
+ }
+ }
+ return 0;
+}
+
+static int tavil_set_prim_interpolator_rate(struct snd_soc_dai *dai,
+ u8 rate_reg_val,
+ u32 sample_rate)
+{
+ u8 int_1_mix1_inp;
+ u32 j;
+ u16 int_mux_cfg0, int_mux_cfg1;
+ u16 int_fs_reg;
+ u8 int_mux_cfg0_val, int_mux_cfg1_val;
+ u8 inp0_sel, inp1_sel, inp2_sel;
+ struct snd_soc_codec *codec = dai->codec;
+ struct wcd9xxx_ch *ch;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
+
+ list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) {
+ int_1_mix1_inp = INTn_1_INP_SEL_RX0 + ch->port -
+ WCD934X_RX_PORT_START_NUMBER;
+ if ((int_1_mix1_inp < INTn_1_INP_SEL_RX0) ||
+ (int_1_mix1_inp > INTn_1_INP_SEL_RX7)) {
+ dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n",
+ __func__,
+ (ch->port - WCD934X_RX_PORT_START_NUMBER),
+ dai->id);
+ return -EINVAL;
+ }
+
+ int_mux_cfg0 = WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0;
+
+ /*
+ * Loop through all interpolator MUX inputs and find out
+ * to which interpolator input, the slim rx port
+ * is connected
+ */
+ for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) {
+ /* Interpolators 5 and 6 are not aviliable in Tavil */
+ if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) {
+ int_mux_cfg0 += 2;
+ continue;
+ }
+ int_mux_cfg1 = int_mux_cfg0 + 1;
+
+ int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0);
+ int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1);
+ inp0_sel = int_mux_cfg0_val & 0x0F;
+ inp1_sel = (int_mux_cfg0_val >> 4) & 0x0F;
+ inp2_sel = (int_mux_cfg1_val >> 4) & 0x0F;
+ if ((inp0_sel == int_1_mix1_inp) ||
+ (inp1_sel == int_1_mix1_inp) ||
+ (inp2_sel == int_1_mix1_inp)) {
+ /*
+ * Ear and speaker primary path does not support
+ * native sample rates
+ */
+ if ((j == INTERP_EAR || j == INTERP_SPKR1 ||
+ j == INTERP_SPKR2) &&
+ (rate_reg_val > 0x7)) {
+ dev_err_ratelimited(codec->dev,
+ "%s: Invalid rate for AIF_PB DAI(%d)\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ int_fs_reg = WCD934X_CDC_RX0_RX_PATH_CTL +
+ 20 * j;
+ dev_dbg(codec->dev,
+ "%s: AIF_PB DAI(%d) connected to INT%u_1\n",
+ __func__, dai->id, j);
+ dev_dbg(codec->dev,
+ "%s: set INT%u_1 sample rate to %u\n",
+ __func__, j, sample_rate);
+ snd_soc_update_bits(codec, int_fs_reg, 0x0F,
+ rate_reg_val);
+ }
+ int_mux_cfg0 += 2;
+ }
+ if (dsd_conf)
+ tavil_dsd_set_interp_rate(dsd_conf, ch->port,
+ sample_rate, rate_reg_val);
+ }
+
+ return 0;
+}
+
+
+static int tavil_set_interpolator_rate(struct snd_soc_dai *dai,
+ u32 sample_rate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int rate_val = 0;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) {
+ if (sample_rate == sr_val_tbl[i].sample_rate) {
+ rate_val = sr_val_tbl[i].rate_val;
+ break;
+ }
+ }
+ if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) {
+ dev_err(codec->dev, "%s: Unsupported sample rate: %d\n",
+ __func__, sample_rate);
+ return -EINVAL;
+ }
+
+ ret = tavil_set_prim_interpolator_rate(dai, (u8)rate_val, sample_rate);
+ if (ret)
+ return ret;
+ ret = tavil_set_mix_interpolator_rate(dai, (u8)rate_val, sample_rate);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static int tavil_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+ return 0;
+}
+
+static int tavil_vi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec);
+
+ dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n",
+ __func__, dai->name, dai->id, params_rate(params),
+ params_channels(params));
+
+ tavil->dai[dai->id].rate = params_rate(params);
+ tavil->dai[dai->id].bit_width = 32;
+
+ return 0;
+}
+
+static int tavil_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec);
+ int ret = 0;
+
+ dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n",
+ __func__, dai->name, dai->id, params_rate(params),
+ params_channels(params));
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ ret = tavil_set_interpolator_rate(dai, params_rate(params));
+ if (ret) {
+ dev_err(tavil->dev, "%s: cannot set sample rate: %u\n",
+ __func__, params_rate(params));
+ return ret;
+ }
+ switch (params_width(params)) {
+ case 16:
+ tavil->dai[dai->id].bit_width = 16;
+ break;
+ case 24:
+ tavil->dai[dai->id].bit_width = 24;
+ break;
+ case 32:
+ tavil->dai[dai->id].bit_width = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ tavil->dai[dai->id].rate = params_rate(params);
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ if (dai->id != AIF4_MAD_TX)
+ ret = tavil_set_decimator_rate(dai,
+ params_rate(params));
+ if (ret) {
+ dev_err(tavil->dev, "%s: cannot set TX Decimator rate: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ switch (params_width(params)) {
+ case 16:
+ tavil->dai[dai->id].bit_width = 16;
+ break;
+ case 24:
+ tavil->dai[dai->id].bit_width = 24;
+ break;
+ default:
+ dev_err(tavil->dev, "%s: Invalid format 0x%x\n",
+ __func__, params_width(params));
+ return -EINVAL;
+ };
+ tavil->dai[dai->id].rate = params_rate(params);
+ break;
+ default:
+ dev_err(tavil->dev, "%s: Invalid stream type %d\n", __func__,
+ substream->stream);
+ return -EINVAL;
+ };
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tavil_dai_ops = {
+ .startup = tavil_startup,
+ .shutdown = tavil_shutdown,
+ .hw_params = tavil_hw_params,
+ .prepare = tavil_prepare,
+ .set_channel_map = tavil_set_channel_map,
+ .get_channel_map = tavil_get_channel_map,
+};
+
+static struct snd_soc_dai_ops tavil_vi_dai_ops = {
+ .hw_params = tavil_vi_hw_params,
+ .set_channel_map = tavil_set_channel_map,
+ .get_channel_map = tavil_get_channel_map,
+};
+
+static struct snd_soc_dai_driver tavil_dai[] = {
+ {
+ .name = "tavil_rx1",
+ .id = AIF1_PB,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+ .formats = WCD934X_FORMATS_S16_S24_S32_LE,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tavil_dai_ops,
+ },
+ {
+ .name = "tavil_tx1",
+ .id = AIF1_CAP,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .rates = WCD934X_RATES_MASK,
+ .formats = WCD934X_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tavil_dai_ops,
+ },
+ {
+ .name = "tavil_rx2",
+ .id = AIF2_PB,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+ .formats = WCD934X_FORMATS_S16_S24_S32_LE,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tavil_dai_ops,
+ },
+ {
+ .name = "tavil_tx2",
+ .id = AIF2_CAP,
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .rates = WCD934X_RATES_MASK,
+ .formats = WCD934X_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tavil_dai_ops,
+ },
+ {
+ .name = "tavil_rx3",
+ .id = AIF3_PB,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+ .formats = WCD934X_FORMATS_S16_S24_S32_LE,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tavil_dai_ops,
+ },
+ {
+ .name = "tavil_tx3",
+ .id = AIF3_CAP,
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .rates = WCD934X_RATES_MASK,
+ .formats = WCD934X_FORMATS_S16_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tavil_dai_ops,
+ },
+ {
+ .name = "tavil_rx4",
+ .id = AIF4_PB,
+ .playback = {
+ .stream_name = "AIF4 Playback",
+ .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+ .formats = WCD934X_FORMATS_S16_S24_S32_LE,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tavil_dai_ops,
+ },
+ {
+ .name = "tavil_vifeedback",
+ .id = AIF4_VIFEED,
+ .capture = {
+ .stream_name = "VIfeed",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+ .formats = WCD934X_FORMATS_S16_S24_S32_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &tavil_vi_dai_ops,
+ },
+ {
+ .name = "tavil_mad1",
+ .id = AIF4_MAD_TX,
+ .capture = {
+ .stream_name = "AIF4 MAD TX",
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = WCD934X_FORMATS_S16_LE,
+ .rate_min = 16000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ .ops = &tavil_dai_ops,
+ },
+};
+
+static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil)
+{
+ mutex_lock(&tavil->power_lock);
+ dev_dbg(tavil->dev, "%s: Entering power gating function, %d\n",
+ __func__, tavil->power_active_ref);
+
+ if (tavil->power_active_ref > 0)
+ goto exit;
+
+ wcd9xxx_set_power_state(tavil->wcd9xxx,
+ WCD_REGION_POWER_COLLAPSE_BEGIN,
+ WCD9XXX_DIG_CORE_REGION_1);
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x04, 0x04);
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x01, 0x00);
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x02, 0x00);
+ wcd9xxx_set_power_state(tavil->wcd9xxx, WCD_REGION_POWER_DOWN,
+ WCD9XXX_DIG_CORE_REGION_1);
+exit:
+ dev_dbg(tavil->dev, "%s: Exiting power gating function, %d\n",
+ __func__, tavil->power_active_ref);
+ mutex_unlock(&tavil->power_lock);
+}
+
+static void tavil_codec_power_gate_work(struct work_struct *work)
+{
+ struct tavil_priv *tavil;
+ struct delayed_work *dwork;
+
+ dwork = to_delayed_work(work);
+ tavil = container_of(dwork, struct tavil_priv, power_gate_work);
+
+ tavil_codec_power_gate_digital_core(tavil);
+}
+
+/* called under power_lock acquisition */
+static int tavil_dig_core_remove_power_collapse(struct tavil_priv *tavil)
+{
+ regmap_write(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x05);
+ regmap_write(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x07);
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x00);
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x02);
+ regmap_write(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x03);
+
+ wcd9xxx_set_power_state(tavil->wcd9xxx,
+ WCD_REGION_POWER_COLLAPSE_REMOVE,
+ WCD9XXX_DIG_CORE_REGION_1);
+ regcache_mark_dirty(tavil->wcd9xxx->regmap);
+ regcache_sync_region(tavil->wcd9xxx->regmap,
+ WCD934X_DIG_CORE_REG_MIN,
+ WCD934X_DIG_CORE_REG_MAX);
+
+ tavil_restore_iir_coeff(tavil, IIR0);
+ tavil_restore_iir_coeff(tavil, IIR1);
+ return 0;
+}
+
+static int tavil_dig_core_power_collapse(struct tavil_priv *tavil,
+ int req_state)
+{
+ int cur_state;
+
+ /* Exit if feature is disabled */
+ if (!dig_core_collapse_enable)
+ return 0;
+
+ mutex_lock(&tavil->power_lock);
+ if (req_state == POWER_COLLAPSE)
+ tavil->power_active_ref--;
+ else if (req_state == POWER_RESUME)
+ tavil->power_active_ref++;
+ else
+ goto unlock_mutex;
+
+ if (tavil->power_active_ref < 0) {
+ dev_dbg(tavil->dev, "%s: power_active_ref is negative\n",
+ __func__);
+ goto unlock_mutex;
+ }
+
+ if (req_state == POWER_COLLAPSE) {
+ if (tavil->power_active_ref == 0) {
+ schedule_delayed_work(&tavil->power_gate_work,
+ msecs_to_jiffies(dig_core_collapse_timer * 1000));
+ }
+ } else if (req_state == POWER_RESUME) {
+ if (tavil->power_active_ref == 1) {
+ /*
+ * At this point, there can be two cases:
+ * 1. Core already in power collapse state
+ * 2. Timer kicked in and still did not expire or
+ * waiting for the power_lock
+ */
+ cur_state = wcd9xxx_get_current_power_state(
+ tavil->wcd9xxx,
+ WCD9XXX_DIG_CORE_REGION_1);
+ if (cur_state == WCD_REGION_POWER_DOWN) {
+ tavil_dig_core_remove_power_collapse(tavil);
+ } else {
+ mutex_unlock(&tavil->power_lock);
+ cancel_delayed_work_sync(
+ &tavil->power_gate_work);
+ mutex_lock(&tavil->power_lock);
+ }
+ }
+ }
+
+unlock_mutex:
+ mutex_unlock(&tavil->power_lock);
+
+ return 0;
+}
+
+static int tavil_cdc_req_mclk_enable(struct tavil_priv *tavil,
+ bool enable)
+{
+ int ret = 0;
+
+ if (enable) {
+ ret = clk_prepare_enable(tavil->wcd_ext_clk);
+ if (ret) {
+ dev_err(tavil->dev, "%s: ext clk enable failed\n",
+ __func__);
+ goto done;
+ }
+ /* get BG */
+ wcd_resmgr_enable_master_bias(tavil->resmgr);
+ /* get MCLK */
+ wcd_resmgr_enable_clk_block(tavil->resmgr, WCD_CLK_MCLK);
+ } else {
+ /* put MCLK */
+ wcd_resmgr_disable_clk_block(tavil->resmgr, WCD_CLK_MCLK);
+ /* put BG */
+ wcd_resmgr_disable_master_bias(tavil->resmgr);
+ clk_disable_unprepare(tavil->wcd_ext_clk);
+ }
+
+done:
+ return ret;
+}
+
+static int __tavil_cdc_mclk_enable_locked(struct tavil_priv *tavil,
+ bool enable)
+{
+ int ret = 0;
+
+ if (!tavil->wcd_ext_clk) {
+ dev_err(tavil->dev, "%s: wcd ext clock is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(tavil->dev, "%s: mclk_enable = %u\n", __func__, enable);
+
+ if (enable) {
+ tavil_dig_core_power_collapse(tavil, POWER_RESUME);
+ tavil_vote_svs(tavil, true);
+ ret = tavil_cdc_req_mclk_enable(tavil, true);
+ if (ret)
+ goto done;
+ } else {
+ tavil_cdc_req_mclk_enable(tavil, false);
+ tavil_vote_svs(tavil, false);
+ tavil_dig_core_power_collapse(tavil, POWER_COLLAPSE);
+ }
+
+done:
+ return ret;
+}
+
+static int __tavil_cdc_mclk_enable(struct tavil_priv *tavil,
+ bool enable)
+{
+ int ret;
+
+ WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr);
+ ret = __tavil_cdc_mclk_enable_locked(tavil, enable);
+ if (enable)
+ wcd_resmgr_set_sido_input_src(tavil->resmgr,
+ SIDO_SOURCE_RCO_BG);
+ WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr);
+
+ return ret;
+}
+
+static ssize_t tavil_codec_version_read(struct snd_info_entry *entry,
+ void *file_private_data,
+ struct file *file,
+ char __user *buf, size_t count,
+ loff_t pos)
+{
+ struct tavil_priv *tavil;
+ struct wcd9xxx *wcd9xxx;
+ char buffer[TAVIL_VERSION_ENTRY_SIZE];
+ int len = 0;
+
+ tavil = (struct tavil_priv *) entry->private_data;
+ if (!tavil) {
+ pr_err("%s: tavil priv is null\n", __func__);
+ return -EINVAL;
+ }
+
+ wcd9xxx = tavil->wcd9xxx;
+
+ switch (wcd9xxx->version) {
+ case TAVIL_VERSION_WCD9340_1_0:
+ len = snprintf(buffer, sizeof(buffer), "WCD9340_1_0\n");
+ break;
+ case TAVIL_VERSION_WCD9341_1_0:
+ len = snprintf(buffer, sizeof(buffer), "WCD9341_1_0\n");
+ break;
+ case TAVIL_VERSION_WCD9340_1_1:
+ len = snprintf(buffer, sizeof(buffer), "WCD9340_1_1\n");
+ break;
+ case TAVIL_VERSION_WCD9341_1_1:
+ len = snprintf(buffer, sizeof(buffer), "WCD9341_1_1\n");
+ break;
+ default:
+ len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n");
+ }
+
+ return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static struct snd_info_entry_ops tavil_codec_info_ops = {
+ .read = tavil_codec_version_read,
+};
+
+/*
+ * tavil_codec_info_create_codec_entry - creates wcd934x module
+ * @codec_root: The parent directory
+ * @codec: Codec instance
+ *
+ * Creates wcd934x module and version entry under the given
+ * parent directory.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int tavil_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+ struct snd_soc_codec *codec)
+{
+ struct snd_info_entry *version_entry;
+ struct tavil_priv *tavil;
+ struct snd_soc_card *card;
+
+ if (!codec_root || !codec)
+ return -EINVAL;
+
+ tavil = snd_soc_codec_get_drvdata(codec);
+ card = codec->component.card;
+ tavil->entry = snd_register_module_info(codec_root->module,
+ "tavil",
+ codec_root);
+ if (!tavil->entry) {
+ dev_dbg(codec->dev, "%s: failed to create wcd934x entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry = snd_info_create_card_entry(card->snd_card,
+ "version",
+ tavil->entry);
+ if (!version_entry) {
+ dev_dbg(codec->dev, "%s: failed to create wcd934x version entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry->private_data = tavil;
+ version_entry->size = TAVIL_VERSION_ENTRY_SIZE;
+ version_entry->content = SNDRV_INFO_CONTENT_DATA;
+ version_entry->c.ops = &tavil_codec_info_ops;
+
+ if (snd_info_register(version_entry) < 0) {
+ snd_info_free_entry(version_entry);
+ return -ENOMEM;
+ }
+ tavil->version_entry = version_entry;
+
+ return 0;
+}
+EXPORT_SYMBOL(tavil_codec_info_create_codec_entry);
+
+/**
+ * tavil_cdc_mclk_enable - Enable/disable codec mclk
+ *
+ * @codec: codec instance
+ * @enable: Indicates clk enable or disable
+ *
+ * Returns 0 on Success and error on failure
+ */
+int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ return __tavil_cdc_mclk_enable(tavil, enable);
+}
+EXPORT_SYMBOL(tavil_cdc_mclk_enable);
+
+static int __tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+ bool enable)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if (enable) {
+ if (wcd_resmgr_get_clk_type(tavil->resmgr) ==
+ WCD_CLK_RCO) {
+ ret = wcd_resmgr_enable_clk_block(tavil->resmgr,
+ WCD_CLK_RCO);
+ } else {
+ ret = tavil_cdc_req_mclk_enable(tavil, true);
+ if (ret) {
+ dev_err(codec->dev,
+ "%s: mclk_enable failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+ wcd_resmgr_set_sido_input_src(tavil->resmgr,
+ SIDO_SOURCE_RCO_BG);
+ ret = wcd_resmgr_enable_clk_block(tavil->resmgr,
+ WCD_CLK_RCO);
+ ret |= tavil_cdc_req_mclk_enable(tavil, false);
+ }
+
+ } else {
+ ret = wcd_resmgr_disable_clk_block(tavil->resmgr,
+ WCD_CLK_RCO);
+ }
+
+ if (ret) {
+ dev_err(codec->dev, "%s: Error in %s RCO\n",
+ __func__, (enable ? "enabling" : "disabling"));
+ ret = -EINVAL;
+ }
+
+done:
+ return ret;
+}
+
+/*
+ * tavil_codec_internal_rco_ctrl: Enable/Disable codec's RCO clock
+ * @codec: Handle to the codec
+ * @enable: Indicates whether clock should be enabled or disabled
+ */
+static int tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+ bool enable)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr);
+ ret = __tavil_codec_internal_rco_ctrl(codec, enable);
+ WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr);
+ return ret;
+}
+
+static const struct wcd_resmgr_cb tavil_resmgr_cb = {
+ .cdc_rco_ctrl = __tavil_codec_internal_rco_ctrl,
+};
+
+static const struct tavil_reg_mask_val tavil_codec_mclk2_1_1_defaults[] = {
+ {WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20},
+};
+
+static const struct tavil_reg_mask_val tavil_codec_mclk2_1_0_defaults[] = {
+ /*
+ * PLL Settings:
+ * Clock Root: MCLK2,
+ * Clock Source: EXT_CLK,
+ * Clock Destination: MCLK2
+ * Clock Freq In: 19.2MHz,
+ * Clock Freq Out: 11.2896MHz
+ */
+ {WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20},
+ {WCD934X_CLK_SYS_INT_POST_DIV_REG0, 0xFF, 0x5E},
+ {WCD934X_CLK_SYS_INT_POST_DIV_REG1, 0x1F, 0x1F},
+ {WCD934X_CLK_SYS_INT_REF_DIV_REG0, 0xFF, 0x54},
+ {WCD934X_CLK_SYS_INT_REF_DIV_REG1, 0xFF, 0x01},
+ {WCD934X_CLK_SYS_INT_FILTER_REG1, 0x07, 0x04},
+ {WCD934X_CLK_SYS_INT_PLL_L_VAL, 0xFF, 0x93},
+ {WCD934X_CLK_SYS_INT_PLL_N_VAL, 0xFF, 0xFA},
+ {WCD934X_CLK_SYS_INT_TEST_REG0, 0xFF, 0x90},
+ {WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG, 0xFF, 0x7E},
+ {WCD934X_CLK_SYS_INT_VCO_PROG, 0xFF, 0xF8},
+ {WCD934X_CLK_SYS_INT_TEST_REG1, 0xFF, 0x68},
+ {WCD934X_CLK_SYS_INT_LDO_LOCK_CFG, 0xFF, 0x40},
+ {WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0xFF, 0x32},
+};
+
+static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = {
+ {WCD934X_BIAS_VBG_FINE_ADJ, 0xFF, 0x75},
+ {WCD934X_CODEC_CPR_SVS_CX_VDD, 0xFF, 0x7C}, /* value in svs mode */
+ {WCD934X_CODEC_CPR_SVS2_CX_VDD, 0xFF, 0x58}, /* value in svs2 mode */
+ {WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+ {WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+ {WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+ {WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+ {WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+ {WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+ {WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+ {WCD934X_CDC_COMPANDER8_CTL7, 0x1E, 0x18},
+ {WCD934X_CDC_COMPANDER7_CTL7, 0x1E, 0x18},
+ {WCD934X_CDC_RX0_RX_PATH_SEC0, 0x08, 0x0},
+ {WCD934X_CDC_CLSH_DECAY_CTRL, 0x03, 0x0},
+ {WCD934X_MICB1_TEST_CTL_2, 0x07, 0x01},
+ {WCD934X_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12},
+ {WCD934X_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08},
+ {WCD934X_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12},
+ {WCD934X_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08},
+ {WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x1F, 0x09},
+ {WCD934X_CDC_TX0_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX1_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX2_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX3_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX4_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX5_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX6_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX7_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX8_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_RX_OCP_CTL, 0x0F, 0x02}, /* OCP number of attempts is 2 */
+ {WCD934X_HPH_OCP_CTL, 0xFF, 0x3A}, /* OCP current limit */
+ {WCD934X_HPH_L_TEST, 0x01, 0x01},
+ {WCD934X_HPH_R_TEST, 0x01, 0x01},
+ {WCD934X_CPE_FLL_CONFIG_CTL_2, 0xFF, 0x20},
+};
+
+static const struct tavil_reg_mask_val tavil_codec_reg_init_1_1_val[] = {
+ {WCD934X_CDC_COMPANDER1_CTL7, 0x1E, 0x06},
+ {WCD934X_CDC_COMPANDER2_CTL7, 0x1E, 0x06},
+ {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xFF, 0x84},
+ {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xFF, 0x84},
+ {WCD934X_CDC_RX3_RX_PATH_SEC0, 0xFC, 0xF4},
+ {WCD934X_CDC_RX4_RX_PATH_SEC0, 0xFC, 0xF4},
+};
+
+static const struct tavil_cpr_reg_defaults cpr_defaults[] = {
+ { 0x00000820, 0x00000094 },
+ { 0x00000fC0, 0x00000048 },
+ { 0x0000f000, 0x00000044 },
+ { 0x0000bb80, 0xC0000178 },
+ { 0x00000000, 0x00000160 },
+ { 0x10854522, 0x00000060 },
+ { 0x10854509, 0x00000064 },
+ { 0x108544dd, 0x00000068 },
+ { 0x108544ad, 0x0000006C },
+ { 0x0000077E, 0x00000070 },
+ { 0x000007da, 0x00000074 },
+ { 0x00000000, 0x00000078 },
+ { 0x00000000, 0x0000007C },
+ { 0x00042029, 0x00000080 },
+ { 0x4002002A, 0x00000090 },
+ { 0x4002002B, 0x00000090 },
+};
+
+static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = {
+ {WCD934X_CDC_CLSH_K2_MSB, 0x0F, 0x00},
+ {WCD934X_CDC_CLSH_K2_LSB, 0xFF, 0x60},
+ {WCD934X_CPE_SS_DMIC_CFG, 0x80, 0x00},
+ {WCD934X_CDC_BOOST0_BOOST_CTL, 0x70, 0x50},
+ {WCD934X_CDC_BOOST1_BOOST_CTL, 0x70, 0x50},
+ {WCD934X_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08},
+ {WCD934X_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08},
+ {WCD934X_CDC_TOP_TOP_CFG1, 0x02, 0x02},
+ {WCD934X_CDC_TOP_TOP_CFG1, 0x01, 0x01},
+ {WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+ {WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0x01, 0x01},
+ {WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x01, 0x01},
+ {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80},
+ {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80},
+ {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01},
+ {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01},
+ {WCD934X_CODEC_RPM_CLK_GATE, 0x08, 0x00},
+ {WCD934X_TLMM_DMIC3_CLK_PINCFG, 0xFF, 0x0a},
+ {WCD934X_TLMM_DMIC3_DATA_PINCFG, 0xFF, 0x0a},
+ {WCD934X_CPE_SS_SVA_CFG, 0x60, 0x00},
+ {WCD934X_CPE_SS_CPAR_CFG, 0x10, 0x10},
+};
+
+static void tavil_codec_init_reg(struct tavil_priv *priv)
+{
+ struct snd_soc_codec *codec = priv->codec;
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_common_val); i++)
+ snd_soc_update_bits(codec,
+ tavil_codec_reg_init_common_val[i].reg,
+ tavil_codec_reg_init_common_val[i].mask,
+ tavil_codec_reg_init_common_val[i].val);
+
+ if (TAVIL_IS_1_1(priv->wcd9xxx)) {
+ for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_1_1_val); i++)
+ snd_soc_update_bits(codec,
+ tavil_codec_reg_init_1_1_val[i].reg,
+ tavil_codec_reg_init_1_1_val[i].mask,
+ tavil_codec_reg_init_1_1_val[i].val);
+ }
+}
+
+static void tavil_update_reg_defaults(struct tavil_priv *tavil)
+{
+ u32 i;
+ struct wcd9xxx *wcd9xxx;
+
+ wcd9xxx = tavil->wcd9xxx;
+ for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_defaults); i++)
+ regmap_update_bits(wcd9xxx->regmap,
+ tavil_codec_reg_defaults[i].reg,
+ tavil_codec_reg_defaults[i].mask,
+ tavil_codec_reg_defaults[i].val);
+}
+
+static void tavil_update_cpr_defaults(struct tavil_priv *tavil)
+{
+ int i;
+ struct wcd9xxx *wcd9xxx;
+
+ wcd9xxx = tavil->wcd9xxx;
+ if (!TAVIL_IS_1_1(wcd9xxx))
+ return;
+
+ __tavil_cdc_mclk_enable(tavil, true);
+
+ regmap_write(wcd9xxx->regmap, WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD, 0x2C);
+ regmap_update_bits(wcd9xxx->regmap, WCD934X_CODEC_RPM_CLK_GATE,
+ 0x10, 0x00);
+
+ for (i = 0; i < ARRAY_SIZE(cpr_defaults); i++) {
+ regmap_bulk_write(wcd9xxx->regmap,
+ WCD934X_CODEC_CPR_WR_DATA_0,
+ (u8 *)&cpr_defaults[i].wr_data, 4);
+ regmap_bulk_write(wcd9xxx->regmap,
+ WCD934X_CODEC_CPR_WR_ADDR_0,
+ (u8 *)&cpr_defaults[i].wr_addr, 4);
+ }
+
+ __tavil_cdc_mclk_enable(tavil, false);
+}
+
+static void tavil_slim_interface_init_reg(struct snd_soc_codec *codec)
+{
+ int i;
+ struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
+ wcd9xxx_interface_reg_write(priv->wcd9xxx,
+ WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + i,
+ 0xFF);
+}
+
+static irqreturn_t tavil_misc_irq(int irq, void *data)
+{
+ struct tavil_priv *tavil = data;
+ int misc_val;
+
+ /* Find source of interrupt */
+ regmap_read(tavil->wcd9xxx->regmap, WCD934X_INTR_CODEC_MISC_STATUS,
+ &misc_val);
+
+ if (misc_val & 0x08) {
+ dev_info(tavil->dev, "%s: irq: %d, DSD DC detected!\n",
+ __func__, irq);
+ /* DSD DC interrupt, reset DSD path */
+ tavil_dsd_reset(tavil->dsd_config);
+ } else {
+ dev_err(tavil->dev, "%s: Codec misc irq: %d, val: 0x%x\n",
+ __func__, irq, misc_val);
+ }
+
+ /* Clear interrupt status */
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_INTR_CODEC_MISC_CLEAR, misc_val, 0x00);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tavil_slimbus_irq(int irq, void *data)
+{
+ struct tavil_priv *tavil = data;
+ unsigned long status = 0;
+ int i, j, port_id, k;
+ u32 bit;
+ u8 val, int_val = 0;
+ bool tx, cleared;
+ unsigned short reg = 0;
+
+ for (i = WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0;
+ i <= WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) {
+ val = wcd9xxx_interface_reg_read(tavil->wcd9xxx, i);
+ status |= ((u32)val << (8 * j));
+ }
+
+ for_each_set_bit(j, &status, 32) {
+ tx = (j >= 16 ? true : false);
+ port_id = (tx ? j - 16 : j);
+ val = wcd9xxx_interface_reg_read(tavil->wcd9xxx,
+ WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 + j);
+ if (val) {
+ if (!tx)
+ reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 +
+ (port_id / 8);
+ else
+ reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 +
+ (port_id / 8);
+ int_val = wcd9xxx_interface_reg_read(
+ tavil->wcd9xxx, reg);
+ /*
+ * Ignore interrupts for ports for which the
+ * interrupts are not specifically enabled.
+ */
+ if (!(int_val & (1 << (port_id % 8))))
+ continue;
+ }
+ if (val & WCD934X_SLIM_IRQ_OVERFLOW)
+ dev_err_ratelimited(tavil->dev, "%s: overflow error on %s port %d, value %x\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val);
+ if (val & WCD934X_SLIM_IRQ_UNDERFLOW)
+ dev_err_ratelimited(tavil->dev, "%s: underflow error on %s port %d, value %x\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val);
+ if ((val & WCD934X_SLIM_IRQ_OVERFLOW) ||
+ (val & WCD934X_SLIM_IRQ_UNDERFLOW)) {
+ if (!tx)
+ reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 +
+ (port_id / 8);
+ else
+ reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 +
+ (port_id / 8);
+ int_val = wcd9xxx_interface_reg_read(
+ tavil->wcd9xxx, reg);
+ if (int_val & (1 << (port_id % 8))) {
+ int_val = int_val ^ (1 << (port_id % 8));
+ wcd9xxx_interface_reg_write(tavil->wcd9xxx,
+ reg, int_val);
+ }
+ }
+ if (val & WCD934X_SLIM_IRQ_PORT_CLOSED) {
+ /*
+ * INT SOURCE register starts from RX to TX
+ * but port number in the ch_mask is in opposite way
+ */
+ bit = (tx ? j - 16 : j + 16);
+ dev_dbg(tavil->dev, "%s: %s port %d closed value %x, bit %u\n",
+ __func__, (tx ? "TX" : "RX"), port_id, val,
+ bit);
+ for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) {
+ dev_dbg(tavil->dev, "%s: tavil->dai[%d].ch_mask = 0x%lx\n",
+ __func__, k, tavil->dai[k].ch_mask);
+ if (test_and_clear_bit(bit,
+ &tavil->dai[k].ch_mask)) {
+ cleared = true;
+ if (!tavil->dai[k].ch_mask)
+ wake_up(
+ &tavil->dai[k].dai_wait);
+ /*
+ * There are cases when multiple DAIs
+ * might be using the same slimbus
+ * channel. Hence don't break here.
+ */
+ }
+ }
+ WARN(!cleared,
+ "Couldn't find slimbus %s port %d for closing\n",
+ (tx ? "TX" : "RX"), port_id);
+ }
+ wcd9xxx_interface_reg_write(tavil->wcd9xxx,
+ WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 +
+ (j / 8),
+ 1 << (j % 8));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int tavil_setup_irqs(struct tavil_priv *tavil)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = tavil->codec;
+ struct wcd9xxx *wcd9xxx = tavil->wcd9xxx;
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+
+ ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS,
+ tavil_slimbus_irq, "SLIMBUS Slave", tavil);
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_SLIMBUS);
+ else
+ tavil_slim_interface_init_reg(codec);
+
+ /* Register for misc interrupts as well */
+ ret = wcd9xxx_request_irq(core_res, WCD934X_IRQ_MISC,
+ tavil_misc_irq, "CDC MISC Irq", tavil);
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to request cdc misc irq\n",
+ __func__);
+
+ return ret;
+}
+
+static void tavil_init_slim_slave_cfg(struct snd_soc_codec *codec)
+{
+ struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct afe_param_cdc_slimbus_slave_cfg *cfg;
+ struct wcd9xxx *wcd9xxx = priv->wcd9xxx;
+ uint64_t eaddr = 0;
+
+ cfg = &priv->slimbus_slave_cfg;
+ cfg->minor_version = 1;
+ cfg->tx_slave_port_offset = 0;
+ cfg->rx_slave_port_offset = 16;
+
+ memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr));
+ WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6);
+ cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF;
+ cfg->device_enum_addr_msw = eaddr >> 32;
+
+ dev_dbg(codec->dev, "%s: slimbus logical address 0x%llx\n",
+ __func__, eaddr);
+}
+
+static void tavil_cleanup_irqs(struct tavil_priv *tavil)
+{
+ struct wcd9xxx *wcd9xxx = tavil->wcd9xxx;
+ struct wcd9xxx_core_resource *core_res =
+ &wcd9xxx->core_res;
+
+ wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tavil);
+ wcd9xxx_free_irq(core_res, WCD934X_IRQ_MISC, tavil);
+}
+
+/*
+ * wcd934x_get_micb_vout_ctl_val: converts micbias from volts to register value
+ * @micb_mv: micbias in mv
+ *
+ * return register value converted
+ */
+int wcd934x_get_micb_vout_ctl_val(u32 micb_mv)
+{
+ /* min micbias voltage is 1V and maximum is 2.85V */
+ if (micb_mv < 1000 || micb_mv > 2850) {
+ pr_err("%s: unsupported micbias voltage\n", __func__);
+ return -EINVAL;
+ }
+
+ return (micb_mv - 1000) / 50;
+}
+EXPORT_SYMBOL(wcd934x_get_micb_vout_ctl_val);
+
+static int tavil_handle_pdata(struct tavil_priv *tavil,
+ struct wcd9xxx_pdata *pdata)
+{
+ struct snd_soc_codec *codec = tavil->codec;
+ u8 mad_dmic_ctl_val;
+ u8 anc_ctl_value;
+ u32 def_dmic_rate, dmic_clk_drv;
+ int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4;
+ int rc = 0;
+
+ if (!pdata) {
+ dev_err(codec->dev, "%s: NULL pdata\n", __func__);
+ return -ENODEV;
+ }
+
+ /* set micbias voltage */
+ vout_ctl_1 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb1_mv);
+ vout_ctl_2 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb2_mv);
+ vout_ctl_3 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb3_mv);
+ vout_ctl_4 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb4_mv);
+
+ if (IS_ERR_VALUE(vout_ctl_1) || IS_ERR_VALUE(vout_ctl_2) ||
+ IS_ERR_VALUE(vout_ctl_3) || IS_ERR_VALUE(vout_ctl_4)) {
+ rc = -EINVAL;
+ goto done;
+ }
+ snd_soc_update_bits(codec, WCD934X_ANA_MICB1, 0x3F, vout_ctl_1);
+ snd_soc_update_bits(codec, WCD934X_ANA_MICB2, 0x3F, vout_ctl_2);
+ snd_soc_update_bits(codec, WCD934X_ANA_MICB3, 0x3F, vout_ctl_3);
+ snd_soc_update_bits(codec, WCD934X_ANA_MICB4, 0x3F, vout_ctl_4);
+
+ /* Set the DMIC sample rate */
+ switch (pdata->mclk_rate) {
+ case WCD934X_MCLK_CLK_9P6MHZ:
+ def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+ break;
+ case WCD934X_MCLK_CLK_12P288MHZ:
+ def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ;
+ break;
+ default:
+ /* should never happen */
+ dev_err(codec->dev, "%s: Invalid mclk_rate %d\n",
+ __func__, pdata->mclk_rate);
+ rc = -EINVAL;
+ goto done;
+ };
+
+ if (pdata->dmic_sample_rate ==
+ WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+ dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n",
+ __func__, def_dmic_rate);
+ pdata->dmic_sample_rate = def_dmic_rate;
+ }
+ if (pdata->mad_dmic_sample_rate ==
+ WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+ dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n",
+ __func__, def_dmic_rate);
+ /*
+ * use dmic_sample_rate as the default for MAD
+ * if mad dmic sample rate is undefined
+ */
+ pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate;
+ }
+
+ if (pdata->dmic_clk_drv ==
+ WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) {
+ pdata->dmic_clk_drv = WCD934X_DMIC_CLK_DRIVE_DEFAULT;
+ dev_dbg(codec->dev,
+ "%s: dmic_clk_strength invalid, default = %d\n",
+ __func__, pdata->dmic_clk_drv);
+ }
+
+ switch (pdata->dmic_clk_drv) {
+ case 2:
+ dmic_clk_drv = 0;
+ break;
+ case 4:
+ dmic_clk_drv = 1;
+ break;
+ case 8:
+ dmic_clk_drv = 2;
+ break;
+ case 16:
+ dmic_clk_drv = 3;
+ break;
+ default:
+ dev_err(codec->dev,
+ "%s: invalid dmic_clk_drv %d, using default\n",
+ __func__, pdata->dmic_clk_drv);
+ dmic_clk_drv = 0;
+ break;
+ }
+
+ snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_PAD_DRVCTL_0,
+ 0x0C, dmic_clk_drv << 2);
+
+ /*
+ * Default the DMIC clk rates to mad_dmic_sample_rate,
+ * whereas, the anc/txfe dmic rates to dmic_sample_rate
+ * since the anc/txfe are independent of mad block.
+ */
+ mad_dmic_ctl_val = tavil_get_dmic_clk_val(tavil->codec,
+ pdata->mclk_rate,
+ pdata->mad_dmic_sample_rate);
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC0_CTL,
+ 0x0E, mad_dmic_ctl_val << 1);
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC1_CTL,
+ 0x0E, mad_dmic_ctl_val << 1);
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC2_CTL,
+ 0x0E, mad_dmic_ctl_val << 1);
+
+ if (dmic_clk_drv == WCD934X_DMIC_CLK_DIV_2)
+ anc_ctl_value = WCD934X_ANC_DMIC_X2_FULL_RATE;
+ else
+ anc_ctl_value = WCD934X_ANC_DMIC_X2_HALF_RATE;
+
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL,
+ 0x40, anc_ctl_value << 6);
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL,
+ 0x20, anc_ctl_value << 5);
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL,
+ 0x40, anc_ctl_value << 6);
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL,
+ 0x20, anc_ctl_value << 5);
+
+done:
+ return rc;
+}
+
+static void tavil_cdc_vote_svs(struct snd_soc_codec *codec, bool vote)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ return tavil_vote_svs(tavil, vote);
+}
+
+struct wcd_dsp_cdc_cb cdc_cb = {
+ .cdc_clk_en = tavil_codec_internal_rco_ctrl,
+ .cdc_vote_svs = tavil_cdc_vote_svs,
+};
+
+static int tavil_wdsp_initialize(struct snd_soc_codec *codec)
+{
+ struct wcd9xxx *control;
+ struct tavil_priv *tavil;
+ struct wcd_dsp_params params;
+ int ret = 0;
+
+ control = dev_get_drvdata(codec->dev->parent);
+ tavil = snd_soc_codec_get_drvdata(codec);
+
+ params.cb = &cdc_cb;
+ params.irqs.cpe_ipc1_irq = WCD934X_IRQ_CPE1_INTR;
+ params.irqs.cpe_err_irq = WCD934X_IRQ_CPE_ERROR;
+ params.irqs.fatal_irqs = CPE_FATAL_IRQS;
+ params.clk_rate = control->mclk_rate;
+ params.dsp_instance = 0;
+
+ wcd_dsp_cntl_init(codec, &params, &tavil->wdsp_cntl);
+ if (!tavil->wdsp_cntl) {
+ dev_err(tavil->dev, "%s: wcd-dsp-control init failed\n",
+ __func__);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/*
+ * tavil_soc_get_mbhc: get wcd934x_mbhc handle of corresponding codec
+ * @codec: handle to snd_soc_codec *
+ *
+ * return wcd934x_mbhc handle or error code in case of failure
+ */
+struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec)
+{
+ struct tavil_priv *tavil;
+
+ if (!codec) {
+ pr_err("%s: Invalid params, NULL codec\n", __func__);
+ return NULL;
+ }
+ tavil = snd_soc_codec_get_drvdata(codec);
+
+ if (!tavil) {
+ pr_err("%s: Invalid params, NULL tavil\n", __func__);
+ return NULL;
+ }
+
+ return tavil->mbhc;
+}
+EXPORT_SYMBOL(tavil_soc_get_mbhc);
+
+static void tavil_mclk2_reg_defaults(struct tavil_priv *tavil)
+{
+ int i;
+ struct snd_soc_codec *codec = tavil->codec;
+
+ if (TAVIL_IS_1_0(tavil->wcd9xxx)) {
+ /* MCLK2 configuration */
+ for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_0_defaults); i++)
+ snd_soc_update_bits(codec,
+ tavil_codec_mclk2_1_0_defaults[i].reg,
+ tavil_codec_mclk2_1_0_defaults[i].mask,
+ tavil_codec_mclk2_1_0_defaults[i].val);
+ }
+ if (TAVIL_IS_1_1(tavil->wcd9xxx)) {
+ /* MCLK2 configuration */
+ for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_1_defaults); i++)
+ snd_soc_update_bits(codec,
+ tavil_codec_mclk2_1_1_defaults[i].reg,
+ tavil_codec_mclk2_1_1_defaults[i].mask,
+ tavil_codec_mclk2_1_1_defaults[i].val);
+ }
+}
+
+static int tavil_device_down(struct wcd9xxx *wcd9xxx)
+{
+ struct snd_soc_codec *codec;
+ struct tavil_priv *priv;
+ int count;
+
+ codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+ priv = snd_soc_codec_get_drvdata(codec);
+ if (priv->swr.ctrl_data)
+ swrm_wcd_notify(priv->swr.ctrl_data[0].swr_pdev,
+ SWR_DEVICE_DOWN, NULL);
+ tavil_dsd_reset(priv->dsd_config);
+ snd_soc_card_change_online_state(codec->component.card, 0);
+ for (count = 0; count < NUM_CODEC_DAIS; count++)
+ priv->dai[count].bus_down_in_recovery = true;
+ wcd_dsp_ssr_event(priv->wdsp_cntl, WCD_CDC_DOWN_EVENT);
+ wcd_resmgr_set_sido_input_src_locked(priv->resmgr,
+ SIDO_SOURCE_INTERNAL);
+
+ return 0;
+}
+
+static int tavil_post_reset_cb(struct wcd9xxx *wcd9xxx)
+{
+ int i, ret = 0;
+ struct wcd9xxx *control;
+ struct snd_soc_codec *codec;
+ struct tavil_priv *tavil;
+ struct wcd9xxx_pdata *pdata;
+ struct wcd_mbhc *mbhc;
+
+ codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+ tavil = snd_soc_codec_get_drvdata(codec);
+ control = dev_get_drvdata(codec->dev->parent);
+
+ wcd9xxx_set_power_state(tavil->wcd9xxx,
+ WCD_REGION_POWER_COLLAPSE_REMOVE,
+ WCD9XXX_DIG_CORE_REGION_1);
+
+ mutex_lock(&tavil->codec_mutex);
+
+ tavil_vote_svs(tavil, true);
+ tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la =
+ control->slim_slave->laddr;
+ tavil_slimbus_slave_port_cfg.slave_dev_pgd_la =
+ control->slim->laddr;
+ tavil_init_slim_slave_cfg(codec);
+ snd_soc_card_change_online_state(codec->component.card, 1);
+
+ for (i = 0; i < TAVIL_MAX_MICBIAS; i++)
+ tavil->micb_ref[i] = 0;
+
+ dev_dbg(codec->dev, "%s: MCLK Rate = %x\n",
+ __func__, control->mclk_rate);
+
+ if (control->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ)
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+ 0x03, 0x00);
+ else if (control->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ)
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+ 0x03, 0x01);
+ wcd_resmgr_post_ssr_v2(tavil->resmgr);
+ tavil_update_reg_defaults(tavil);
+ tavil_codec_init_reg(tavil);
+ __tavil_enable_efuse_sensing(tavil);
+ tavil_mclk2_reg_defaults(tavil);
+
+ __tavil_cdc_mclk_enable(tavil, true);
+ regcache_mark_dirty(codec->component.regmap);
+ regcache_sync(codec->component.regmap);
+ __tavil_cdc_mclk_enable(tavil, false);
+
+ tavil_update_cpr_defaults(tavil);
+
+ pdata = dev_get_platdata(codec->dev->parent);
+ ret = tavil_handle_pdata(tavil, pdata);
+ if (IS_ERR_VALUE(ret))
+ dev_err(codec->dev, "%s: invalid pdata\n", __func__);
+
+ /* Initialize MBHC module */
+ mbhc = &tavil->mbhc->wcd_mbhc;
+ ret = tavil_mbhc_post_ssr_init(tavil->mbhc, codec);
+ if (ret) {
+ dev_err(codec->dev, "%s: mbhc initialization failed\n",
+ __func__);
+ goto done;
+ } else {
+ tavil_mbhc_hs_detect(codec, mbhc->mbhc_cfg);
+ }
+
+ /* DSD initialization */
+ ret = tavil_dsd_post_ssr_init(tavil->dsd_config);
+ if (ret)
+ dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__);
+
+ tavil_cleanup_irqs(tavil);
+ ret = tavil_setup_irqs(tavil);
+ if (ret) {
+ dev_err(codec->dev, "%s: tavil irq setup failed %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ tavil_set_spkr_mode(codec, tavil->swr.spkr_mode);
+ /*
+ * Once the codec initialization is completed, the svs vote
+ * can be released allowing the codec to go to SVS2.
+ */
+ tavil_vote_svs(tavil, false);
+ wcd_dsp_ssr_event(tavil->wdsp_cntl, WCD_CDC_UP_EVENT);
+
+done:
+ mutex_unlock(&tavil->codec_mutex);
+ return ret;
+}
+
+static int tavil_soc_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wcd9xxx *control;
+ struct tavil_priv *tavil;
+ struct wcd9xxx_pdata *pdata;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ int i, ret;
+ void *ptr = NULL;
+
+ control = dev_get_drvdata(codec->dev->parent);
+
+ dev_info(codec->dev, "%s()\n", __func__);
+ tavil = snd_soc_codec_get_drvdata(codec);
+ tavil->intf_type = wcd9xxx_get_intf_type();
+
+ control->dev_down = tavil_device_down;
+ control->post_reset = tavil_post_reset_cb;
+ control->ssr_priv = (void *)codec;
+
+ /* Resource Manager post Init */
+ ret = wcd_resmgr_post_init(tavil->resmgr, &tavil_resmgr_cb, codec);
+ if (ret) {
+ dev_err(codec->dev, "%s: wcd resmgr post init failed\n",
+ __func__);
+ goto err;
+ }
+ /* Class-H Init */
+ wcd_clsh_init(&tavil->clsh_d);
+ /* Default HPH Mode to Class-H Low HiFi */
+ tavil->hph_mode = CLS_H_LOHIFI;
+
+ tavil->fw_data = devm_kzalloc(codec->dev, sizeof(*(tavil->fw_data)),
+ GFP_KERNEL);
+ if (!tavil->fw_data)
+ goto err;
+
+ set_bit(WCD9XXX_ANC_CAL, tavil->fw_data->cal_bit);
+ set_bit(WCD9XXX_MBHC_CAL, tavil->fw_data->cal_bit);
+ set_bit(WCD9XXX_MAD_CAL, tavil->fw_data->cal_bit);
+ set_bit(WCD9XXX_VBAT_CAL, tavil->fw_data->cal_bit);
+
+ ret = wcd_cal_create_hwdep(tavil->fw_data,
+ WCD9XXX_CODEC_HWDEP_NODE, codec);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
+ goto err_hwdep;
+ }
+
+ /* Initialize MBHC module */
+ ret = tavil_mbhc_init(&tavil->mbhc, codec, tavil->fw_data);
+ if (ret) {
+ pr_err("%s: mbhc initialization failed\n", __func__);
+ goto err_hwdep;
+ }
+
+ tavil->codec = codec;
+ for (i = 0; i < COMPANDER_MAX; i++)
+ tavil->comp_enabled[i] = 0;
+
+ tavil_codec_init_reg(tavil);
+
+ pdata = dev_get_platdata(codec->dev->parent);
+ ret = tavil_handle_pdata(tavil, pdata);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev, "%s: bad pdata\n", __func__);
+ goto err_hwdep;
+ }
+
+ ptr = devm_kzalloc(codec->dev, (sizeof(tavil_rx_chs) +
+ sizeof(tavil_tx_chs)), GFP_KERNEL);
+ if (!ptr) {
+ ret = -ENOMEM;
+ goto err_hwdep;
+ }
+
+ snd_soc_dapm_add_routes(dapm, tavil_slim_audio_map,
+ ARRAY_SIZE(tavil_slim_audio_map));
+ for (i = 0; i < NUM_CODEC_DAIS; i++) {
+ INIT_LIST_HEAD(&tavil->dai[i].wcd9xxx_ch_list);
+ init_waitqueue_head(&tavil->dai[i].dai_wait);
+ }
+ tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la =
+ control->slim_slave->laddr;
+ tavil_slimbus_slave_port_cfg.slave_dev_pgd_la =
+ control->slim->laddr;
+ tavil_slimbus_slave_port_cfg.slave_port_mapping[0] =
+ WCD934X_TX13;
+ tavil_init_slim_slave_cfg(codec);
+
+ control->num_rx_port = WCD934X_RX_MAX;
+ control->rx_chs = ptr;
+ memcpy(control->rx_chs, tavil_rx_chs, sizeof(tavil_rx_chs));
+ control->num_tx_port = WCD934X_TX_MAX;
+ control->tx_chs = ptr + sizeof(tavil_rx_chs);
+ memcpy(control->tx_chs, tavil_tx_chs, sizeof(tavil_tx_chs));
+
+ ret = tavil_setup_irqs(tavil);
+ if (ret) {
+ dev_err(tavil->dev, "%s: tavil irq setup failed %d\n",
+ __func__, ret);
+ goto err_pdata;
+ }
+
+ for (i = 0; i < WCD934X_NUM_DECIMATORS; i++) {
+ tavil->tx_hpf_work[i].tavil = tavil;
+ tavil->tx_hpf_work[i].decimator = i;
+ INIT_DELAYED_WORK(&tavil->tx_hpf_work[i].dwork,
+ tavil_tx_hpf_corner_freq_callback);
+
+ tavil->tx_mute_dwork[i].tavil = tavil;
+ tavil->tx_mute_dwork[i].decimator = i;
+ INIT_DELAYED_WORK(&tavil->tx_mute_dwork[i].dwork,
+ tavil_tx_mute_update_callback);
+ }
+
+ tavil->spk_anc_dwork.tavil = tavil;
+ INIT_DELAYED_WORK(&tavil->spk_anc_dwork.dwork,
+ tavil_spk_anc_update_callback);
+
+ tavil_mclk2_reg_defaults(tavil);
+
+ /* DSD initialization */
+ tavil->dsd_config = tavil_dsd_init(codec);
+ if (IS_ERR_OR_NULL(tavil->dsd_config))
+ dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__);
+
+ mutex_lock(&tavil->codec_mutex);
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA");
+ mutex_unlock(&tavil->codec_mutex);
+
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX");
+ snd_soc_dapm_ignore_suspend(dapm, "VIfeed");
+
+ snd_soc_dapm_sync(dapm);
+
+ tavil_wdsp_initialize(codec);
+
+ /*
+ * Once the codec initialization is completed, the svs vote
+ * can be released allowing the codec to go to SVS2.
+ */
+ tavil_vote_svs(tavil, false);
+
+ return ret;
+
+err_pdata:
+ devm_kfree(codec->dev, ptr);
+ control->rx_chs = NULL;
+ control->tx_chs = NULL;
+err_hwdep:
+ devm_kfree(codec->dev, tavil->fw_data);
+ tavil->fw_data = NULL;
+err:
+ return ret;
+}
+
+static int tavil_soc_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wcd9xxx *control;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ control = dev_get_drvdata(codec->dev->parent);
+ devm_kfree(codec->dev, control->rx_chs);
+ control->rx_chs = NULL;
+ control->tx_chs = NULL;
+ tavil_cleanup_irqs(tavil);
+
+ if (tavil->wdsp_cntl)
+ wcd_dsp_cntl_deinit(&tavil->wdsp_cntl);
+
+ /* Deinitialize MBHC module */
+ tavil_mbhc_deinit(codec);
+ tavil->mbhc = NULL;
+
+ return 0;
+}
+
+static struct regmap *tavil_get_regmap(struct device *dev)
+{
+ struct wcd9xxx *control = dev_get_drvdata(dev->parent);
+
+ return control->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_tavil = {
+ .probe = tavil_soc_codec_probe,
+ .remove = tavil_soc_codec_remove,
+ .controls = tavil_snd_controls,
+ .num_controls = ARRAY_SIZE(tavil_snd_controls),
+ .dapm_widgets = tavil_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tavil_dapm_widgets),
+ .dapm_routes = tavil_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tavil_audio_map),
+ .get_regmap = tavil_get_regmap,
+};
+
+#ifdef CONFIG_PM
+static int tavil_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tavil_priv *tavil = platform_get_drvdata(pdev);
+
+ if (!tavil) {
+ dev_err(dev, "%s: tavil private data is NULL\n", __func__);
+ return -EINVAL;
+ }
+ dev_dbg(dev, "%s: system suspend\n", __func__);
+ if (delayed_work_pending(&tavil->power_gate_work) &&
+ cancel_delayed_work_sync(&tavil->power_gate_work))
+ tavil_codec_power_gate_digital_core(tavil);
+ return 0;
+}
+
+static int tavil_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tavil_priv *tavil = platform_get_drvdata(pdev);
+
+ if (!tavil) {
+ dev_err(dev, "%s: tavil private data is NULL\n", __func__);
+ return -EINVAL;
+ }
+ dev_dbg(dev, "%s: system resume\n", __func__);
+ return 0;
+}
+
+static const struct dev_pm_ops tavil_pm_ops = {
+ .suspend = tavil_suspend,
+ .resume = tavil_resume,
+};
+#endif
+
+static int tavil_swrm_read(void *handle, int reg)
+{
+ struct tavil_priv *tavil;
+ struct wcd9xxx *wcd9xxx;
+ unsigned short swr_rd_addr_base;
+ unsigned short swr_rd_data_base;
+ int val, ret;
+
+ if (!handle) {
+ pr_err("%s: NULL handle\n", __func__);
+ return -EINVAL;
+ }
+ tavil = (struct tavil_priv *)handle;
+ wcd9xxx = tavil->wcd9xxx;
+
+ dev_dbg(tavil->dev, "%s: Reading soundwire register, 0x%x\n",
+ __func__, reg);
+ swr_rd_addr_base = WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0;
+ swr_rd_data_base = WCD934X_SWR_AHB_BRIDGE_RD_DATA_0;
+
+ mutex_lock(&tavil->swr.read_mutex);
+ ret = regmap_bulk_write(wcd9xxx->regmap, swr_rd_addr_base,
+ (u8 *)&reg, 4);
+ if (ret < 0) {
+ dev_err(tavil->dev, "%s: RD Addr Failure\n", __func__);
+ goto done;
+ }
+ ret = regmap_bulk_read(wcd9xxx->regmap, swr_rd_data_base,
+ (u8 *)&val, 4);
+ if (ret < 0) {
+ dev_err(tavil->dev, "%s: RD Data Failure\n", __func__);
+ goto done;
+ }
+ ret = val;
+done:
+ mutex_unlock(&tavil->swr.read_mutex);
+
+ return ret;
+}
+
+static int tavil_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len)
+{
+ struct tavil_priv *tavil;
+ struct wcd9xxx *wcd9xxx;
+ struct wcd9xxx_reg_val *bulk_reg;
+ unsigned short swr_wr_addr_base;
+ unsigned short swr_wr_data_base;
+ int i, j, ret;
+
+ if (!handle || !reg || !val) {
+ pr_err("%s: NULL parameter\n", __func__);
+ return -EINVAL;
+ }
+ if (len <= 0) {
+ pr_err("%s: Invalid size: %zu\n", __func__, len);
+ return -EINVAL;
+ }
+ tavil = (struct tavil_priv *)handle;
+ wcd9xxx = tavil->wcd9xxx;
+
+ swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0;
+ swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0;
+
+ bulk_reg = kzalloc((2 * len * sizeof(struct wcd9xxx_reg_val)),
+ GFP_KERNEL);
+ if (!bulk_reg)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < (len * 2); i += 2, j++) {
+ bulk_reg[i].reg = swr_wr_data_base;
+ bulk_reg[i].buf = (u8 *)(&val[j]);
+ bulk_reg[i].bytes = 4;
+ bulk_reg[i+1].reg = swr_wr_addr_base;
+ bulk_reg[i+1].buf = (u8 *)(&reg[j]);
+ bulk_reg[i+1].bytes = 4;
+ }
+
+ mutex_lock(&tavil->swr.write_mutex);
+ ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg,
+ (len * 2), false);
+ if (ret) {
+ dev_err(tavil->dev, "%s: swrm bulk write failed, ret: %d\n",
+ __func__, ret);
+ }
+ mutex_unlock(&tavil->swr.write_mutex);
+
+ kfree(bulk_reg);
+ return ret;
+}
+
+static int tavil_swrm_write(void *handle, int reg, int val)
+{
+ struct tavil_priv *tavil;
+ struct wcd9xxx *wcd9xxx;
+ unsigned short swr_wr_addr_base;
+ unsigned short swr_wr_data_base;
+ struct wcd9xxx_reg_val bulk_reg[2];
+ int ret;
+
+ if (!handle) {
+ pr_err("%s: NULL handle\n", __func__);
+ return -EINVAL;
+ }
+ tavil = (struct tavil_priv *)handle;
+ wcd9xxx = tavil->wcd9xxx;
+
+ swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0;
+ swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0;
+
+ /* First Write the Data to register */
+ bulk_reg[0].reg = swr_wr_data_base;
+ bulk_reg[0].buf = (u8 *)(&val);
+ bulk_reg[0].bytes = 4;
+ bulk_reg[1].reg = swr_wr_addr_base;
+ bulk_reg[1].buf = (u8 *)(&reg);
+ bulk_reg[1].bytes = 4;
+
+ mutex_lock(&tavil->swr.write_mutex);
+ ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false);
+ if (ret < 0)
+ dev_err(tavil->dev, "%s: WR Data Failure\n", __func__);
+ mutex_unlock(&tavil->swr.write_mutex);
+
+ return ret;
+}
+
+static int tavil_swrm_clock(void *handle, bool enable)
+{
+ struct tavil_priv *tavil;
+
+ if (!handle) {
+ pr_err("%s: NULL handle\n", __func__);
+ return -EINVAL;
+ }
+ tavil = (struct tavil_priv *)handle;
+
+ mutex_lock(&tavil->swr.clk_mutex);
+ dev_dbg(tavil->dev, "%s: swrm clock %s\n",
+ __func__, (enable?"enable" : "disable"));
+ if (enable) {
+ tavil->swr.clk_users++;
+ if (tavil->swr.clk_users == 1) {
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_TEST_DEBUG_NPL_DLY_TEST_1,
+ 0x10, 0x00);
+ __tavil_cdc_mclk_enable(tavil, true);
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL,
+ 0x01, 0x01);
+ }
+ } else {
+ tavil->swr.clk_users--;
+ if (tavil->swr.clk_users == 0) {
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL,
+ 0x01, 0x00);
+ __tavil_cdc_mclk_enable(tavil, false);
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_TEST_DEBUG_NPL_DLY_TEST_1,
+ 0x10, 0x10);
+ }
+ }
+ dev_dbg(tavil->dev, "%s: swrm clock users %d\n",
+ __func__, tavil->swr.clk_users);
+ mutex_unlock(&tavil->swr.clk_mutex);
+
+ return 0;
+}
+
+static int tavil_swrm_handle_irq(void *handle,
+ irqreturn_t (*swrm_irq_handler)(int irq,
+ void *data),
+ void *swrm_handle,
+ int action)
+{
+ struct tavil_priv *tavil;
+ int ret = 0;
+ struct wcd9xxx *wcd9xxx;
+
+ if (!handle) {
+ pr_err("%s: NULL handle\n", __func__);
+ return -EINVAL;
+ }
+ tavil = (struct tavil_priv *) handle;
+ wcd9xxx = tavil->wcd9xxx;
+
+ if (action) {
+ ret = wcd9xxx_request_irq(&wcd9xxx->core_res,
+ WCD934X_IRQ_SOUNDWIRE,
+ swrm_irq_handler,
+ "Tavil SWR Master", swrm_handle);
+ if (ret)
+ dev_err(tavil->dev, "%s: Failed to request irq %d\n",
+ __func__, WCD934X_IRQ_SOUNDWIRE);
+ } else
+ wcd9xxx_free_irq(&wcd9xxx->core_res, WCD934X_IRQ_SOUNDWIRE,
+ swrm_handle);
+
+ return ret;
+}
+
+static void tavil_codec_add_spi_device(struct tavil_priv *tavil,
+ struct device_node *node)
+{
+ struct spi_master *master;
+ struct spi_device *spi;
+ u32 prop_value;
+ int rc;
+
+ /* Read the master bus num from DT node */
+ rc = of_property_read_u32(node, "qcom,master-bus-num",
+ &prop_value);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(tavil->dev, "%s: prop %s not found in node %s",
+ __func__, "qcom,master-bus-num", node->full_name);
+ goto done;
+ }
+
+ /* Get the reference to SPI master */
+ master = spi_busnum_to_master(prop_value);
+ if (!master) {
+ dev_err(tavil->dev, "%s: Invalid spi_master for bus_num %u\n",
+ __func__, prop_value);
+ goto done;
+ }
+
+ /* Allocate the spi device */
+ spi = spi_alloc_device(master);
+ if (!spi) {
+ dev_err(tavil->dev, "%s: spi_alloc_device failed\n",
+ __func__);
+ goto err_spi_alloc_dev;
+ }
+
+ /* Initialize device properties */
+ if (of_modalias_node(node, spi->modalias,
+ sizeof(spi->modalias)) < 0) {
+ dev_err(tavil->dev, "%s: cannot find modalias for %s\n",
+ __func__, node->full_name);
+ goto err_dt_parse;
+ }
+
+ rc = of_property_read_u32(node, "qcom,chip-select",
+ &prop_value);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(tavil->dev, "%s: prop %s not found in node %s",
+ __func__, "qcom,chip-select", node->full_name);
+ goto err_dt_parse;
+ }
+ spi->chip_select = prop_value;
+
+ rc = of_property_read_u32(node, "qcom,max-frequency",
+ &prop_value);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(tavil->dev, "%s: prop %s not found in node %s",
+ __func__, "qcom,max-frequency", node->full_name);
+ goto err_dt_parse;
+ }
+ spi->max_speed_hz = prop_value;
+
+ spi->dev.of_node = node;
+
+ rc = spi_add_device(spi);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(tavil->dev, "%s: spi_add_device failed\n", __func__);
+ goto err_dt_parse;
+ }
+
+ /* Put the reference to SPI master */
+ put_device(&master->dev);
+
+ return;
+
+err_dt_parse:
+ spi_dev_put(spi);
+
+err_spi_alloc_dev:
+ /* Put the reference to SPI master */
+ put_device(&master->dev);
+done:
+ return;
+}
+
+static void tavil_add_child_devices(struct work_struct *work)
+{
+ struct tavil_priv *tavil;
+ struct platform_device *pdev;
+ struct device_node *node;
+ struct wcd9xxx *wcd9xxx;
+ struct tavil_swr_ctrl_data *swr_ctrl_data = NULL, *temp;
+ int ret, ctrl_num = 0;
+ struct wcd_swr_ctrl_platform_data *platdata;
+ char plat_dev_name[WCD934X_STRING_LEN];
+
+ tavil = container_of(work, struct tavil_priv,
+ tavil_add_child_devices_work);
+ if (!tavil) {
+ pr_err("%s: Memory for WCD934X does not exist\n",
+ __func__);
+ return;
+ }
+ wcd9xxx = tavil->wcd9xxx;
+ if (!wcd9xxx) {
+ pr_err("%s: Memory for WCD9XXX does not exist\n",
+ __func__);
+ return;
+ }
+ if (!wcd9xxx->dev->of_node) {
+ dev_err(wcd9xxx->dev, "%s: DT node for wcd9xxx does not exist\n",
+ __func__);
+ return;
+ }
+
+ platdata = &tavil->swr.plat_data;
+
+ for_each_child_of_node(wcd9xxx->dev->of_node, node) {
+
+ /* Parse and add the SPI device node */
+ if (!strcmp(node->name, "wcd_spi")) {
+ tavil_codec_add_spi_device(tavil, node);
+ continue;
+ }
+
+ /* Parse other child device nodes and add platform device */
+ if (!strcmp(node->name, "swr_master"))
+ strlcpy(plat_dev_name, "tavil_swr_ctrl",
+ (WCD934X_STRING_LEN - 1));
+ else if (strnstr(node->name, "msm_cdc_pinctrl",
+ strlen("msm_cdc_pinctrl")) != NULL)
+ strlcpy(plat_dev_name, node->name,
+ (WCD934X_STRING_LEN - 1));
+ else
+ continue;
+
+ pdev = platform_device_alloc(plat_dev_name, -1);
+ if (!pdev) {
+ dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_mem;
+ }
+ pdev->dev.parent = tavil->dev;
+ pdev->dev.of_node = node;
+
+ if (strcmp(node->name, "swr_master") == 0) {
+ ret = platform_device_add_data(pdev, platdata,
+ sizeof(*platdata));
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: cannot add plat data ctrl:%d\n",
+ __func__, ctrl_num);
+ goto err_pdev_add;
+ }
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: Cannot add platform device\n",
+ __func__);
+ goto err_pdev_add;
+ }
+
+ if (strcmp(node->name, "swr_master") == 0) {
+ temp = krealloc(swr_ctrl_data,
+ (ctrl_num + 1) * sizeof(
+ struct tavil_swr_ctrl_data),
+ GFP_KERNEL);
+ if (!temp) {
+ dev_err(wcd9xxx->dev, "out of memory\n");
+ ret = -ENOMEM;
+ goto err_pdev_add;
+ }
+ swr_ctrl_data = temp;
+ swr_ctrl_data[ctrl_num].swr_pdev = pdev;
+ ctrl_num++;
+ dev_dbg(&pdev->dev,
+ "%s: Added soundwire ctrl device(s)\n",
+ __func__);
+ tavil->swr.ctrl_data = swr_ctrl_data;
+ }
+ }
+
+ return;
+
+err_pdev_add:
+ platform_device_put(pdev);
+err_mem:
+ return;
+}
+
+static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil)
+{
+ int val, rc;
+
+ WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr);
+ __tavil_cdc_mclk_enable_locked(tavil, true);
+
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x10);
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x01, 0x01);
+ /*
+ * 5ms sleep required after enabling efuse control
+ * before checking the status.
+ */
+ usleep_range(5000, 5500);
+ wcd_resmgr_set_sido_input_src(tavil->resmgr,
+ SIDO_SOURCE_RCO_BG);
+
+ WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr);
+
+ rc = regmap_read(tavil->wcd9xxx->regmap,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, &val);
+ if (rc || (!(val & 0x01)))
+ WARN(1, "%s: Efuse sense is not complete val=%x, ret=%d\n",
+ __func__, val, rc);
+
+ __tavil_cdc_mclk_enable(tavil, false);
+
+ return rc;
+}
+
+static void ___tavil_get_codec_fine_version(struct tavil_priv *tavil)
+{
+ int val1, val2, version;
+ struct regmap *regmap;
+ u16 id_minor;
+ u32 version_mask = 0;
+
+ regmap = tavil->wcd9xxx->regmap;
+ version = tavil->wcd9xxx->version;
+ id_minor = tavil->wcd9xxx->codec_type->id_minor;
+
+ regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val1);
+ regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val2);
+
+ dev_dbg(tavil->dev, "%s: chip version :0x%x 0x:%x\n",
+ __func__, val1, val2);
+
+ version_mask |= (!!((u8)val1 & 0x80)) << DSD_DISABLED_MASK;
+ version_mask |= (!!((u8)val2 & 0x01)) << SLNQ_DISABLED_MASK;
+
+ switch (version_mask) {
+ case DSD_DISABLED | SLNQ_DISABLED:
+ if (id_minor == cpu_to_le16(0))
+ version = TAVIL_VERSION_WCD9340_1_0;
+ else if (id_minor == cpu_to_le16(0x01))
+ version = TAVIL_VERSION_WCD9340_1_1;
+ break;
+ case SLNQ_DISABLED:
+ if (id_minor == cpu_to_le16(0))
+ version = TAVIL_VERSION_WCD9341_1_0;
+ else if (id_minor == cpu_to_le16(0x01))
+ version = TAVIL_VERSION_WCD9341_1_1;
+ break;
+ }
+
+ tavil->wcd9xxx->version = version;
+ tavil->wcd9xxx->codec_type->version = version;
+}
+
+/*
+ * tavil_get_wcd_dsp_cntl: Get the reference to wcd_dsp_cntl
+ * @dev: Device pointer for codec device
+ *
+ * This API gets the reference to codec's struct wcd_dsp_cntl
+ */
+struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev)
+{
+ struct platform_device *pdev;
+ struct tavil_priv *tavil;
+
+ if (!dev) {
+ pr_err("%s: Invalid device\n", __func__);
+ return NULL;
+ }
+
+ pdev = to_platform_device(dev);
+ tavil = platform_get_drvdata(pdev);
+
+ return tavil->wdsp_cntl;
+}
+EXPORT_SYMBOL(tavil_get_wcd_dsp_cntl);
+
+static int tavil_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct tavil_priv *tavil;
+ struct clk *wcd_ext_clk;
+ struct wcd9xxx_resmgr_v2 *resmgr;
+ struct wcd9xxx_power_region *cdc_pwr;
+
+ tavil = devm_kzalloc(&pdev->dev, sizeof(struct tavil_priv),
+ GFP_KERNEL);
+ if (!tavil)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, tavil);
+
+ tavil->wcd9xxx = dev_get_drvdata(pdev->dev.parent);
+ tavil->dev = &pdev->dev;
+ INIT_DELAYED_WORK(&tavil->power_gate_work, tavil_codec_power_gate_work);
+ mutex_init(&tavil->power_lock);
+ INIT_WORK(&tavil->tavil_add_child_devices_work,
+ tavil_add_child_devices);
+ mutex_init(&tavil->micb_lock);
+ mutex_init(&tavil->swr.read_mutex);
+ mutex_init(&tavil->swr.write_mutex);
+ mutex_init(&tavil->swr.clk_mutex);
+ mutex_init(&tavil->codec_mutex);
+ mutex_init(&tavil->svs_mutex);
+
+ /*
+ * Codec hardware by default comes up in SVS mode.
+ * Initialize the svs_ref_cnt to 1 to reflect the hardware
+ * state in the driver.
+ */
+ tavil->svs_ref_cnt = 1;
+
+ cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region),
+ GFP_KERNEL);
+ if (!cdc_pwr) {
+ ret = -ENOMEM;
+ goto err_resmgr;
+ }
+ tavil->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr;
+ cdc_pwr->pwr_collapse_reg_min = WCD934X_DIG_CORE_REG_MIN;
+ cdc_pwr->pwr_collapse_reg_max = WCD934X_DIG_CORE_REG_MAX;
+ wcd9xxx_set_power_state(tavil->wcd9xxx,
+ WCD_REGION_POWER_COLLAPSE_REMOVE,
+ WCD9XXX_DIG_CORE_REGION_1);
+ /*
+ * Init resource manager so that if child nodes such as SoundWire
+ * requests for clock, resource manager can honor the request
+ */
+ resmgr = wcd_resmgr_init(&tavil->wcd9xxx->core_res, NULL);
+ if (IS_ERR(resmgr)) {
+ ret = PTR_ERR(resmgr);
+ dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n",
+ __func__);
+ goto err_resmgr;
+ }
+ tavil->resmgr = resmgr;
+ tavil->swr.plat_data.handle = (void *) tavil;
+ tavil->swr.plat_data.read = tavil_swrm_read;
+ tavil->swr.plat_data.write = tavil_swrm_write;
+ tavil->swr.plat_data.bulk_write = tavil_swrm_bulk_write;
+ tavil->swr.plat_data.clk = tavil_swrm_clock;
+ tavil->swr.plat_data.handle_irq = tavil_swrm_handle_irq;
+ tavil->swr.spkr_gain_offset = WCD934X_RX_GAIN_OFFSET_0_DB;
+
+ /* Register for Clock */
+ wcd_ext_clk = clk_get(tavil->wcd9xxx->dev, "wcd_clk");
+ if (IS_ERR(wcd_ext_clk)) {
+ dev_err(tavil->wcd9xxx->dev, "%s: clk get %s failed\n",
+ __func__, "wcd_ext_clk");
+ goto err_clk;
+ }
+ tavil->wcd_ext_clk = wcd_ext_clk;
+ set_bit(AUDIO_NOMINAL, &tavil->status_mask);
+ /* Update codec register default values */
+ dev_dbg(&pdev->dev, "%s: MCLK Rate = %x\n", __func__,
+ tavil->wcd9xxx->mclk_rate);
+ if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ)
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+ 0x03, 0x00);
+ else if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ)
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+ 0x03, 0x01);
+ tavil_update_reg_defaults(tavil);
+ __tavil_enable_efuse_sensing(tavil);
+ ___tavil_get_codec_fine_version(tavil);
+ tavil_update_cpr_defaults(tavil);
+
+ /* Register with soc framework */
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil,
+ tavil_dai, ARRAY_SIZE(tavil_dai));
+ if (ret) {
+ dev_err(&pdev->dev, "%s: Codec registration failed\n",
+ __func__);
+ goto err_cdc_reg;
+ }
+ schedule_work(&tavil->tavil_add_child_devices_work);
+
+ return ret;
+
+err_cdc_reg:
+ clk_put(tavil->wcd_ext_clk);
+err_clk:
+ wcd_resmgr_remove(tavil->resmgr);
+err_resmgr:
+ mutex_destroy(&tavil->micb_lock);
+ mutex_destroy(&tavil->svs_mutex);
+ mutex_destroy(&tavil->codec_mutex);
+ mutex_destroy(&tavil->swr.read_mutex);
+ mutex_destroy(&tavil->swr.write_mutex);
+ mutex_destroy(&tavil->swr.clk_mutex);
+ devm_kfree(&pdev->dev, tavil);
+
+ return ret;
+}
+
+static int tavil_remove(struct platform_device *pdev)
+{
+ struct tavil_priv *tavil;
+
+ tavil = platform_get_drvdata(pdev);
+ if (!tavil)
+ return -EINVAL;
+
+ mutex_destroy(&tavil->micb_lock);
+ mutex_destroy(&tavil->svs_mutex);
+ mutex_destroy(&tavil->codec_mutex);
+ mutex_destroy(&tavil->swr.read_mutex);
+ mutex_destroy(&tavil->swr.write_mutex);
+ mutex_destroy(&tavil->swr.clk_mutex);
+
+ snd_soc_unregister_codec(&pdev->dev);
+ clk_put(tavil->wcd_ext_clk);
+ wcd_resmgr_remove(tavil->resmgr);
+ if (tavil->dsd_config) {
+ tavil_dsd_deinit(tavil->dsd_config);
+ tavil->dsd_config = NULL;
+ }
+ devm_kfree(&pdev->dev, tavil);
+ return 0;
+}
+
+static struct platform_driver tavil_codec_driver = {
+ .probe = tavil_probe,
+ .remove = tavil_remove,
+ .driver = {
+ .name = "tavil_codec",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &tavil_pm_ops,
+#endif
+ },
+};
+
+module_platform_driver(tavil_codec_driver);
+
+MODULE_DESCRIPTION("Tavil Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd934x/wcd934x.h b/sound/soc/codecs/wcd934x/wcd934x.h
new file mode 100644
index 000000000000..1f5fda88c4f9
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef WCD934X_H
+#define WCD934X_H
+
+#include <sound/apr_audio-v2.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include "wcd934x-dsp-cntl.h"
+#include "../wcd9xxx-common-v2.h"
+#include "../wcd-mbhc-v2.h"
+
+#define WCD934X_REGISTER_START_OFFSET 0x800
+#define WCD934X_SB_PGD_PORT_RX_BASE 0x40
+#define WCD934X_SB_PGD_PORT_TX_BASE 0x50
+#define WCD934X_RX_PORT_START_NUMBER 16
+
+#define WCD934X_DMIC_CLK_DIV_2 0x0
+#define WCD934X_DMIC_CLK_DIV_3 0x1
+#define WCD934X_DMIC_CLK_DIV_4 0x2
+#define WCD934X_DMIC_CLK_DIV_6 0x3
+#define WCD934X_DMIC_CLK_DIV_8 0x4
+#define WCD934X_DMIC_CLK_DIV_16 0x5
+#define WCD934X_DMIC_CLK_DRIVE_DEFAULT 0x02
+
+#define WCD934X_ANC_DMIC_X2_FULL_RATE 1
+#define WCD934X_ANC_DMIC_X2_HALF_RATE 0
+
+#define TAVIL_MAX_MICBIAS 4
+#define TAVIL_NUM_INTERPOLATORS 9
+#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64
+
+/* Convert from vout ctl to micbias voltage in mV */
+#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50)
+
+/* Feature masks to distinguish codec version */
+#define DSD_DISABLED_MASK 0
+#define SLNQ_DISABLED_MASK 1
+
+#define DSD_DISABLED (1 << DSD_DISABLED_MASK)
+#define SLNQ_DISABLED (1 << SLNQ_DISABLED_MASK)
+
+/* Number of input and output Slimbus port */
+enum {
+ WCD934X_RX0 = 0,
+ WCD934X_RX1,
+ WCD934X_RX2,
+ WCD934X_RX3,
+ WCD934X_RX4,
+ WCD934X_RX5,
+ WCD934X_RX6,
+ WCD934X_RX7,
+ WCD934X_RX_MAX,
+};
+
+enum {
+ WCD934X_TX0 = 0,
+ WCD934X_TX1,
+ WCD934X_TX2,
+ WCD934X_TX3,
+ WCD934X_TX4,
+ WCD934X_TX5,
+ WCD934X_TX6,
+ WCD934X_TX7,
+ WCD934X_TX8,
+ WCD934X_TX9,
+ WCD934X_TX10,
+ WCD934X_TX11,
+ WCD934X_TX12,
+ WCD934X_TX13,
+ WCD934X_TX14,
+ WCD934X_TX15,
+ WCD934X_TX_MAX,
+};
+
+enum {
+ INTERP_EAR = 0,
+ INTERP_HPHL,
+ INTERP_HPHR,
+ INTERP_LO1,
+ INTERP_LO2,
+ INTERP_LO3_NA, /* LO3 not avalible in Tavil*/
+ INTERP_LO4_NA,
+ INTERP_SPKR1,
+ INTERP_SPKR2,
+ INTERP_MAX,
+};
+
+enum {
+ /* INTR_REG 0 */
+ WCD934X_IRQ_MISC = 1,
+ WCD934X_IRQ_HPH_PA_OCPL_FAULT,
+ WCD934X_IRQ_HPH_PA_OCPR_FAULT,
+ WCD934X_IRQ_EAR_PA_OCP_FAULT,
+ WCD934X_IRQ_HPH_PA_CNPL_COMPLETE,
+ WCD934X_IRQ_HPH_PA_CNPR_COMPLETE,
+ WCD934X_IRQ_EAR_PA_CNP_COMPLETE,
+ /* INTR_REG 1 */
+ WCD934X_IRQ_MBHC_SW_DET,
+ WCD934X_IRQ_MBHC_ELECT_INS_REM_DET,
+ WCD934X_IRQ_MBHC_BUTTON_PRESS_DET,
+ WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET,
+ WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+ WCD934X_IRQ_RESERVED_0,
+ WCD934X_IRQ_RESERVED_1,
+ WCD934X_IRQ_RESERVED_2,
+ /* INTR_REG 2 */
+ WCD934X_IRQ_LINE_PA1_CNP_COMPLETE,
+ WCD934X_IRQ_LINE_PA2_CNP_COMPLETE,
+ WCD934X_IRQ_SLNQ_ANALOG_ERROR,
+ WCD934X_IRQ_RESERVED_3,
+ WCD934X_IRQ_SOUNDWIRE,
+ WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE,
+ WCD934X_IRQ_RCO_ERROR,
+ WCD934X_IRQ_CPE_ERROR,
+ /* INTR_REG 3 */
+ WCD934X_IRQ_MAD_AUDIO,
+ WCD934X_IRQ_MAD_BEACON,
+ WCD934X_IRQ_MAD_ULTRASOUND,
+ WCD934X_IRQ_VBAT_ATTACK,
+ WCD934X_IRQ_VBAT_RESTORE,
+ WCD934X_IRQ_CPE1_INTR,
+ WCD934X_IRQ_RESERVED_4,
+ WCD934X_IRQ_SLNQ_DIGITAL,
+ WCD934X_NUM_IRQS,
+};
+
+/*
+ * Selects compander and smart boost settings
+ * for a given speaker mode
+ */
+enum {
+ WCD934X_SPKR_MODE_DEFAULT,
+ WCD934X_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */
+};
+
+/*
+ * Rx path gain offsets
+ */
+enum {
+ WCD934X_RX_GAIN_OFFSET_M1P5_DB,
+ WCD934X_RX_GAIN_OFFSET_0_DB,
+};
+
+/*
+ * Dai data structure holds the
+ * dai specific info like rate,
+ * channel number etc.
+ */
+struct tavil_codec_dai_data {
+ u32 rate;
+ u32 *ch_num;
+ u32 ch_act;
+ u32 ch_tot;
+};
+
+/*
+ * Structure used to update codec
+ * register defaults after reset
+ */
+struct tavil_reg_mask_val {
+ u16 reg;
+ u8 mask;
+ u8 val;
+};
+
+extern void *tavil_get_afe_config(struct snd_soc_codec *codec,
+ enum afe_config_type config_type);
+extern int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable);
+extern int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode);
+extern int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset);
+extern struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev);
+extern int wcd934x_get_micb_vout_ctl_val(u32 micb_mv);
+extern int tavil_micbias_control(struct snd_soc_codec *codec,
+ int micb_num,
+ int req, bool is_dapm);
+extern int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec,
+ int req_volt,
+ int micb_num);
+extern struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec);
+extern int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec,
+ int event, int intp_idx);
+extern struct tavil_dsd_config *tavil_get_dsd_config(struct snd_soc_codec *);
+extern int tavil_codec_info_create_codec_entry(struct snd_info_entry *,
+ struct snd_soc_codec *);
+#endif
diff --git a/sound/soc/codecs/wcd9xxx-common-v2.c b/sound/soc/codecs/wcd9xxx-common-v2.c
new file mode 100644
index 000000000000..c2f7b1e99cd5
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-common-v2.c
@@ -0,0 +1,1365 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include "wcd9xxx-common-v2.h"
+
+#define WCD_USLEEP_RANGE 50
+#define MAX_IMPED_PARAMS 6
+
+enum {
+ DAC_GAIN_0DB = 0,
+ DAC_GAIN_0P2DB,
+ DAC_GAIN_0P4DB,
+ DAC_GAIN_0P6DB,
+ DAC_GAIN_0P8DB,
+ DAC_GAIN_M0P2DB,
+ DAC_GAIN_M0P4DB,
+ DAC_GAIN_M0P6DB,
+};
+
+enum {
+ VREF_FILT_R_0OHM = 0,
+ VREF_FILT_R_25KOHM,
+ VREF_FILT_R_50KOHM,
+ VREF_FILT_R_100KOHM,
+};
+
+enum {
+ DELTA_I_0MA,
+ DELTA_I_10MA,
+ DELTA_I_20MA,
+ DELTA_I_30MA,
+ DELTA_I_40MA,
+ DELTA_I_50MA,
+};
+
+struct wcd_imped_val {
+ u32 imped_val;
+ u8 index;
+};
+
+static const struct wcd_reg_mask_val imped_table[][MAX_IMPED_PARAMS] = {
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf5},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf5},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf5},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf5},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf7},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf7},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf7},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf7},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf9},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf9},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x0},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf9},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf9},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x0},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfa},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfa},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfa},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfa},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfb},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfb},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfb},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfb},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfc},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfc},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfc},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfc},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfe},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfe},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfe},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfe},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xff},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xff},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xff},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xff},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00},
+ },
+};
+
+static const struct wcd_reg_mask_val imped_table_tavil[][MAX_IMPED_PARAMS] = {
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf2},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf2},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf2},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf2},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf4},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf4},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf4},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf4},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf7},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf7},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf7},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf7},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf9},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf9},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf9},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf9},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfa},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfa},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfa},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfa},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfb},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfb},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfb},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfb},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfc},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfc},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfc},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfc},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00},
+ },
+ {
+ {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+ {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd},
+ {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+ },
+};
+
+static const struct wcd_imped_val imped_index[] = {
+ {4, 0},
+ {5, 1},
+ {6, 2},
+ {7, 3},
+ {8, 4},
+ {9, 5},
+ {10, 6},
+ {11, 7},
+ {12, 8},
+ {13, 9},
+};
+
+static void (*clsh_state_fp[NUM_CLSH_STATES_V2])(struct snd_soc_codec *,
+ struct wcd_clsh_cdc_data *,
+ u8 req_state, bool en, int mode);
+
+static int get_impedance_index(int imped)
+{
+ int i = 0;
+
+ if (imped < imped_index[i].imped_val) {
+ pr_debug("%s, detected impedance is less than 4 Ohm\n",
+ __func__);
+ i = 0;
+ goto ret;
+ }
+ if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) {
+ pr_debug("%s, detected impedance is greater than 12 Ohm\n",
+ __func__);
+ i = ARRAY_SIZE(imped_index) - 1;
+ goto ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) {
+ if (imped >= imped_index[i].imped_val &&
+ imped < imped_index[i + 1].imped_val)
+ break;
+ }
+ret:
+ pr_debug("%s: selected impedance index = %d\n",
+ __func__, imped_index[i].index);
+ return imped_index[i].index;
+}
+
+/*
+ * Function: wcd_clsh_imped_config
+ * Params: codec, imped, reset
+ * Description:
+ * This function updates HPHL and HPHR gain settings
+ * according to the impedance value.
+ */
+void wcd_clsh_imped_config(struct snd_soc_codec *codec, int imped, bool reset)
+{
+ int i;
+ int index = 0;
+ int table_size;
+
+ static const struct wcd_reg_mask_val
+ (*imped_table_ptr)[MAX_IMPED_PARAMS];
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ if (IS_CODEC_TYPE(wcd9xxx, WCD934X)) {
+ table_size = ARRAY_SIZE(imped_table_tavil);
+ imped_table_ptr = imped_table_tavil;
+ } else {
+ table_size = ARRAY_SIZE(imped_table);
+ imped_table_ptr = imped_table;
+ }
+
+ /* reset = 1, which means request is to reset the register values */
+ if (reset) {
+ for (i = 0; i < MAX_IMPED_PARAMS; i++)
+ snd_soc_update_bits(codec,
+ imped_table_ptr[index][i].reg,
+ imped_table_ptr[index][i].mask, 0);
+ return;
+ }
+ index = get_impedance_index(imped);
+ if (index >= (ARRAY_SIZE(imped_index) - 1)) {
+ pr_debug("%s, impedance not in range = %d\n", __func__, imped);
+ return;
+ }
+ if (index >= table_size) {
+ pr_debug("%s, impedance index not in range = %d\n", __func__,
+ index);
+ return;
+ }
+ for (i = 0; i < MAX_IMPED_PARAMS; i++)
+ snd_soc_update_bits(codec,
+ imped_table_ptr[index][i].reg,
+ imped_table_ptr[index][i].mask,
+ imped_table_ptr[index][i].val);
+}
+EXPORT_SYMBOL(wcd_clsh_imped_config);
+
+static bool is_native_44_1_active(struct snd_soc_codec *codec)
+{
+ bool native_active = false;
+ u8 native_clk, rx1_rate, rx2_rate;
+
+ native_clk = snd_soc_read(codec,
+ WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL);
+ rx1_rate = snd_soc_read(codec, WCD9XXX_CDC_RX1_RX_PATH_CTL);
+ rx2_rate = snd_soc_read(codec, WCD9XXX_CDC_RX2_RX_PATH_CTL);
+
+ dev_dbg(codec->dev, "%s: native_clk %x rx1_rate= %x rx2_rate= %x",
+ __func__, native_clk, rx1_rate, rx2_rate);
+
+ if ((native_clk & 0x2) &&
+ ((rx1_rate & 0x0F) == 0x9 || (rx2_rate & 0x0F) == 0x9))
+ native_active = true;
+
+ return native_active;
+}
+
+static const char *mode_to_str(int mode)
+{
+ switch (mode) {
+ case CLS_H_NORMAL:
+ return "CLS_H_NORMAL";
+ case CLS_H_HIFI:
+ return "CLS_H_HIFI";
+ case CLS_H_LOHIFI:
+ return "CLS_H_LOHIFI";
+ case CLS_H_LP:
+ return "CLS_H_LP";
+ case CLS_H_ULP:
+ return "CLS_H_ULP";
+ case CLS_AB:
+ return "CLS_AB";
+ case CLS_AB_HIFI:
+ return "CLS_AB_HIFI";
+ default:
+ return "CLS_H_INVALID";
+ };
+}
+
+static const char *state_to_str(u8 state, char *buf, size_t buflen)
+{
+ int i;
+ int cnt = 0;
+ /*
+ * This array of strings should match with enum wcd_clsh_state_bit.
+ */
+ const char *states[] = {
+ "STATE_EAR",
+ "STATE_HPH_L",
+ "STATE_HPH_R",
+ "STATE_LO",
+ };
+
+ if (state == WCD_CLSH_STATE_IDLE) {
+ snprintf(buf, buflen, "[STATE_IDLE]");
+ goto done;
+ }
+
+ buf[0] = '\0';
+ for (i = 0; i < ARRAY_SIZE(states); i++) {
+ if (!(state & (1 << i)))
+ continue;
+ cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf,
+ buf[0] == '\0' ? "[" : "|",
+ states[i]);
+ }
+ if (cnt > 0)
+ strlcat(buf + cnt, "]", buflen);
+
+done:
+ if (buf[0] == '\0')
+ snprintf(buf, buflen, "[STATE_UNKNOWN]");
+ return buf;
+}
+
+static inline void
+wcd_enable_clsh_block(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d, bool enable)
+{
+ if ((enable && ++clsh_d->clsh_users == 1) ||
+ (!enable && --clsh_d->clsh_users == 0))
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_CRC, 0x01,
+ (u8) enable);
+ if (clsh_d->clsh_users < 0)
+ clsh_d->clsh_users = 0;
+ dev_dbg(codec->dev, "%s: clsh_users %d, enable %d", __func__,
+ clsh_d->clsh_users, enable);
+}
+
+static inline bool wcd_clsh_enable_status(struct snd_soc_codec *codec)
+{
+ return snd_soc_read(codec, WCD9XXX_A_CDC_CLSH_CRC) & 0x01;
+}
+
+static inline int wcd_clsh_get_int_mode(struct wcd_clsh_cdc_data *clsh_d,
+ int clsh_state)
+{
+ int mode;
+
+ if ((clsh_state != WCD_CLSH_STATE_EAR) &&
+ (clsh_state != WCD_CLSH_STATE_HPHL) &&
+ (clsh_state != WCD_CLSH_STATE_HPHR) &&
+ (clsh_state != WCD_CLSH_STATE_LO))
+ mode = CLS_NONE;
+ else
+ mode = clsh_d->interpolator_modes[ffs(clsh_state)];
+
+ return mode;
+}
+
+static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_data *clsh_d,
+ int clsh_state, int mode)
+{
+ if ((clsh_state != WCD_CLSH_STATE_EAR) &&
+ (clsh_state != WCD_CLSH_STATE_HPHL) &&
+ (clsh_state != WCD_CLSH_STATE_HPHR) &&
+ (clsh_state != WCD_CLSH_STATE_LO))
+ return;
+
+ clsh_d->interpolator_modes[ffs(clsh_state)] = mode;
+}
+
+static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec,
+ int mode)
+{
+ if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+ mode == CLS_AB_HIFI || mode == CLS_AB)
+ snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+ 0x08, 0x08); /* set to HIFI */
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+ 0x08, 0x00); /* set to default */
+}
+
+static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec,
+ int mode)
+{
+ if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+ mode == CLS_AB_HIFI || mode == CLS_AB)
+ snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+ 0x04, 0x04); /* set to HIFI */
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+ 0x04, 0x00); /* set to Default */
+}
+
+static inline void wcd_clsh_gm3_boost_disable(struct snd_soc_codec *codec,
+ int mode)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ if (!IS_CODEC_TYPE(wcd9xxx, WCD934X))
+ return;
+
+ if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+ mode == CLS_AB_HIFI || mode == CLS_AB) {
+ if (TAVIL_IS_1_0(wcd9xxx))
+ snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL,
+ 0x80, 0x0); /* disable GM3 Boost */
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
+ 0xF0, 0x80);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL,
+ 0x80, 0x80); /* set to Default */
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
+ 0xF0, 0x70);
+ }
+}
+
+
+static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec,
+ int mode)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ if (!IS_CODEC_TYPE(wcd9xxx, WCD934X))
+ return;
+
+ if (mode == CLS_H_LOHIFI || mode == CLS_AB) {
+ snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
+ 0x20, 0x20);
+ snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+ 0xF0, 0xC0);
+ snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
+ 0x0E, 0x02);
+ } else {
+
+ snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
+ 0x20, 0x0);
+ snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+ 0xF0, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
+ 0x0E, 0x06);
+ }
+}
+
+static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d,
+ int mode,
+ bool enable)
+{
+ /* enable/disable buck */
+ if ((enable && (++clsh_d->buck_users == 1)) ||
+ (!enable && (--clsh_d->buck_users == 0)))
+ snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+ (1 << 7), (enable << 7));
+ dev_dbg(codec->dev, "%s: buck_users %d, enable %d, mode: %s",
+ __func__, clsh_d->buck_users, enable, mode_to_str(mode));
+ /*
+ * 500us sleep is required after buck enable/disable
+ * as per HW requirement
+ */
+ usleep_range(500, 500 + WCD_USLEEP_RANGE);
+}
+
+static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d,
+ int mode,
+ bool enable)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_reg_val bulk_reg[2];
+ u8 vneg[] = {0x00, 0x40};
+
+ /* enable/disable flyback */
+ if ((enable && (++clsh_d->flyback_users == 1)) ||
+ (!enable && (--clsh_d->flyback_users == 0))) {
+ snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+ (1 << 6), (enable << 6));
+ /* 100usec delay is needed as per HW requirement */
+ usleep_range(100, 110);
+ if (enable && (TASHA_IS_1_1(wcd9xxx))) {
+ wcd_clsh_set_flyback_mode(codec, CLS_H_HIFI);
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN,
+ 0x60, 0x40);
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN,
+ 0x10, 0x10);
+ vneg[0] = snd_soc_read(codec,
+ WCD9XXX_A_ANA_RX_SUPPLIES);
+ vneg[0] &= ~(0x40);
+ vneg[1] = vneg[0] | 0x40;
+ bulk_reg[0].reg = WCD9XXX_A_ANA_RX_SUPPLIES;
+ bulk_reg[0].buf = &vneg[0];
+ bulk_reg[0].bytes = 1;
+ bulk_reg[1].reg = WCD9XXX_A_ANA_RX_SUPPLIES;
+ bulk_reg[1].buf = &vneg[1];
+ bulk_reg[1].bytes = 1;
+ /* 500usec delay is needed as per HW requirement */
+ usleep_range(500, 510);
+ wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2,
+ false);
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN,
+ 0x10, 0x00);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ }
+
+ }
+ dev_dbg(codec->dev, "%s: flyback_users %d, enable %d, mode: %s",
+ __func__, clsh_d->flyback_users, enable, mode_to_str(mode));
+ /*
+ * 500us sleep is required after flyback enable/disable
+ * as per HW requirement
+ */
+ usleep_range(500, 500 + WCD_USLEEP_RANGE);
+}
+
+static void wcd_clsh_set_gain_path(struct snd_soc_codec *codec,
+ int mode)
+{
+ u8 val = 0;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ if (!TASHA_IS_2_0(wcd9xxx))
+ return;
+
+ switch (mode) {
+ case CLS_H_NORMAL:
+ case CLS_AB:
+ val = 0x00;
+ break;
+ case CLS_H_HIFI:
+ val = 0x02;
+ break;
+ case CLS_H_LP:
+ val = 0x01;
+ break;
+ default:
+ return;
+ };
+ snd_soc_update_bits(codec, WCD9XXX_HPH_L_EN, 0xC0, (val << 6));
+ snd_soc_update_bits(codec, WCD9XXX_HPH_R_EN, 0xC0, (val << 6));
+}
+
+static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec,
+ int mode)
+{
+ u8 val = 0;
+ u8 gain = 0;
+ u8 res_val = VREF_FILT_R_0OHM;
+ u8 ipeak = DELTA_I_50MA;
+
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ switch (mode) {
+ case CLS_H_NORMAL:
+ res_val = VREF_FILT_R_50KOHM;
+ val = 0x00;
+ gain = DAC_GAIN_0DB;
+ ipeak = DELTA_I_50MA;
+ break;
+ case CLS_AB:
+ val = 0x00;
+ gain = DAC_GAIN_0DB;
+ ipeak = DELTA_I_50MA;
+ break;
+ case CLS_AB_HIFI:
+ val = 0x08;
+ break;
+ case CLS_H_HIFI:
+ val = 0x08;
+ gain = DAC_GAIN_M0P2DB;
+ ipeak = DELTA_I_50MA;
+ break;
+ case CLS_H_LOHIFI:
+ val = 0x00;
+ if ((IS_CODEC_TYPE(wcd9xxx, WCD9335)) ||
+ (IS_CODEC_TYPE(wcd9xxx, WCD9326))) {
+ val = 0x08;
+ gain = DAC_GAIN_M0P2DB;
+ ipeak = DELTA_I_50MA;
+ }
+ break;
+ case CLS_H_ULP:
+ val = 0x0C;
+ break;
+ case CLS_H_LP:
+ val = 0x04;
+ ipeak = DELTA_I_30MA;
+ break;
+ default:
+ return;
+ };
+
+ /*
+ * For tavil set mode to Lower_power for
+ * CLS_H_LOHIFI and CLS_AB
+ */
+ if ((IS_CODEC_TYPE(wcd9xxx, WCD934X)) &&
+ (mode == CLS_H_LOHIFI || mode == CLS_AB))
+ val = 0x04;
+
+ snd_soc_update_bits(codec, WCD9XXX_A_ANA_HPH, 0x0C, val);
+ if (TASHA_IS_2_0(wcd9xxx)) {
+ snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_VCL_2,
+ 0x30, (res_val << 4));
+ if (mode != CLS_H_LP)
+ snd_soc_update_bits(codec, WCD9XXX_HPH_REFBUFF_UHQA_CTL,
+ 0x07, gain);
+ snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_CCL_1,
+ 0xF0, (ipeak << 4));
+ }
+}
+
+static void wcd_clsh_set_flyback_vneg_ctl(struct snd_soc_codec *codec,
+ bool enable)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ if (!TASHA_IS_2_0(wcd9xxx))
+ return;
+
+ if (enable) {
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_1, 0xE0,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
+ 0xE0, (0x07 << 5));
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_1, 0xE0,
+ (0x07 << 5));
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
+ 0xE0, (0x02 << 5));
+ }
+}
+
+static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ if (!TASHA_IS_2_0(wcd9xxx))
+ return;
+
+ snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A);
+ snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0xF0, 0xA0);
+ /* Sleep needed to avoid click and pop as per HW requirement */
+ usleep_range(100, 110);
+}
+
+static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_codec *codec,
+ int mode)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+ 0x02, 0x00);
+}
+
+static void wcd_clsh_state_lo(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (mode != CLS_AB && mode != CLS_AB_HIFI) {
+ dev_err(codec->dev, "%s: LO cannot be in this mode: %d\n",
+ __func__, mode);
+ return;
+ }
+
+ if (is_enable) {
+ wcd_clsh_set_buck_regulator_mode(codec, mode);
+ wcd_clsh_set_flyback_vneg_ctl(codec, true);
+ wcd_clsh_set_buck_mode(codec, mode);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_flyback_current(codec, mode);
+ wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+ } else {
+ wcd_clsh_buck_ctrl(codec, clsh_d, mode, false);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false);
+ wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_flyback_vneg_ctl(codec, false);
+ wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ int hph_mode = 0;
+
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (is_enable) {
+ if (req_state == WCD_CLSH_STATE_EAR) {
+ /* If HPH is running in CLS-AB when
+ * EAR comes, let it continue to run
+ * in Class-AB, no need to enable Class-H
+ * for EAR.
+ */
+ if (clsh_d->state & WCD_CLSH_STATE_HPHL)
+ hph_mode = wcd_clsh_get_int_mode(clsh_d,
+ WCD_CLSH_STATE_HPHL);
+ else if (clsh_d->state & WCD_CLSH_STATE_HPHR)
+ hph_mode = wcd_clsh_get_int_mode(clsh_d,
+ WCD_CLSH_STATE_HPHR);
+ else
+ return;
+ if (hph_mode != CLS_AB && hph_mode != CLS_AB_HIFI
+ && !is_native_44_1_active(codec))
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+ 0x40, 0x40);
+ }
+
+ if (is_native_44_1_active(codec)) {
+ snd_soc_write(codec, WCD9XXX_CDC_CLSH_HPH_V_PA, 0x39);
+ snd_soc_update_bits(codec,
+ WCD9XXX_CDC_RX0_RX_PATH_SEC0,
+ 0x03, 0x00);
+ if ((req_state == WCD_CLSH_STATE_HPHL) ||
+ (req_state == WCD_CLSH_STATE_HPHR))
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+ 0x40, 0x00);
+ }
+
+ if (req_state == WCD_CLSH_STATE_HPHL)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+ 0x40, 0x40);
+ if (req_state == WCD_CLSH_STATE_HPHR)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+ 0x40, 0x40);
+ if ((req_state == WCD_CLSH_STATE_HPHL) ||
+ (req_state == WCD_CLSH_STATE_HPHR)) {
+ wcd_clsh_set_gain_path(codec, mode);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_set_buck_mode(codec, mode);
+ }
+ } else {
+ if (req_state == WCD_CLSH_STATE_EAR) {
+ /*
+ * If EAR goes away, disable EAR Channel Enable
+ * if HPH running in Class-H otherwise
+ * and if HPH requested mode is CLS_AB then
+ * no need to disable EAR channel enable bit.
+ */
+ if (wcd_clsh_enable_status(codec))
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+ 0x40, 0x00);
+ }
+
+ if (is_native_44_1_active(codec)) {
+ snd_soc_write(codec, WCD9XXX_CDC_CLSH_HPH_V_PA, 0x1C);
+ snd_soc_update_bits(codec,
+ WCD9XXX_CDC_RX0_RX_PATH_SEC0,
+ 0x03, 0x01);
+ if (((clsh_d->state & WCD_CLSH_STATE_HPH_ST)
+ != WCD_CLSH_STATE_HPH_ST) &&
+ ((req_state == WCD_CLSH_STATE_HPHL) ||
+ (req_state == WCD_CLSH_STATE_HPHR)))
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+ 0x40, 0x40);
+ }
+
+ if (req_state == WCD_CLSH_STATE_HPHL)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+ 0x40, 0x00);
+ if (req_state == WCD_CLSH_STATE_HPHR)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+ 0x40, 0x00);
+ if ((req_state & WCD_CLSH_STATE_HPH_ST) &&
+ !wcd_clsh_enable_status(codec)) {
+ /* If Class-H is not enabled when HPH is turned
+ * off, enable it as EAR is in progress
+ */
+ wcd_enable_clsh_block(codec, clsh_d, true);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+ 0x40, 0x40);
+ wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+ }
+ }
+}
+
+static void wcd_clsh_state_ear_lo(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (is_enable) {
+ /* LO powerup is taken care in PA sequence.
+ * No need to change to class AB here.
+ */
+ if (req_state == WCD_CLSH_STATE_EAR) {
+ /* EAR powerup.*/
+ if (!wcd_clsh_enable_status(codec)) {
+ wcd_enable_clsh_block(codec, clsh_d, true);
+ wcd_clsh_set_buck_mode(codec, mode);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ }
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+ 0x40, 0x40);
+ }
+ } else {
+ if (req_state == WCD_CLSH_STATE_EAR) {
+ /* EAR powerdown.*/
+ wcd_enable_clsh_block(codec, clsh_d, false);
+ wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+ 0x40, 0x00);
+ }
+ /* LO powerdown is taken care in PA sequence.
+ * No need to change to class H here.
+ */
+ }
+}
+
+static void wcd_clsh_state_hph_lo(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ int hph_mode = 0;
+
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (is_enable) {
+ /*
+ * If requested state is LO, put regulator
+ * in class-AB or if requested state is HPH,
+ * which means LO is already enabled, keep
+ * the regulator config the same at class-AB
+ * and just set the power modes for flyback
+ * and buck.
+ */
+ if (req_state == WCD_CLSH_STATE_LO)
+ wcd_clsh_set_buck_regulator_mode(codec, CLS_AB);
+ else {
+ if (!wcd_clsh_enable_status(codec)) {
+ wcd_enable_clsh_block(codec, clsh_d, true);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_CLSH_K1_MSB,
+ 0x0F, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_CLSH_K1_LSB,
+ 0xFF, 0xC0);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_set_flyback_vneg_ctl(codec, false);
+ wcd_clsh_set_buck_mode(codec, mode);
+ wcd_clsh_set_hph_mode(codec, mode);
+ wcd_clsh_set_gain_path(codec, mode);
+ } else {
+ dev_dbg(codec->dev, "%s:clsh is already enabled\n",
+ __func__);
+ }
+ if (req_state == WCD_CLSH_STATE_HPHL)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+ 0x40, 0x40);
+ if (req_state == WCD_CLSH_STATE_HPHR)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+ 0x40, 0x40);
+ }
+ } else {
+ if ((req_state == WCD_CLSH_STATE_HPHL) ||
+ (req_state == WCD_CLSH_STATE_HPHR)) {
+ if (req_state == WCD_CLSH_STATE_HPHL)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+ 0x40, 0x00);
+ if (req_state == WCD_CLSH_STATE_HPHR)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+ 0x40, 0x00);
+ /*
+ * If HPH is powering down first, then disable clsh,
+ * set the buck/flyback mode to default and keep the
+ * regulator at Class-AB
+ */
+ if ((clsh_d->state & WCD_CLSH_STATE_HPH_ST)
+ != WCD_CLSH_STATE_HPH_ST) {
+ wcd_enable_clsh_block(codec, clsh_d, false);
+ wcd_clsh_set_flyback_vneg_ctl(codec, true);
+ wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+ }
+ } else {
+ /* LO powerdown.
+ * If HPH mode also is CLS-AB, no need
+ * to turn-on class-H, otherwise enable
+ * Class-H configuration.
+ */
+ if (clsh_d->state & WCD_CLSH_STATE_HPHL)
+ hph_mode = wcd_clsh_get_int_mode(clsh_d,
+ WCD_CLSH_STATE_HPHL);
+ else if (clsh_d->state & WCD_CLSH_STATE_HPHR)
+ hph_mode = wcd_clsh_get_int_mode(clsh_d,
+ WCD_CLSH_STATE_HPHR);
+ else
+ return;
+ dev_dbg(codec->dev, "%s: hph_mode = %d\n", __func__,
+ hph_mode);
+
+ if ((hph_mode == CLS_AB) ||
+ (hph_mode == CLS_AB_HIFI) ||
+ (hph_mode == CLS_NONE))
+ goto end;
+
+ /*
+ * If Class-H is already enabled (HPH ON and then
+ * LO ON), no need to turn on again, just set the
+ * regulator mode.
+ */
+ if (wcd_clsh_enable_status(codec)) {
+ wcd_clsh_set_buck_regulator_mode(codec,
+ hph_mode);
+ goto end;
+ } else {
+ dev_dbg(codec->dev, "%s: clsh is not enabled\n",
+ __func__);
+ }
+
+ wcd_enable_clsh_block(codec, clsh_d, true);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_CLSH_K1_MSB,
+ 0x0F, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_CLSH_K1_LSB,
+ 0xFF, 0xC0);
+ wcd_clsh_set_buck_regulator_mode(codec,
+ hph_mode);
+ if (clsh_d->state & WCD_CLSH_STATE_HPHL)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+ 0x40, 0x40);
+ if (clsh_d->state & WCD_CLSH_STATE_HPHR)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+ 0x40, 0x40);
+ wcd_clsh_set_hph_mode(codec, hph_mode);
+ }
+ }
+end:
+ return;
+}
+
+static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (mode == CLS_AB || mode == CLS_AB_HIFI)
+ return;
+
+ if (is_enable) {
+ if (req_state == WCD_CLSH_STATE_HPHL)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+ 0x40, 0x40);
+ if (req_state == WCD_CLSH_STATE_HPHR)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+ 0x40, 0x40);
+ } else {
+ if (req_state == WCD_CLSH_STATE_HPHL)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+ 0x40, 0x00);
+ if (req_state == WCD_CLSH_STATE_HPHR)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+ 0x40, 0x00);
+ }
+}
+
+static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (mode == CLS_H_NORMAL) {
+ dev_err(codec->dev, "%s: Normal mode not applicable for hph_r\n",
+ __func__);
+ return;
+ }
+
+ if (is_enable) {
+ if (mode != CLS_AB && mode != CLS_AB_HIFI) {
+ wcd_enable_clsh_block(codec, clsh_d, true);
+ /*
+ * These K1 values depend on the Headphone Impedance
+ * For now it is assumed to be 16 ohm
+ */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_MSB,
+ 0x0F, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_LSB,
+ 0xFF, 0xC0);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+ 0x40, 0x40);
+ }
+ wcd_clsh_set_buck_regulator_mode(codec, mode);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_gm3_boost_disable(codec, mode);
+ wcd_clsh_force_iq_ctl(codec, mode);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_flyback_current(codec, mode);
+ wcd_clsh_set_buck_mode(codec, mode);
+ wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_hph_mode(codec, mode);
+ wcd_clsh_set_gain_path(codec, mode);
+ } else {
+ wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
+
+ if (mode != CLS_AB && mode != CLS_AB_HIFI) {
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+ 0x40, 0x00);
+ wcd_enable_clsh_block(codec, clsh_d, false);
+ }
+ /* buck and flyback set to default mode and disable */
+ wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL);
+ wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL);
+ wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (mode == CLS_H_NORMAL) {
+ dev_err(codec->dev, "%s: Normal mode not applicable for hph_l\n",
+ __func__);
+ return;
+ }
+
+ if (is_enable) {
+ if (mode != CLS_AB && mode != CLS_AB_HIFI) {
+ wcd_enable_clsh_block(codec, clsh_d, true);
+ /*
+ * These K1 values depend on the Headphone Impedance
+ * For now it is assumed to be 16 ohm
+ */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_MSB,
+ 0x0F, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_LSB,
+ 0xFF, 0xC0);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+ 0x40, 0x40);
+ }
+ wcd_clsh_set_buck_regulator_mode(codec, mode);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_gm3_boost_disable(codec, mode);
+ wcd_clsh_force_iq_ctl(codec, mode);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_flyback_current(codec, mode);
+ wcd_clsh_set_buck_mode(codec, mode);
+ wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_hph_mode(codec, mode);
+ wcd_clsh_set_gain_path(codec, mode);
+ } else {
+ wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
+
+ if (mode != CLS_AB && mode != CLS_AB_HIFI) {
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+ 0x40, 0x00);
+ wcd_enable_clsh_block(codec, clsh_d, false);
+ }
+ /* set buck and flyback to Default Mode */
+ wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL);
+ wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL);
+ wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_ear(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+ is_enable ? "enable" : "disable");
+
+ if (mode != CLS_H_NORMAL) {
+ dev_err(codec->dev, "%s: mode: %s cannot be used for EAR\n",
+ __func__, mode_to_str(mode));
+ return;
+ }
+
+ if (is_enable) {
+ wcd_enable_clsh_block(codec, clsh_d, true);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+ 0x40, 0x40);
+ wcd_clsh_set_buck_mode(codec, mode);
+ wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+ wcd_clsh_set_flyback_current(codec, mode);
+ wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+ } else {
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+ 0x40, 0x00);
+ wcd_enable_clsh_block(codec, clsh_d, false);
+ wcd_clsh_buck_ctrl(codec, clsh_d, mode, false);
+ wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false);
+ wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+ wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+ }
+}
+
+static void wcd_clsh_state_err(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable, int mode)
+{
+ char msg[128];
+
+ dev_err(codec->dev,
+ "%s Wrong request for class H state machine requested to %s %s",
+ __func__, is_enable ? "enable" : "disable",
+ state_to_str(req_state, msg, sizeof(msg)));
+ WARN_ON(1);
+}
+
+/*
+ * Function: wcd_clsh_is_state_valid
+ * Params: state
+ * Description:
+ * Provides information on valid states of Class H configuration
+ */
+static bool wcd_clsh_is_state_valid(u8 state)
+{
+ switch (state) {
+ case WCD_CLSH_STATE_IDLE:
+ case WCD_CLSH_STATE_EAR:
+ case WCD_CLSH_STATE_HPHL:
+ case WCD_CLSH_STATE_HPHR:
+ case WCD_CLSH_STATE_HPH_ST:
+ case WCD_CLSH_STATE_LO:
+ case WCD_CLSH_STATE_HPHL_EAR:
+ case WCD_CLSH_STATE_HPHR_EAR:
+ case WCD_CLSH_STATE_HPH_ST_EAR:
+ case WCD_CLSH_STATE_HPHL_LO:
+ case WCD_CLSH_STATE_HPHR_LO:
+ case WCD_CLSH_STATE_HPH_ST_LO:
+ case WCD_CLSH_STATE_EAR_LO:
+ return true;
+ default:
+ return false;
+ };
+}
+
+/*
+ * Function: wcd_clsh_fsm
+ * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event
+ * Description:
+ * This function handles PRE DAC and POST DAC conditions of different devices
+ * and updates class H configuration of different combination of devices
+ * based on validity of their states. cdc_clsh_d will contain current
+ * class h state information
+ */
+void wcd_clsh_fsm(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *cdc_clsh_d,
+ u8 clsh_event, u8 req_state,
+ int int_mode)
+{
+ u8 old_state, new_state;
+ char msg0[128], msg1[128];
+
+ switch (clsh_event) {
+ case WCD_CLSH_EVENT_PRE_DAC:
+ old_state = cdc_clsh_d->state;
+ new_state = old_state | req_state;
+
+ if (!wcd_clsh_is_state_valid(new_state)) {
+ dev_err(codec->dev,
+ "%s: Class-H not a valid new state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0, sizeof(msg0)));
+ return;
+ }
+ if (new_state == old_state) {
+ dev_err(codec->dev,
+ "%s: Class-H already in requested state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0, sizeof(msg0)));
+ return;
+ }
+ cdc_clsh_d->state = new_state;
+ wcd_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode);
+ (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state,
+ CLSH_REQ_ENABLE, int_mode);
+ dev_dbg(codec->dev,
+ "%s: ClassH state transition from %s to %s\n",
+ __func__, state_to_str(old_state, msg0, sizeof(msg0)),
+ state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1)));
+ break;
+ case WCD_CLSH_EVENT_POST_PA:
+ old_state = cdc_clsh_d->state;
+ new_state = old_state & (~req_state);
+ if (new_state < NUM_CLSH_STATES_V2) {
+ if (!wcd_clsh_is_state_valid(old_state)) {
+ dev_err(codec->dev,
+ "%s:Invalid old state:%s\n",
+ __func__,
+ state_to_str(old_state, msg0,
+ sizeof(msg0)));
+ return;
+ }
+ if (new_state == old_state) {
+ dev_err(codec->dev,
+ "%s: Class-H already in requested state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0,
+ sizeof(msg0)));
+ return;
+ }
+ (*clsh_state_fp[old_state]) (codec, cdc_clsh_d,
+ req_state, CLSH_REQ_DISABLE,
+ int_mode);
+ cdc_clsh_d->state = new_state;
+ wcd_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE);
+ dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n",
+ __func__, state_to_str(old_state, msg0,
+ sizeof(msg0)),
+ state_to_str(cdc_clsh_d->state, msg1,
+ sizeof(msg1)));
+ }
+ break;
+ };
+}
+
+int wcd_clsh_get_clsh_state(struct wcd_clsh_cdc_data *clsh)
+{
+ return clsh->state;
+}
+EXPORT_SYMBOL(wcd_clsh_get_clsh_state);
+
+void wcd_clsh_init(struct wcd_clsh_cdc_data *clsh)
+{
+ int i;
+ clsh->state = WCD_CLSH_STATE_IDLE;
+
+ for (i = 0; i < NUM_CLSH_STATES_V2; i++)
+ clsh_state_fp[i] = wcd_clsh_state_err;
+
+ clsh_state_fp[WCD_CLSH_STATE_EAR] = wcd_clsh_state_ear;
+ clsh_state_fp[WCD_CLSH_STATE_HPHL] =
+ wcd_clsh_state_hph_l;
+ clsh_state_fp[WCD_CLSH_STATE_HPHR] =
+ wcd_clsh_state_hph_r;
+ clsh_state_fp[WCD_CLSH_STATE_HPH_ST] =
+ wcd_clsh_state_hph_st;
+ clsh_state_fp[WCD_CLSH_STATE_LO] = wcd_clsh_state_lo;
+ clsh_state_fp[WCD_CLSH_STATE_HPHL_EAR] =
+ wcd_clsh_state_hph_ear;
+ clsh_state_fp[WCD_CLSH_STATE_HPHR_EAR] =
+ wcd_clsh_state_hph_ear;
+ clsh_state_fp[WCD_CLSH_STATE_HPH_ST_EAR] =
+ wcd_clsh_state_hph_ear;
+ clsh_state_fp[WCD_CLSH_STATE_HPHL_LO] = wcd_clsh_state_hph_lo;
+ clsh_state_fp[WCD_CLSH_STATE_HPHR_LO] = wcd_clsh_state_hph_lo;
+ clsh_state_fp[WCD_CLSH_STATE_HPH_ST_LO] =
+ wcd_clsh_state_hph_lo;
+ clsh_state_fp[WCD_CLSH_STATE_EAR_LO] = wcd_clsh_state_ear_lo;
+ /* Set interpolaotr modes to NONE */
+ wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_EAR, CLS_NONE);
+ wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHL, CLS_NONE);
+ wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHR, CLS_NONE);
+ wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_LO, CLS_NONE);
+ clsh->flyback_users = 0;
+ clsh->buck_users = 0;
+ clsh->clsh_users = 0;
+}
+EXPORT_SYMBOL(wcd_clsh_init);
+
+MODULE_DESCRIPTION("WCD9XXX Common Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-common-v2.h b/sound/soc/codecs/wcd9xxx-common-v2.h
new file mode 100644
index 000000000000..53c9a84b51ad
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-common-v2.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WCD9XXX_COMMON_V2
+
+#define _WCD9XXX_COMMON_V2
+
+#define CLSH_REQ_ENABLE true
+#define CLSH_REQ_DISABLE false
+
+#define WCD_CLSH_EVENT_PRE_DAC 0x01
+#define WCD_CLSH_EVENT_POST_PA 0x02
+#define MAX_VBAT_MONITOR_WRITES 17
+/*
+ * Basic states for Class H state machine.
+ * represented as a bit mask within a u8 data type
+ * bit 0: EAR mode
+ * bit 1: HPH Left mode
+ * bit 2: HPH Right mode
+ * bit 3: Lineout mode
+ */
+#define WCD_CLSH_STATE_IDLE 0x00
+#define WCD_CLSH_STATE_EAR (0x01 << 0)
+#define WCD_CLSH_STATE_HPHL (0x01 << 1)
+#define WCD_CLSH_STATE_HPHR (0x01 << 2)
+#define WCD_CLSH_STATE_LO (0x01 << 3)
+
+/*
+ * Though number of CLSH states are 4, max state shoulbe be 5
+ * because state array index starts from 1.
+ */
+#define WCD_CLSH_STATE_MAX 5
+#define NUM_CLSH_STATES_V2 (0x01 << WCD_CLSH_STATE_MAX)
+
+
+/* Derived State: Bits 1 and 2 should be set for Headphone stereo */
+#define WCD_CLSH_STATE_HPH_ST (WCD_CLSH_STATE_HPHL | \
+ WCD_CLSH_STATE_HPHR)
+
+#define WCD_CLSH_STATE_HPHL_LO (WCD_CLSH_STATE_HPHL | \
+ WCD_CLSH_STATE_LO)
+#define WCD_CLSH_STATE_HPHR_LO (WCD_CLSH_STATE_HPHR | \
+ WCD_CLSH_STATE_LO)
+#define WCD_CLSH_STATE_HPH_ST_LO (WCD_CLSH_STATE_HPH_ST | \
+ WCD_CLSH_STATE_LO)
+#define WCD_CLSH_STATE_EAR_LO (WCD_CLSH_STATE_EAR | \
+ WCD_CLSH_STATE_LO)
+#define WCD_CLSH_STATE_HPHL_EAR (WCD_CLSH_STATE_HPHL | \
+ WCD_CLSH_STATE_EAR)
+#define WCD_CLSH_STATE_HPHR_EAR (WCD_CLSH_STATE_HPHR | \
+ WCD_CLSH_STATE_EAR)
+#define WCD_CLSH_STATE_HPH_ST_EAR (WCD_CLSH_STATE_HPH_ST | \
+ WCD_CLSH_STATE_EAR)
+
+enum {
+ CLS_H_NORMAL = 0, /* Class-H Default */
+ CLS_H_HIFI, /* Class-H HiFi */
+ CLS_H_LP, /* Class-H Low Power */
+ CLS_AB, /* Class-AB Low HIFI*/
+ CLS_H_LOHIFI, /* LoHIFI */
+ CLS_H_ULP, /* Ultra Low power */
+ CLS_AB_HIFI, /* Class-AB */
+ CLS_NONE, /* None of the above modes */
+};
+
+/* Class H data that the codec driver will maintain */
+struct wcd_clsh_cdc_data {
+ u8 state;
+ int flyback_users;
+ int buck_users;
+ int clsh_users;
+ int interpolator_modes[WCD_CLSH_STATE_MAX];
+};
+
+struct wcd_mad_audio_header {
+ u32 reserved[3];
+ u32 num_reg_cfg;
+};
+
+struct wcd_mad_microphone_info {
+ uint8_t input_microphone;
+ uint8_t cycle_time;
+ uint8_t settle_time;
+ uint8_t padding;
+} __packed;
+
+struct wcd_mad_micbias_info {
+ uint8_t micbias;
+ uint8_t k_factor;
+ uint8_t external_bypass_capacitor;
+ uint8_t internal_biasing;
+ uint8_t cfilter;
+ uint8_t padding[3];
+} __packed;
+
+struct wcd_mad_rms_audio_beacon_info {
+ uint8_t rms_omit_samples;
+ uint8_t rms_comp_time;
+ uint8_t detection_mechanism;
+ uint8_t rms_diff_threshold;
+ uint8_t rms_threshold_lsb;
+ uint8_t rms_threshold_msb;
+ uint8_t padding[2];
+ uint8_t iir_coefficients[36];
+} __packed;
+
+struct wcd_mad_rms_ultrasound_info {
+ uint8_t rms_comp_time;
+ uint8_t detection_mechanism;
+ uint8_t rms_diff_threshold;
+ uint8_t rms_threshold_lsb;
+ uint8_t rms_threshold_msb;
+ uint8_t padding[3];
+ uint8_t iir_coefficients[36];
+} __packed;
+
+struct wcd_mad_audio_cal {
+ uint32_t version;
+ struct wcd_mad_microphone_info microphone_info;
+ struct wcd_mad_micbias_info micbias_info;
+ struct wcd_mad_rms_audio_beacon_info audio_info;
+ struct wcd_mad_rms_audio_beacon_info beacon_info;
+ struct wcd_mad_rms_ultrasound_info ultrasound_info;
+} __packed;
+
+struct wcd9xxx_anc_header {
+ u32 reserved[3];
+ u32 num_anc_slots;
+};
+
+struct vbat_monitor_reg {
+ u32 size;
+ u32 writes[MAX_VBAT_MONITOR_WRITES];
+} __packed;
+
+struct wcd_reg_mask_val {
+ u16 reg;
+ u8 mask;
+ u8 val;
+};
+
+extern void wcd_clsh_fsm(struct snd_soc_codec *codec,
+ struct wcd_clsh_cdc_data *cdc_clsh_d,
+ u8 clsh_event, u8 req_state,
+ int int_mode);
+
+extern void wcd_clsh_init(struct wcd_clsh_cdc_data *clsh);
+extern int wcd_clsh_get_clsh_state(struct wcd_clsh_cdc_data *clsh);
+extern void wcd_clsh_imped_config(struct snd_soc_codec *codec, int imped,
+ bool reset);
+
+enum {
+ RESERVED = 0,
+ AANC_LPF_FF_FB = 1,
+ AANC_LPF_COEFF_MSB,
+ AANC_LPF_COEFF_LSB,
+ HW_MAD_AUDIO_ENABLE,
+ HW_MAD_ULTR_ENABLE,
+ HW_MAD_BEACON_ENABLE,
+ HW_MAD_AUDIO_SLEEP_TIME,
+ HW_MAD_ULTR_SLEEP_TIME,
+ HW_MAD_BEACON_SLEEP_TIME,
+ HW_MAD_TX_AUDIO_SWITCH_OFF,
+ HW_MAD_TX_ULTR_SWITCH_OFF,
+ HW_MAD_TX_BEACON_SWITCH_OFF,
+ MAD_AUDIO_INT_DEST_SELECT_REG,
+ MAD_ULT_INT_DEST_SELECT_REG,
+ MAD_BEACON_INT_DEST_SELECT_REG,
+ MAD_CLIP_INT_DEST_SELECT_REG,
+ VBAT_INT_DEST_SELECT_REG,
+ MAD_AUDIO_INT_MASK_REG,
+ MAD_ULT_INT_MASK_REG,
+ MAD_BEACON_INT_MASK_REG,
+ MAD_CLIP_INT_MASK_REG,
+ VBAT_INT_MASK_REG,
+ MAD_AUDIO_INT_STATUS_REG,
+ MAD_ULT_INT_STATUS_REG,
+ MAD_BEACON_INT_STATUS_REG,
+ MAD_CLIP_INT_STATUS_REG,
+ VBAT_INT_STATUS_REG,
+ MAD_AUDIO_INT_CLEAR_REG,
+ MAD_ULT_INT_CLEAR_REG,
+ MAD_BEACON_INT_CLEAR_REG,
+ MAD_CLIP_INT_CLEAR_REG,
+ VBAT_INT_CLEAR_REG,
+ SB_PGD_PORT_TX_WATERMARK_N,
+ SB_PGD_PORT_TX_ENABLE_N,
+ SB_PGD_PORT_RX_WATERMARK_N,
+ SB_PGD_PORT_RX_ENABLE_N,
+ SB_PGD_TX_PORTn_MULTI_CHNL_0,
+ SB_PGD_TX_PORTn_MULTI_CHNL_1,
+ SB_PGD_RX_PORTn_MULTI_CHNL_0,
+ SB_PGD_RX_PORTn_MULTI_CHNL_1,
+ AANC_FF_GAIN_ADAPTIVE,
+ AANC_FFGAIN_ADAPTIVE_EN,
+ AANC_GAIN_CONTROL,
+ SPKR_CLIP_PIPE_BANK_SEL,
+ SPKR_CLIPDET_VAL0,
+ SPKR_CLIPDET_VAL1,
+ SPKR_CLIPDET_VAL2,
+ SPKR_CLIPDET_VAL3,
+ SPKR_CLIPDET_VAL4,
+ SPKR_CLIPDET_VAL5,
+ SPKR_CLIPDET_VAL6,
+ SPKR_CLIPDET_VAL7,
+ VBAT_RELEASE_INT_DEST_SELECT_REG,
+ VBAT_RELEASE_INT_MASK_REG,
+ VBAT_RELEASE_INT_STATUS_REG,
+ VBAT_RELEASE_INT_CLEAR_REG,
+ MAD2_CLIP_INT_DEST_SELECT_REG,
+ MAD2_CLIP_INT_MASK_REG,
+ MAD2_CLIP_INT_STATUS_REG,
+ MAD2_CLIP_INT_CLEAR_REG,
+ SPKR2_CLIP_PIPE_BANK_SEL,
+ SPKR2_CLIPDET_VAL0,
+ SPKR2_CLIPDET_VAL1,
+ SPKR2_CLIPDET_VAL2,
+ SPKR2_CLIPDET_VAL3,
+ SPKR2_CLIPDET_VAL4,
+ SPKR2_CLIPDET_VAL5,
+ SPKR2_CLIPDET_VAL6,
+ SPKR2_CLIPDET_VAL7,
+ MAX_CFG_REGISTERS,
+};
+
+#endif
diff --git a/sound/soc/codecs/wcd9xxx-common.c b/sound/soc/codecs/wcd9xxx-common.c
new file mode 100644
index 000000000000..6d6eb114e898
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-common.c
@@ -0,0 +1,1478 @@
+/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include "wcd9xxx-common.h"
+
+#define CLSH_COMPUTE_EAR 0x01
+#define CLSH_COMPUTE_HPH_L 0x02
+#define CLSH_COMPUTE_HPH_R 0x03
+
+#define BUCK_VREF_0P494V 0x3F
+#define BUCK_VREF_2V 0xFF
+#define BUCK_VREF_0P494V 0x3F
+#define BUCK_VREF_1P8V 0xE6
+
+#define BUCK_SETTLE_TIME_US 50
+#define NCP_SETTLE_TIME_US 50
+
+#define MAX_IMPED_PARAMS 13
+
+#define USLEEP_RANGE_MARGIN_US 100
+
+struct wcd9xxx_imped_val {
+ u32 imped_val;
+ u8 index;
+};
+
+static const struct wcd9xxx_reg_mask_val imped_table[][MAX_IMPED_PARAMS] = {
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x46},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x15},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x04},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0C},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x47},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x15},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0C},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x49},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x12},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x35},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x4E},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0E},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x49},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x16},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x17},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x5F},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xCF},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0F},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x59},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x15},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xCE},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xBD},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x10},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x66},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9A},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xBD},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA6},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x11},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x79},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x37},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA6},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAD},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x08},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x12},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x76},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x4E},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAD},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x09},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x12},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x78},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x12},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xD0},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0A},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x13},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x7A},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xB7},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0B},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x14},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x60},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x09},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA4},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1F},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0C},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x14},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x79},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x17},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1F},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0D},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x15},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x78},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x16},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0E},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x89},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x40},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x10},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x97},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xD0},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x12},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x17},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8A},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xB7},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x10},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x13},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x17},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8A},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA4},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x15},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x18},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x9A},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x08},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x27},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x18},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8B},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x18},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x20},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1A},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x9A},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x17},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1D},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x1A},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xA9},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1F},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xB9},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x10},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x23},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x18},
+ },
+ {
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xA9},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x27},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x35},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x26},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16},
+ },
+};
+
+static const struct wcd9xxx_imped_val imped_index[] = {
+ {4000, 0},
+ {4500, 1},
+ {5000, 2},
+ {5500, 3},
+ {6000, 4},
+ {6500, 5},
+ {7000, 6},
+ {7700, 7},
+ {8470, 8},
+ {9317, 9},
+ {10248, 10},
+ {11273, 11},
+ {12400, 12},
+ {13641, 13},
+ {15005, 14},
+ {16505, 15},
+ {18156, 16},
+ {19971, 17},
+ {21969, 18},
+ {24165, 19},
+ {26582, 20},
+ {29240, 21},
+ {32164, 22},
+};
+
+static inline void
+wcd9xxx_enable_clsh_block(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d, bool enable)
+{
+ if ((enable && ++clsh_d->clsh_users == 1) ||
+ (!enable && --clsh_d->clsh_users == 0))
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL,
+ 0x01, enable ? 0x01 : 0x00);
+ dev_dbg(codec->dev, "%s: clsh_users %d, enable %d", __func__,
+ clsh_d->clsh_users, enable);
+}
+
+static inline void wcd9xxx_enable_anc_delay(
+ struct snd_soc_codec *codec,
+ bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL,
+ 0x02, on ? 0x02 : 0x00);
+}
+
+static inline void
+wcd9xxx_enable_buck(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d, bool enable)
+{
+ if ((enable && ++clsh_d->buck_users == 1) ||
+ (!enable && --clsh_d->buck_users == 0))
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1,
+ 0x80, enable ? 0x80 : 0x00);
+ dev_dbg(codec->dev, "%s: buck_users %d, enable %d", __func__,
+ clsh_d->buck_users, enable);
+}
+
+static void (*clsh_state_fp[NUM_CLSH_STATES])(struct snd_soc_codec *,
+ struct wcd9xxx_clsh_cdc_data *,
+ u8 req_state, bool req_type);
+
+static const char *state_to_str(u8 state, char *buf, size_t buflen)
+{
+ int i;
+ int cnt = 0;
+ /*
+ * This array of strings should match with enum wcd9xxx_clsh_state_bit.
+ */
+ const char *states[] = {
+ "STATE_EAR",
+ "STATE_HPH_L",
+ "STATE_HPH_R",
+ "STATE_LO",
+ };
+
+ if (state == WCD9XXX_CLSH_STATE_IDLE) {
+ snprintf(buf, buflen, "[STATE_IDLE]");
+ goto done;
+ }
+
+ buf[0] = '\0';
+ for (i = 0; i < ARRAY_SIZE(states); i++) {
+ if (!(state & (1 << i)))
+ continue;
+ cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf,
+ buf[0] == '\0' ? "[" : "|",
+ states[i]);
+ }
+ if (cnt > 0)
+ strlcat(buf + cnt, "]", buflen);
+
+done:
+ if (buf[0] == '\0')
+ snprintf(buf, buflen, "[STATE_UNKNOWN]");
+ return buf;
+}
+
+static void wcd9xxx_cfg_clsh_param_common(
+ struct snd_soc_codec *codec)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, 0x3 << 0, 0},
+ {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, 0x3 << 2, 1 << 2},
+ {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, (0x1 << 4), 0},
+ {WCD9XXX_A_CDC_CLSH_B2_CTL, (0x3 << 0), 0x01},
+ {WCD9XXX_A_CDC_CLSH_B2_CTL, (0x3 << 2), (0x01 << 2)},
+ {WCD9XXX_A_CDC_CLSH_B2_CTL, (0xf << 4), (0x03 << 4)},
+ {WCD9XXX_A_CDC_CLSH_B3_CTL, (0xf << 4), (0x03 << 4)},
+ {WCD9XXX_A_CDC_CLSH_B3_CTL, (0xf << 0), (0x0B)},
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 5), (0x01 << 5)},
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 1), (0x01 << 1)},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask,
+ reg_set[i].val);
+
+ dev_dbg(codec->dev, "%s: Programmed class H controller common parameters",
+ __func__);
+}
+
+static void wcd9xxx_chargepump_request(struct snd_soc_codec *codec, bool on)
+{
+ static int cp_count;
+
+ if (on && (++cp_count == 1)) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+ 0x01, 0x01);
+ dev_dbg(codec->dev, "%s: Charge Pump enabled, count = %d\n",
+ __func__, cp_count);
+ } else if (!on) {
+ if (--cp_count < 0) {
+ dev_dbg(codec->dev,
+ "%s: Unbalanced disable for charge pump\n",
+ __func__);
+ if (snd_soc_read(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL) &
+ 0x01) {
+ dev_dbg(codec->dev,
+ "%s: Actual chargepump is ON\n",
+ __func__);
+ }
+ cp_count = 0;
+ WARN_ON(1);
+ }
+
+ if (cp_count == 0) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+ 0x01, 0x00);
+ dev_dbg(codec->dev,
+ "%s: Charge pump disabled, count = %d\n",
+ __func__, cp_count);
+ }
+ }
+}
+
+void wcd9xxx_enable_high_perf_mode(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 uhqa_mode, u8 req_state, bool req_type)
+{
+ dev_dbg(codec->dev, "%s: users fclk8 %d, fclk5 %d", __func__,
+ clsh_d->ncp_users[NCP_FCLK_LEVEL_8],
+ clsh_d->ncp_users[NCP_FCLK_LEVEL_5]);
+
+ if (req_type == WCD9XXX_CLSAB_REQ_ENABLE) {
+ clsh_d->ncp_users[NCP_FCLK_LEVEL_8]++;
+ snd_soc_write(codec, WCD9XXX_A_RX_HPH_BIAS_PA,
+ WCD9XXX_A_RX_HPH_BIAS_PA__POR);
+ snd_soc_write(codec, WCD9XXX_A_RX_HPH_L_PA_CTL, 0x48);
+ snd_soc_write(codec, WCD9XXX_A_RX_HPH_R_PA_CTL, 0x48);
+ if (uhqa_mode)
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CHOP_CTL,
+ 0x20, 0x00);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ wcd9xxx_enable_buck(codec, clsh_d, false);
+ if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] > 0)
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 0x0F, 0x08);
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x30, 0x30);
+
+ /* Enable NCP and wait until settles down */
+ if (snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN, 0x01, 0x01))
+ usleep_range(NCP_SETTLE_TIME_US, NCP_SETTLE_TIME_US+10);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CHOP_CTL,
+ 0x20, 0x20);
+ snd_soc_write(codec, WCD9XXX_A_RX_HPH_L_PA_CTL,
+ WCD9XXX_A_RX_HPH_L_PA_CTL__POR);
+ snd_soc_write(codec, WCD9XXX_A_RX_HPH_R_PA_CTL,
+ WCD9XXX_A_RX_HPH_R_PA_CTL__POR);
+ snd_soc_write(codec, WCD9XXX_A_RX_HPH_BIAS_PA, 0x57);
+ wcd9xxx_enable_buck(codec, clsh_d, true);
+ wcd9xxx_chargepump_request(codec, false);
+ wcd9xxx_enable_anc_delay(codec, false);
+ clsh_d->ncp_users[NCP_FCLK_LEVEL_8]--;
+ if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0 &&
+ clsh_d->ncp_users[NCP_FCLK_LEVEL_5] == 0)
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN,
+ 0x01, 0x00);
+ else if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0)
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 0x0F, 0x05);
+ }
+ dev_dbg(codec->dev, "%s: leave\n", __func__);
+}
+EXPORT_SYMBOL(wcd9xxx_enable_high_perf_mode);
+
+static int get_impedance_index(u32 imped)
+{
+ int i = 0;
+ if (imped < imped_index[i].imped_val) {
+ pr_debug("%s, detected impedance is less than 4 Ohm\n",
+ __func__);
+ goto ret;
+ }
+ if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) {
+ pr_debug("%s, detected impedance is greater than 32164 Ohm\n",
+ __func__);
+ i = ARRAY_SIZE(imped_index) - 1;
+ goto ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) {
+ if (imped >= imped_index[i].imped_val &&
+ imped < imped_index[i + 1].imped_val)
+ break;
+ }
+ret:
+ pr_debug("%s: selected impedance index = %d\n",
+ __func__, imped_index[i].index);
+ return imped_index[i].index;
+}
+
+void wcd9xxx_clsh_imped_config(struct snd_soc_codec *codec,
+ int imped)
+{
+ int i = 0;
+ int index = 0;
+ index = get_impedance_index(imped);
+ if (index >= ARRAY_SIZE(imped_index)) {
+ pr_err("%s, invalid imped = %d\n", __func__, imped);
+ return;
+ }
+ for (i = 0; i < MAX_IMPED_PARAMS; i++)
+ snd_soc_write(codec, imped_table[index][i].reg,
+ imped_table[index][i].val);
+}
+
+static void wcd9xxx_clsh_comp_req(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ int compute_pa, bool on)
+{
+ u8 shift;
+
+ if (compute_pa == CLSH_COMPUTE_EAR) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL, 0x10,
+ (on ? 0x10 : 0));
+ } else {
+ if (compute_pa == CLSH_COMPUTE_HPH_L) {
+ shift = 3;
+ } else if (compute_pa == CLSH_COMPUTE_HPH_R) {
+ shift = 2;
+ } else {
+ dev_dbg(codec->dev,
+ "%s: classh computation request is incorrect\n",
+ __func__);
+ return;
+ }
+
+ if (on)
+ wcd9xxx_resmgr_add_cond_update_bits(clsh_d->resmgr,
+ WCD9XXX_COND_HPH,
+ WCD9XXX_A_CDC_CLSH_B1_CTL,
+ shift, false);
+ else
+ wcd9xxx_resmgr_rm_cond_update_bits(clsh_d->resmgr,
+ WCD9XXX_COND_HPH,
+ WCD9XXX_A_CDC_CLSH_B1_CTL,
+ shift, false);
+ }
+}
+
+int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec,
+ struct list_head *list,
+ uint16_t reg, uint8_t mask,
+ uint8_t value, int delay)
+{
+ int rc;
+ struct wcd9xxx_register_save_node *node;
+
+ node = kmalloc(sizeof(*node), GFP_KERNEL);
+ if (unlikely(!node)) {
+ pr_err("%s: Not enough memory\n", __func__);
+ return -ENOMEM;
+ }
+ node->reg = reg;
+ node->value = snd_soc_read(codec, reg);
+ list_add(&node->lh, list);
+ if (mask == 0xFF)
+ rc = snd_soc_write(codec, reg, value);
+ else
+ rc = snd_soc_update_bits(codec, reg, mask, value);
+ if (delay)
+ usleep_range(delay, delay + USLEEP_RANGE_MARGIN_US);
+ return rc;
+}
+EXPORT_SYMBOL(wcd9xxx_soc_update_bits_push);
+
+void wcd9xxx_restore_registers(struct snd_soc_codec *codec,
+ struct list_head *lh)
+{
+ struct wcd9xxx_register_save_node *node, *nodetmp;
+
+ list_for_each_entry_safe(node, nodetmp, lh, lh) {
+ snd_soc_write(codec, node->reg, node->value);
+ list_del(&node->lh);
+ kfree(node);
+ }
+}
+EXPORT_SYMBOL(wcd9xxx_restore_registers);
+
+static void wcd9xxx_dynamic_bypass_buck_ctrl_lo(struct snd_soc_codec *cdc,
+ bool enable)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_BUCK_MODE_3, (0x1 << 3), (enable << 3)},
+ {WCD9XXX_A_BUCK_MODE_5, enable ? 0xFF : 0x02, 0x02},
+ {WCD9XXX_A_BUCK_MODE_5, 0x1, 0x01}
+ };
+
+ if (!enable) {
+ snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_1,
+ (0x1 << 3), 0x00);
+ snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_4,
+ 0xFF, BUCK_VREF_2V);
+ }
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(cdc, reg_set[i].reg, reg_set[i].mask,
+ reg_set[i].val);
+
+ /* 50us sleep is reqd. as per the class H HW design sequence */
+ usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10);
+}
+
+static void wcd9xxx_dynamic_bypass_buck_ctrl(struct snd_soc_codec *cdc,
+ bool enable)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_BUCK_MODE_3, (0x1 << 3), (enable << 3)},
+ {WCD9XXX_A_BUCK_MODE_5, (0x1 << 1), ((!enable) << 1)},
+ {WCD9XXX_A_BUCK_MODE_5, 0x1, !enable}
+ };
+ if (!enable) {
+ snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_1,
+ (0x1 << 3), 0x00);
+ snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_4,
+ 0xFF, BUCK_VREF_2V);
+ }
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(cdc, reg_set[i].reg, reg_set[i].mask,
+ reg_set[i].val);
+
+ /* 50us sleep is reqd. as per the class H HW design sequence */
+ usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10);
+}
+
+static void wcd9xxx_set_buck_mode(struct snd_soc_codec *codec, u8 buck_vref)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_BUCK_MODE_5, 0x02, 0x02},
+ {WCD9XXX_A_BUCK_MODE_4, 0xFF, buck_vref},
+ {WCD9XXX_A_BUCK_MODE_1, 0x04, 0x04},
+ {WCD9XXX_A_BUCK_MODE_3, 0x04, 0x00},
+ {WCD9XXX_A_BUCK_MODE_3, 0x08, 0x00},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg,
+ reg_set[i].mask, reg_set[i].val);
+
+ dev_dbg(codec->dev, "%s: Done\n", __func__);
+ usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US);
+}
+
+
+/* This will be called for all states except Lineout */
+static void wcd9xxx_clsh_enable_post_pa(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *cdc_clsh_d)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_BUCK_MODE_5, 0x02, 0x00},
+ {WCD9XXX_A_NCP_STATIC, 0x20, 0x00},
+ {WCD9XXX_A_BUCK_MODE_3, 0x04, 0x04},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg,
+ reg_set[i].mask, reg_set[i].val);
+
+ if (!cdc_clsh_d->is_dynamic_vdd_cp)
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_3,
+ 0x08, 0x08);
+
+ dev_dbg(codec->dev, "%s: completed clsh mode settings after PA enable\n",
+ __func__);
+
+}
+
+static void wcd9xxx_set_fclk_get_ncp(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ enum ncp_fclk_level fclk_level)
+{
+ clsh_d->ncp_users[fclk_level]++;
+
+ pr_debug("%s: enter ncp type %d users fclk8 %d, fclk5 %d\n", __func__,
+ fclk_level, clsh_d->ncp_users[NCP_FCLK_LEVEL_8],
+ clsh_d->ncp_users[NCP_FCLK_LEVEL_5]);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x10, 0x00);
+ /* fclk level 8 dominates level 5 */
+ if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] > 0)
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x0F, 0x08);
+ else if (clsh_d->ncp_users[NCP_FCLK_LEVEL_5] > 0)
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x0F, 0x05);
+ else
+ WARN_ONCE(1, "Unexpected users %d,%d\n",
+ clsh_d->ncp_users[NCP_FCLK_LEVEL_8],
+ clsh_d->ncp_users[NCP_FCLK_LEVEL_5]);
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x20, 0x20);
+
+ /* enable NCP and wait until settles down */
+ if (snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN, 0x01, 0x01))
+ usleep_range(NCP_SETTLE_TIME_US, NCP_SETTLE_TIME_US + 50);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_set_fclk_put_ncp(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ enum ncp_fclk_level fclk_level)
+{
+ clsh_d->ncp_users[fclk_level]--;
+
+ pr_debug("%s: enter ncp type %d users fclk8 %d, fclk5 %d\n", __func__,
+ fclk_level, clsh_d->ncp_users[NCP_FCLK_LEVEL_8],
+ clsh_d->ncp_users[NCP_FCLK_LEVEL_5]);
+
+ if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0 &&
+ clsh_d->ncp_users[NCP_FCLK_LEVEL_5] == 0)
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN, 0x01, 0x00);
+ else if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0)
+ /* if dominating level 8 has gone, switch to 5 */
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x0F, 0x05);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_cfg_clsh_param_ear(struct snd_soc_codec *codec)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 7), 0},
+ {WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR, (0x3f << 0), 0x0D},
+ {WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR, (0x3f << 0), 0x3A},
+
+ /* Under assumption that EAR load is 10.7ohm */
+ {WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD, (0x3f << 0), 0x26},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD, (0x3f << 0), 0x2C},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L, 0xff, 0xA9},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, (0x1 << 7), 0},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, (0xf << 0), 0x08},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1b},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2d},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x36},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x37},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg,
+ reg_set[i].mask, reg_set[i].val);
+
+ dev_dbg(codec->dev, "%s: Programmed Class H controller EAR specific params\n",
+ __func__);
+}
+
+static void wcd9xxx_cfg_clsh_param_hph(struct snd_soc_codec *codec)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 6), 0},
+ {WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH, 0x3f, 0x0D},
+ {WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH, 0x3f, 0x1D},
+
+ /* Under assumption that HPH load is 16ohm per channel */
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0x3f, 0x13},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0x1f, 0x19},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x97},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, (0x1 << 7), 0},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0x0f, 0},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask,
+ reg_set[i].val);
+ dev_dbg(codec->dev, "%s: Programmed Class H controller HPH specific params\n",
+ __func__);
+}
+
+static void wcd9xxx_ncp_bypass_enable(struct snd_soc_codec *cdc, bool enable)
+{
+ snd_soc_update_bits(cdc, WCD9XXX_A_NCP_STATIC, 0x10, (enable << 4));
+ /* 50us sleep is reqd. as per the class H HW design sequence */
+ usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10);
+}
+
+static void wcd9xxx_clsh_set_Iest(struct snd_soc_codec *codec,
+ u8 value)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+ 0x01, (0x01 & 0x03));
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+ 0xFC, (value << 2));
+}
+
+static void wcd9xxx_clsh_state_hph_ear(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ int compute_pa = 0;
+
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+
+ if (is_enable) {
+ /*
+ * The below check condition is required to make sure
+ * functions inside if condition will execute only once.
+ */
+ if ((clsh_d->state == WCD9XXX_CLSH_STATE_EAR) ||
+ (req_state == WCD9XXX_CLSH_STATE_EAR)) {
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, false);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ }
+ switch (req_state) {
+ case WCD9XXX_CLSH_STATE_HPHL:
+ compute_pa = CLSH_COMPUTE_HPH_L;
+ break;
+ case WCD9XXX_CLSH_STATE_HPHR:
+ compute_pa = CLSH_COMPUTE_HPH_R;
+ break;
+ case WCD9XXX_CLSH_STATE_EAR:
+ compute_pa = CLSH_COMPUTE_EAR;
+ break;
+ default:
+ dev_dbg(codec->dev,
+ "%s:Invalid state:0x%x,enable:0x%x\n",
+ __func__, req_state, is_enable);
+ break;
+ }
+ wcd9xxx_clsh_comp_req(codec, clsh_d, compute_pa, true);
+
+ dev_dbg(codec->dev, "%s: Enabled hph+ear mode clsh\n",
+ __func__);
+ } else {
+ switch (req_state) {
+ case WCD9XXX_CLSH_STATE_HPHL:
+ compute_pa = CLSH_COMPUTE_HPH_L;
+ break;
+ case WCD9XXX_CLSH_STATE_HPHR:
+ compute_pa = CLSH_COMPUTE_HPH_R;
+ break;
+ case WCD9XXX_CLSH_STATE_EAR:
+ compute_pa = CLSH_COMPUTE_EAR;
+ break;
+ default:
+ dev_dbg(codec->dev,
+ "%s:Invalid state:0x%x,enable:0x%x\n",
+ __func__, req_state, is_enable);
+ break;
+ }
+ wcd9xxx_clsh_comp_req(codec, clsh_d, compute_pa, false);
+
+ if (((clsh_d->state & (~req_state)) ==
+ WCD9XXX_CLSH_STATE_EAR) ||
+ (req_state == WCD9XXX_CLSH_STATE_EAR)) {
+ wcd9xxx_ncp_bypass_enable(codec, false);
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, true);
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_lo(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+ if (is_enable) {
+ if ((clsh_d->state == WCD9XXX_CLSH_STATE_LO) ||
+ (req_state == WCD9XXX_CLSH_STATE_LO)) {
+ wcd9xxx_dynamic_bypass_buck_ctrl_lo(codec, false);
+ wcd9xxx_enable_buck(codec, clsh_d, true);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ if (req_state & WCD9XXX_CLSH_STATE_HPH_ST) {
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_8);
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_5);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ }
+ }
+ if (req_state == WCD9XXX_CLSH_STATE_HPHL)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_L, true);
+ if (req_state == WCD9XXX_CLSH_STATE_HPHR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_R, true);
+ } else {
+ switch (req_state) {
+ case WCD9XXX_CLSH_STATE_LO:
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 0x20, 0x00);
+ wcd9xxx_dynamic_bypass_buck_ctrl_lo(codec, true);
+ break;
+ case WCD9XXX_CLSH_STATE_HPHL:
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_L, false);
+ break;
+ case WCD9XXX_CLSH_STATE_HPHR:
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_R, false);
+ break;
+ default:
+ dev_dbg(codec->dev,
+ "%s:Invalid state:0x%x,enable:0x%x\n",
+ __func__, req_state, is_enable);
+ break;
+ }
+ if ((req_state == WCD9XXX_CLSH_STATE_LO) ||
+ ((clsh_d->state & (~req_state)) == WCD9XXX_CLSH_STATE_LO)) {
+ wcd9xxx_ncp_bypass_enable(codec, false);
+
+ if ((clsh_d->state & (~req_state)) ==
+ WCD9XXX_CLSH_STATE_LO) {
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_5);
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_8);
+ }
+
+ if (req_state & WCD9XXX_CLSH_STATE_HPH_ST) {
+ usleep_range(BUCK_SETTLE_TIME_US,
+ BUCK_SETTLE_TIME_US + 10);
+ if (clsh_d->buck_mv ==
+ WCD9XXX_CDC_BUCK_MV_1P8) {
+ wcd9xxx_enable_buck(codec, clsh_d,
+ false);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ } else {
+ /*
+ *NCP settle time recommended by codec
+ *specification
+ */
+ usleep_range(NCP_SETTLE_TIME_US,
+ NCP_SETTLE_TIME_US + 10);
+ wcd9xxx_clsh_set_Iest(codec, 0x02);
+ }
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_BUCK_MODE_1,
+ 0x04, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_BUCK_MODE_4,
+ 0xFF, BUCK_VREF_1P8V);
+ }
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_ear_lo(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+ if (is_enable) {
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, false);
+ wcd9xxx_enable_buck(codec, clsh_d, true);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ if (req_state & WCD9XXX_CLSH_STATE_EAR) {
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_8);
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_5);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_EAR, true);
+ }
+ } else {
+ wcd9xxx_ncp_bypass_enable(codec, false);
+
+ if ((clsh_d->state & (~req_state)) == WCD9XXX_CLSH_STATE_LO) {
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_5);
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_8);
+ }
+
+ if (req_state & WCD9XXX_CLSH_STATE_LO) {
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 0x20, 0x00);
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, true);
+ } else if (req_state & WCD9XXX_CLSH_STATE_EAR) {
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR,
+ false);
+ /*sleep 5ms*/
+ if (clsh_d->buck_mv == WCD9XXX_CDC_BUCK_MV_1P8) {
+ wcd9xxx_enable_buck(codec, clsh_d, false);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ } else {
+ /* NCP settle time recommended by codec spec */
+ usleep_range(NCP_SETTLE_TIME_US,
+ NCP_SETTLE_TIME_US + 10);
+ wcd9xxx_clsh_set_Iest(codec, 0x02);
+ }
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1,
+ 0x04, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_4,
+ 0xFF, BUCK_VREF_1P8V);
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_ear_lo(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+
+ if (req_state & WCD9XXX_CLSH_STATE_HPHL)
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L,
+ is_enable);
+
+ if (req_state & WCD9XXX_CLSH_STATE_HPHR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R,
+ is_enable);
+
+ if (req_state & WCD9XXX_CLSH_STATE_EAR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR,
+ is_enable);
+}
+
+static void wcd9xxx_clsh_state_ear(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable");
+ if (is_enable) {
+ wcd9xxx_cfg_clsh_param_common(codec);
+ wcd9xxx_cfg_clsh_param_ear(codec);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR, true);
+ wcd9xxx_set_buck_mode(codec, BUCK_VREF_2V);
+ wcd9xxx_enable_buck(codec, clsh_d, true);
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+
+ dev_dbg(codec->dev, "%s: Enabled ear mode class h\n", __func__);
+ } else {
+ dev_dbg(codec->dev, "%s: stub fallback to ear\n", __func__);
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+ wcd9xxx_enable_buck(codec, clsh_d, false);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR, false);
+ wcd9xxx_chargepump_request(codec, false);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, false);
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_l(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable");
+
+ if (is_enable) {
+ wcd9xxx_cfg_clsh_param_common(codec);
+ wcd9xxx_cfg_clsh_param_hph(codec);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, true);
+ wcd9xxx_set_buck_mode(codec, BUCK_VREF_0P494V);
+ wcd9xxx_enable_buck(codec, clsh_d, true);
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+
+ dev_dbg(codec->dev, "%s: Done\n", __func__);
+ } else {
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+ wcd9xxx_enable_buck(codec, clsh_d, false);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, false);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, false);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, false);
+ wcd9xxx_chargepump_request(codec, false);
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_r(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable");
+
+ if (is_enable) {
+ wcd9xxx_cfg_clsh_param_common(codec);
+ wcd9xxx_cfg_clsh_param_hph(codec);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, true);
+ wcd9xxx_set_buck_mode(codec, BUCK_VREF_0P494V);
+ wcd9xxx_enable_buck(codec, clsh_d, true);
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+
+ dev_dbg(codec->dev, "%s: Done\n", __func__);
+ } else {
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+ wcd9xxx_enable_buck(codec, clsh_d, false);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, false);
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, false);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, false);
+ wcd9xxx_chargepump_request(codec, false);
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_st(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable");
+
+ if (is_enable) {
+ dev_dbg(codec->dev, "%s: stub fallback to hph_st\n", __func__);
+ } else {
+ dev_dbg(codec->dev, "%s: stub fallback to hph_st\n", __func__);
+ }
+}
+
+static void wcd9xxx_clsh_state_lo(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ pr_debug("%s: enter %s, buck_mv %d\n", __func__,
+ is_enable ? "enable" : "disable", clsh_d->buck_mv);
+
+ if (is_enable) {
+ wcd9xxx_set_buck_mode(codec, BUCK_VREF_1P8V);
+ wcd9xxx_enable_buck(codec, clsh_d, true);
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_5);
+
+ if (clsh_d->buck_mv == WCD9XXX_CDC_BUCK_MV_1P8) {
+ wcd9xxx_enable_buck(codec, clsh_d, false);
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 1 << 4, 1 << 4);
+ /* NCP settle time recommended by codec specification */
+ usleep_range(NCP_SETTLE_TIME_US,
+ NCP_SETTLE_TIME_US + 10);
+ } else {
+ /* NCP settle time recommended by codec specification */
+ usleep_range(NCP_SETTLE_TIME_US,
+ NCP_SETTLE_TIME_US + 10);
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+ 0x01, (0x01 & 0x03));
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+ 0xFC, (0xFC & 0xB));
+ }
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1, 0x04, 0x00);
+ } else {
+ dev_dbg(codec->dev, "%s: stub fallback to lineout\n", __func__);
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_5);
+ if (clsh_d->buck_mv != WCD9XXX_CDC_BUCK_MV_1P8)
+ wcd9xxx_enable_buck(codec, clsh_d, false);
+ }
+}
+
+static void wcd9xxx_clsh_state_err(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ char msg[128];
+
+ dev_dbg(codec->dev,
+ "%s Wrong request for class H state machine requested to %s %s",
+ __func__, is_enable ? "enable" : "disable",
+ state_to_str(req_state, msg, sizeof(msg)));
+ WARN_ON(1);
+}
+
+/*
+ * Function: wcd9xxx_clsh_is_state_valid
+ * Params: state
+ * Description:
+ * Provides information on valid states of Class H configuration
+ */
+static int wcd9xxx_clsh_is_state_valid(u8 state)
+{
+ switch (state) {
+ case WCD9XXX_CLSH_STATE_IDLE:
+ case WCD9XXX_CLSH_STATE_EAR:
+ case WCD9XXX_CLSH_STATE_HPHL:
+ case WCD9XXX_CLSH_STATE_HPHR:
+ case WCD9XXX_CLSH_STATE_HPH_ST:
+ case WCD9XXX_CLSH_STATE_LO:
+ case WCD9XXX_CLSH_STATE_HPHL_EAR:
+ case WCD9XXX_CLSH_STATE_HPHR_EAR:
+ case WCD9XXX_CLSH_STATE_HPH_ST_EAR:
+ case WCD9XXX_CLSH_STATE_HPHL_LO:
+ case WCD9XXX_CLSH_STATE_HPHR_LO:
+ case WCD9XXX_CLSH_STATE_HPH_ST_LO:
+ case WCD9XXX_CLSH_STATE_EAR_LO:
+ case WCD9XXX_CLSH_STATE_HPHL_EAR_LO:
+ case WCD9XXX_CLSH_STATE_HPHR_EAR_LO:
+ case WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Function: wcd9xxx_clsh_fsm
+ * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event
+ * Description:
+ * This function handles PRE DAC and POST DAC conditions of different devices
+ * and updates class H configuration of different combination of devices
+ * based on validity of their states. cdc_clsh_d will contain current
+ * class h state information
+ */
+void wcd9xxx_clsh_fsm(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *cdc_clsh_d,
+ u8 req_state, bool req_type, u8 clsh_event)
+{
+ u8 old_state, new_state;
+ char msg0[128], msg1[128];
+
+ switch (clsh_event) {
+ case WCD9XXX_CLSH_EVENT_PRE_DAC:
+ /* PRE_DAC event should be used only for Enable */
+ BUG_ON(req_type != WCD9XXX_CLSH_REQ_ENABLE);
+
+ old_state = cdc_clsh_d->state;
+ new_state = old_state | req_state;
+
+ if (!wcd9xxx_clsh_is_state_valid(new_state)) {
+ dev_dbg(codec->dev,
+ "%s: classH not a valid new state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0, sizeof(msg0)));
+ return;
+ }
+ if (new_state == old_state) {
+ dev_dbg(codec->dev,
+ "%s: classH already in requested state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0, sizeof(msg0)));
+ return;
+ }
+ (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state,
+ req_type);
+ cdc_clsh_d->state = new_state;
+ dev_dbg(codec->dev,
+ "%s: ClassH state transition from %s to %s\n",
+ __func__, state_to_str(old_state, msg0, sizeof(msg0)),
+ state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1)));
+
+ break;
+ case WCD9XXX_CLSH_EVENT_POST_PA:
+ if (req_type == WCD9XXX_CLSH_REQ_DISABLE) {
+ old_state = cdc_clsh_d->state;
+ new_state = old_state & (~req_state);
+
+ if (new_state < NUM_CLSH_STATES) {
+ if (!wcd9xxx_clsh_is_state_valid(old_state)) {
+ dev_dbg(codec->dev,
+ "%s:Invalid old state:%s\n",
+ __func__,
+ state_to_str(old_state, msg0,
+ sizeof(msg0)));
+ return;
+ }
+ if (new_state == old_state) {
+ dev_dbg(codec->dev,
+ "%s: clsH already in old state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0,
+ sizeof(msg0)));
+ return;
+ }
+ (*clsh_state_fp[old_state]) (codec, cdc_clsh_d,
+ req_state,
+ req_type);
+ cdc_clsh_d->state = new_state;
+ dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n",
+ __func__, state_to_str(old_state, msg0,
+ sizeof(msg0)),
+ state_to_str(cdc_clsh_d->state, msg1,
+ sizeof(msg1)));
+
+ } else {
+ dev_dbg(codec->dev, "%s:wrong new state=0x%x\n",
+ __func__, new_state);
+ }
+ } else if (!(cdc_clsh_d->state & WCD9XXX_CLSH_STATE_LO)) {
+ wcd9xxx_clsh_enable_post_pa(codec, cdc_clsh_d);
+ }
+
+ break;
+ }
+
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_clsh_fsm);
+
+void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh,
+ struct wcd9xxx_resmgr *resmgr)
+{
+ int i;
+ clsh->state = WCD9XXX_CLSH_STATE_IDLE;
+ clsh->resmgr = resmgr;
+
+ for (i = 0; i < NUM_CLSH_STATES; i++)
+ clsh_state_fp[i] = wcd9xxx_clsh_state_err;
+
+ clsh_state_fp[WCD9XXX_CLSH_STATE_EAR] = wcd9xxx_clsh_state_ear;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL] =
+ wcd9xxx_clsh_state_hph_l;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR] =
+ wcd9xxx_clsh_state_hph_r;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST] =
+ wcd9xxx_clsh_state_hph_st;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_LO] = wcd9xxx_clsh_state_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_EAR] =
+ wcd9xxx_clsh_state_hph_ear;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_EAR] =
+ wcd9xxx_clsh_state_hph_ear;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_EAR] =
+ wcd9xxx_clsh_state_hph_ear;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_LO] = wcd9xxx_clsh_state_hph_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_LO] = wcd9xxx_clsh_state_hph_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_LO] =
+ wcd9xxx_clsh_state_hph_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_EAR_LO] = wcd9xxx_clsh_state_ear_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_EAR_LO] =
+ wcd9xxx_clsh_state_hph_ear_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_EAR_LO] =
+ wcd9xxx_clsh_state_hph_ear_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO] =
+ wcd9xxx_clsh_state_hph_ear_lo;
+
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_clsh_init);
+
+MODULE_DESCRIPTION("WCD9XXX Common");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-common.h b/sound/soc/codecs/wcd9xxx-common.h
new file mode 100644
index 000000000000..5c0c4a98f3fc
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-common.h
@@ -0,0 +1,286 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WCD9XXX_CODEC_COMMON
+
+#define WCD9XXX_CODEC_COMMON
+
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_CLSH_REQ_ENABLE true
+#define WCD9XXX_CLSH_REQ_DISABLE false
+
+#define WCD9XXX_CLSH_EVENT_PRE_DAC 0x01
+#define WCD9XXX_CLSH_EVENT_POST_PA 0x02
+
+/* Basic states for Class H state machine.
+ * represented as a bit mask within a u8 data type
+ * bit 0: EAR mode
+ * bit 1: HPH Left mode
+ * bit 2: HPH Right mode
+ * bit 3: Lineout mode
+ * bit 4: Ultrasound mode
+ */
+#define WCD9XXX_CLSH_STATE_IDLE 0x00
+#define WCD9XXX_CLSH_STATE_EAR (0x01 << 0)
+#define WCD9XXX_CLSH_STATE_HPHL (0x01 << 1)
+#define WCD9XXX_CLSH_STATE_HPHR (0x01 << 2)
+#define WCD9XXX_CLSH_STATE_LO (0x01 << 3)
+#define NUM_CLSH_STATES (0x01 << 4)
+
+#define WCD9XXX_CLSAB_STATE_IDLE 0x00
+#define WCD9XXX_CLSAB_STATE_HPHL (0x01 << 1)
+#define WCD9XXX_CLSAB_STATE_HPHR (0x01 << 2)
+
+#define WCD9XXX_CLSAB_REQ_ENABLE true
+#define WCD9XXX_CLSAB_REQ_DISABLE false
+
+#define WCD9XXX_NON_UHQA_MODE 0
+
+#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_2 0x0
+#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_3 0x1
+#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_4 0x2
+
+#define WCD9XXX_DMIC_B1_CTL_DIV_2 0x00
+#define WCD9XXX_DMIC_B1_CTL_DIV_3 0x22
+#define WCD9XXX_DMIC_B1_CTL_DIV_4 0x44
+
+#define WCD9XXX_DMIC_B2_CTL_DIV_2 0x00
+#define WCD9XXX_DMIC_B2_CTL_DIV_3 0x02
+#define WCD9XXX_DMIC_B2_CTL_DIV_4 0x04
+
+#define WCD9XXX_ANC_DMIC_X2_ON 0x1
+#define WCD9XXX_ANC_DMIC_X2_OFF 0x0
+
+/* Derived State: Bits 1 and 2 should be set for Headphone stereo */
+#define WCD9XXX_CLSH_STATE_HPH_ST (WCD9XXX_CLSH_STATE_HPHL | \
+ WCD9XXX_CLSH_STATE_HPHR)
+
+#define WCD9XXX_CLSH_STATE_HPHL_EAR (WCD9XXX_CLSH_STATE_HPHL | \
+ WCD9XXX_CLSH_STATE_EAR)
+#define WCD9XXX_CLSH_STATE_HPHR_EAR (WCD9XXX_CLSH_STATE_HPHR | \
+ WCD9XXX_CLSH_STATE_EAR)
+
+#define WCD9XXX_CLSH_STATE_HPH_ST_EAR (WCD9XXX_CLSH_STATE_HPH_ST | \
+ WCD9XXX_CLSH_STATE_EAR)
+
+#define WCD9XXX_CLSH_STATE_HPHL_LO (WCD9XXX_CLSH_STATE_HPHL | \
+ WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPHR_LO (WCD9XXX_CLSH_STATE_HPHR | \
+ WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_HPH_ST_LO (WCD9XXX_CLSH_STATE_HPH_ST | \
+ WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_EAR_LO (WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_HPHL_EAR_LO (WCD9XXX_CLSH_STATE_HPHL | \
+ WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPHR_EAR_LO (WCD9XXX_CLSH_STATE_HPHR | \
+ WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO (WCD9XXX_CLSH_STATE_HPH_ST | \
+ WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
+
+struct wcd9xxx_reg_mask_val {
+ u16 reg;
+ u8 mask;
+ u8 val;
+};
+
+enum ncp_fclk_level {
+ NCP_FCLK_LEVEL_8,
+ NCP_FCLK_LEVEL_5,
+ NCP_FCLK_LEVEL_MAX,
+};
+
+/* Class H data that the codec driver will maintain */
+struct wcd9xxx_clsh_cdc_data {
+ u8 state;
+ int buck_mv;
+ bool is_dynamic_vdd_cp;
+ int clsh_users;
+ int buck_users;
+ int ncp_users[NCP_FCLK_LEVEL_MAX];
+ struct wcd9xxx_resmgr *resmgr;
+};
+
+struct wcd9xxx_anc_header {
+ u32 reserved[3];
+ u32 num_anc_slots;
+};
+
+enum wcd9xxx_buck_volt {
+ WCD9XXX_CDC_BUCK_UNSUPPORTED = 0,
+ WCD9XXX_CDC_BUCK_MV_1P8 = 1800000,
+ WCD9XXX_CDC_BUCK_MV_2P15 = 2150000,
+};
+
+struct mad_audio_header {
+ u32 reserved[3];
+ u32 num_reg_cfg;
+};
+
+struct mad_microphone_info {
+ uint8_t input_microphone;
+ uint8_t cycle_time;
+ uint8_t settle_time;
+ uint8_t padding;
+} __packed;
+
+struct mad_micbias_info {
+ uint8_t micbias;
+ uint8_t k_factor;
+ uint8_t external_bypass_capacitor;
+ uint8_t internal_biasing;
+ uint8_t cfilter;
+ uint8_t padding[3];
+} __packed;
+
+struct mad_rms_audio_beacon_info {
+ uint8_t rms_omit_samples;
+ uint8_t rms_comp_time;
+ uint8_t detection_mechanism;
+ uint8_t rms_diff_threshold;
+ uint8_t rms_threshold_lsb;
+ uint8_t rms_threshold_msb;
+ uint8_t padding[2];
+ uint8_t iir_coefficients[36];
+} __packed;
+
+struct mad_rms_ultrasound_info {
+ uint8_t rms_comp_time;
+ uint8_t detection_mechanism;
+ uint8_t rms_diff_threshold;
+ uint8_t rms_threshold_lsb;
+ uint8_t rms_threshold_msb;
+ uint8_t padding[3];
+ uint8_t iir_coefficients[36];
+} __packed;
+
+struct mad_audio_cal {
+ uint32_t version;
+ struct mad_microphone_info microphone_info;
+ struct mad_micbias_info micbias_info;
+ struct mad_rms_audio_beacon_info audio_info;
+ struct mad_rms_audio_beacon_info beacon_info;
+ struct mad_rms_ultrasound_info ultrasound_info;
+} __packed;
+
+extern void wcd9xxx_clsh_fsm(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *cdc_clsh_d,
+ u8 req_state, bool req_type, u8 clsh_event);
+
+extern void wcd9xxx_enable_high_perf_mode(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 uhqa_mode, u8 req_state, bool req_type);
+
+extern void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh,
+ struct wcd9xxx_resmgr *resmgr);
+
+extern void wcd9xxx_clsh_imped_config(struct snd_soc_codec *codec,
+ int imped);
+
+enum wcd9xxx_codec_event {
+ WCD9XXX_CODEC_EVENT_CODEC_UP = 0,
+};
+
+struct wcd9xxx_register_save_node {
+ struct list_head lh;
+ u16 reg;
+ u16 value;
+};
+
+extern int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec,
+ struct list_head *lh,
+ uint16_t reg, uint8_t mask,
+ uint8_t value, int delay);
+extern void wcd9xxx_restore_registers(struct snd_soc_codec *codec,
+ struct list_head *lh);
+enum {
+ RESERVED = 0,
+ AANC_LPF_FF_FB = 1,
+ AANC_LPF_COEFF_MSB,
+ AANC_LPF_COEFF_LSB,
+ HW_MAD_AUDIO_ENABLE,
+ HW_MAD_ULTR_ENABLE,
+ HW_MAD_BEACON_ENABLE,
+ HW_MAD_AUDIO_SLEEP_TIME,
+ HW_MAD_ULTR_SLEEP_TIME,
+ HW_MAD_BEACON_SLEEP_TIME,
+ HW_MAD_TX_AUDIO_SWITCH_OFF,
+ HW_MAD_TX_ULTR_SWITCH_OFF,
+ HW_MAD_TX_BEACON_SWITCH_OFF,
+ MAD_AUDIO_INT_DEST_SELECT_REG,
+ MAD_ULT_INT_DEST_SELECT_REG,
+ MAD_BEACON_INT_DEST_SELECT_REG,
+ MAD_CLIP_INT_DEST_SELECT_REG,
+ MAD_VBAT_INT_DEST_SELECT_REG,
+ MAD_AUDIO_INT_MASK_REG,
+ MAD_ULT_INT_MASK_REG,
+ MAD_BEACON_INT_MASK_REG,
+ MAD_CLIP_INT_MASK_REG,
+ MAD_VBAT_INT_MASK_REG,
+ MAD_AUDIO_INT_STATUS_REG,
+ MAD_ULT_INT_STATUS_REG,
+ MAD_BEACON_INT_STATUS_REG,
+ MAD_CLIP_INT_STATUS_REG,
+ MAD_VBAT_INT_STATUS_REG,
+ MAD_AUDIO_INT_CLEAR_REG,
+ MAD_ULT_INT_CLEAR_REG,
+ MAD_BEACON_INT_CLEAR_REG,
+ MAD_CLIP_INT_CLEAR_REG,
+ MAD_VBAT_INT_CLEAR_REG,
+ SB_PGD_PORT_TX_WATERMARK_N,
+ SB_PGD_PORT_TX_ENABLE_N,
+ SB_PGD_PORT_RX_WATERMARK_N,
+ SB_PGD_PORT_RX_ENABLE_N,
+ SB_PGD_TX_PORTn_MULTI_CHNL_0,
+ SB_PGD_TX_PORTn_MULTI_CHNL_1,
+ SB_PGD_RX_PORTn_MULTI_CHNL_0,
+ SB_PGD_RX_PORTn_MULTI_CHNL_1,
+ AANC_FF_GAIN_ADAPTIVE,
+ AANC_FFGAIN_ADAPTIVE_EN,
+ AANC_GAIN_CONTROL,
+ SPKR_CLIP_PIPE_BANK_SEL,
+ SPKR_CLIPDET_VAL0,
+ SPKR_CLIPDET_VAL1,
+ SPKR_CLIPDET_VAL2,
+ SPKR_CLIPDET_VAL3,
+ SPKR_CLIPDET_VAL4,
+ SPKR_CLIPDET_VAL5,
+ SPKR_CLIPDET_VAL6,
+ SPKR_CLIPDET_VAL7,
+ VBAT_RELEASE_INT_DEST_SELECT_REG,
+ VBAT_RELEASE_INT_MASK_REG,
+ VBAT_RELEASE_INT_STATUS_REG,
+ VBAT_RELEASE_INT_CLEAR_REG,
+ MAD2_CLIP_INT_DEST_SELECT_REG,
+ MAD2_CLIP_INT_MASK_REG,
+ MAD2_CLIP_INT_STATUS_REG,
+ MAD2_CLIP_INT_CLEAR_REG,
+ SPKR2_CLIP_PIPE_BANK_SEL,
+ SPKR2_CLIPDET_VAL0,
+ SPKR2_CLIPDET_VAL1,
+ SPKR2_CLIPDET_VAL2,
+ SPKR2_CLIPDET_VAL3,
+ SPKR2_CLIPDET_VAL4,
+ SPKR2_CLIPDET_VAL5,
+ SPKR2_CLIPDET_VAL6,
+ SPKR2_CLIPDET_VAL7,
+ MAX_CFG_REGISTERS,
+};
+
+#endif
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
new file mode 100644
index 000000000000..2012e4617ee1
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -0,0 +1,5643 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include "wcd9xxx-mbhc.h"
+#include "wcdcal-hwdep.h"
+#include "wcd9xxx-resmgr.h"
+#include "wcd9xxx-common.h"
+
+#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
+ SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
+ SND_JACK_UNSUPPORTED | SND_JACK_MICROPHONE2 | \
+ SND_JACK_MECHANICAL)
+#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+ SND_JACK_BTN_4 | SND_JACK_BTN_5 )
+
+#define NUM_DCE_PLUG_DETECT 3
+#define NUM_DCE_PLUG_INS_DETECT 5
+#define NUM_ATTEMPTS_INSERT_DETECT 25
+#define NUM_ATTEMPTS_TO_REPORT 5
+
+#define FAKE_INS_LOW 10
+#define FAKE_INS_HIGH 80
+#define FAKE_INS_HIGH_NO_SWCH 150
+#define FAKE_REMOVAL_MIN_PERIOD_MS 50
+#define FAKE_INS_DELTA_SCALED_MV 300
+
+#define BUTTON_MIN 0x8000
+#define STATUS_REL_DETECTION 0x0C
+
+#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
+#define ANC_HPH_DETECT_PLUG_TIME_MS (5 * 1000)
+#define HS_DETECT_PLUG_INERVAL_MS 100
+#define SWCH_REL_DEBOUNCE_TIME_MS 50
+#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
+#define BTN_RELEASE_DEBOUNCE_TIME_MS 25
+
+#define GND_MIC_SWAP_THRESHOLD 2
+#define OCP_ATTEMPT 1
+
+#define FW_READ_ATTEMPTS 15
+#define FW_READ_TIMEOUT 4000000
+
+#define BUTTON_POLLING_SUPPORTED true
+
+#define MCLK_RATE_12288KHZ 12288000
+#define MCLK_RATE_9600KHZ 9600000
+
+#define DEFAULT_DCE_STA_WAIT 55
+#define DEFAULT_DCE_WAIT 60000
+#define DEFAULT_STA_WAIT 5000
+
+#define VDDIO_MICBIAS_MV 1800
+
+#define WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US 5000
+
+#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000
+#define WCD9XXX_MUX_SWITCH_READY_WAIT_MS 50
+#define WCD9XXX_MEAS_DELTA_MAX_MV 120
+#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20
+#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80
+
+/* Threshold in milliohm used for mono/stereo
+ * plug classification
+ */
+#define WCD9XXX_MONO_HS_DIFF_THR 20000000
+#define WCD9XXX_MONO_HS_MIN_THR 2000
+
+/*
+ * Invalid voltage range for the detection
+ * of plug type with current source
+ */
+#define WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV 160
+#define WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV 265
+
+/*
+ * Threshold used to detect euro headset
+ * with current source
+ */
+#define WCD9XXX_CS_GM_SWAP_THRES_MIN_MV 10
+#define WCD9XXX_CS_GM_SWAP_THRES_MAX_MV 40
+
+#define WCD9XXX_MBHC_NSC_CS 9
+#define WCD9XXX_GM_SWAP_THRES_MIN_MV 150
+#define WCD9XXX_GM_SWAP_THRES_MAX_MV 650
+#define WCD9XXX_THRESHOLD_MIC_THRESHOLD 200
+
+#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
+
+/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
+#define WCD9XXX_WG_TIME_FACTOR_US 240
+
+#define WCD9XXX_V_CS_HS_MAX 500
+#define WCD9XXX_V_CS_NO_MIC 5
+#define WCD9XXX_MB_MEAS_DELTA_MAX_MV 80
+#define WCD9XXX_CS_MEAS_DELTA_MAX_MV 12
+
+#define WCD9XXX_ZDET_ZONE_1 80000
+#define WCD9XXX_ZDET_ZONE_2 800000
+
+#define WCD9XXX_IS_IN_ZDET_ZONE_1(x) (x < WCD9XXX_ZDET_ZONE_1 ? 1 : 0)
+#define WCD9XXX_IS_IN_ZDET_ZONE_2(x) ((x > WCD9XXX_ZDET_ZONE_1 && \
+ x < WCD9XXX_ZDET_ZONE_2) ? 1 : 0)
+#define WCD9XXX_IS_IN_ZDET_ZONE_3(x) (x > WCD9XXX_ZDET_ZONE_2 ? 1 : 0)
+#define WCD9XXX_BOX_CAR_AVRG_MIN 1
+#define WCD9XXX_BOX_CAR_AVRG_MAX 10
+
+/*
+ * Need to report LINEIN if H/L impedance
+ * is larger than 5K ohm
+ */
+#define WCD9XXX_LINEIN_THRESHOLD 5000000
+
+static int impedance_detect_en;
+module_param(impedance_detect_en, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(impedance_detect_en, "enable/disable impedance detect");
+static unsigned int z_det_box_car_avg = 1;
+module_param(z_det_box_car_avg, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(z_det_box_car_avg,
+ "Number of samples for impedance detection");
+
+static bool detect_use_vddio_switch;
+
+struct wcd9xxx_mbhc_detect {
+ u16 dce;
+ u16 sta;
+ u16 hphl_status;
+ bool swap_gnd;
+ bool vddio;
+ bool hwvalue;
+ bool mic_bias;
+ /* internal purpose from here */
+ bool _above_no_mic;
+ bool _below_v_hs_max;
+ s16 _vdces;
+ enum wcd9xxx_mbhc_plug_type _type;
+};
+
+enum meas_type {
+ STA = 0,
+ DCE,
+};
+
+enum {
+ MBHC_USE_HPHL_TRIGGER = 1,
+ MBHC_USE_MB_TRIGGER = 2
+};
+
+/*
+ * Flags to track of PA and DAC state.
+ * PA and DAC should be tracked separately as AUXPGA loopback requires
+ * only PA to be turned on without DAC being on.
+ */
+enum pa_dac_ack_flags {
+ WCD9XXX_HPHL_PA_OFF_ACK = 0,
+ WCD9XXX_HPHR_PA_OFF_ACK,
+ WCD9XXX_HPHL_DAC_OFF_ACK,
+ WCD9XXX_HPHR_DAC_OFF_ACK
+};
+
+enum wcd9xxx_current_v_idx {
+ WCD9XXX_CURRENT_V_INS_H,
+ WCD9XXX_CURRENT_V_INS_HU,
+ WCD9XXX_CURRENT_V_B1_H,
+ WCD9XXX_CURRENT_V_B1_HU,
+ WCD9XXX_CURRENT_V_BR_H,
+};
+
+static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr);
+static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
+ const enum wcd9xxx_current_v_idx idx);
+static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z,
+ struct mbhc_micbias_regs *micb_regs,
+ bool norel);
+
+static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc);
+
+static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
+ enum meas_type dce, s16 vin_mv,
+ bool cs_enable);
+
+static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ return snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_EN_CTL) & 0x1;
+}
+
+static void wcd9xxx_turn_onoff_override(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x04, on ? 0x04 : 0x00);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->polling_active) {
+ pr_debug("polling not active, nothing to pause\n");
+ return;
+ }
+
+ /* Soft reset MBHC block */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ int mbhc_state = mbhc->mbhc_state;
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->polling_active) {
+ pr_debug("Polling is not active, do not start polling\n");
+ return;
+ }
+
+ /*
+ * setup internal micbias if codec uses internal micbias for
+ * headset detection
+ */
+ if (mbhc->mbhc_cfg->use_int_rbias) {
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
+ mbhc->mbhc_cb->setup_int_rbias(codec, true);
+ else
+ pr_err("%s: internal bias requested but codec did not provide callback\n",
+ __func__);
+ }
+
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
+
+ if (!mbhc->no_mic_headset_override &&
+ mbhc_state == MBHC_STATE_POTENTIAL) {
+ pr_debug("%s recovering MBHC state machine\n", __func__);
+ mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
+ /* set to max button press threshold */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
+ /* set to max */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
+ }
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static int __wcd9xxx_resmgr_get_k_val(struct wcd9xxx_mbhc *mbhc,
+ unsigned int cfilt_mv)
+{
+ return wcd9xxx_resmgr_get_k_val(mbhc->resmgr, cfilt_mv);
+}
+
+/*
+ * called under codec_resource_lock acquisition
+ * return old status
+ */
+static bool __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
+ int vddio_switch, bool restartpolling,
+ bool checkpolling)
+{
+ bool ret;
+ int cfilt_k_val;
+ bool override;
+ struct snd_soc_codec *codec;
+ struct mbhc_internal_cal_data *d = &mbhc->mbhc_data;
+
+ codec = mbhc->codec;
+
+ if (mbhc->micbias_enable) {
+ pr_debug("%s: micbias is already on\n", __func__);
+ ret = mbhc->mbhc_micbias_switched;
+ return ret;
+ }
+
+ ret = mbhc->mbhc_micbias_switched;
+ if (vddio_switch && !mbhc->mbhc_micbias_switched &&
+ (!checkpolling || mbhc->polling_active)) {
+ if (restartpolling)
+ wcd9xxx_pause_hs_polling(mbhc);
+ override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
+ 0x04;
+ if (!override)
+ wcd9xxx_turn_onoff_override(mbhc, true);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL,
+ 0x10, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1,
+ 0x20, 0x00);
+ /* Adjust threshold if Mic Bias voltage changes */
+ if (d->micb_mv != VDDIO_MICBIAS_MV) {
+ cfilt_k_val = __wcd9xxx_resmgr_get_k_val(mbhc,
+ VDDIO_MICBIAS_MV);
+ usleep_range(10000, 10100);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ usleep_range(10000, 10100);
+ /* Threshods for insertion/removal */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+ d->v_ins_hu[MBHC_V_IDX_VDDIO] & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (d->v_ins_hu[MBHC_V_IDX_VDDIO] >> 8) &
+ 0xFF);
+
+ if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
+ /* Threshods for button press */
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
+ d->v_b1_hu[MBHC_V_IDX_VDDIO] & 0xFF);
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+ (d->v_b1_hu[MBHC_V_IDX_VDDIO] >> 8) &
+ 0xFF);
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
+ d->v_b1_h[MBHC_V_IDX_VDDIO] & 0xFF);
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+ (d->v_b1_h[MBHC_V_IDX_VDDIO] >> 8) &
+ 0xFF);
+ /* Threshods for button release */
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
+ d->v_brh[MBHC_V_IDX_VDDIO] & 0xFF);
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+ (d->v_brh[MBHC_V_IDX_VDDIO] >> 8) &
+ 0xFF);
+ }
+ pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
+ __func__);
+ }
+
+ /* Enable MIC BIAS Switch to VDDIO */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x00);
+ if (!override)
+ wcd9xxx_turn_onoff_override(mbhc, false);
+ if (restartpolling)
+ wcd9xxx_start_hs_polling(mbhc);
+
+ mbhc->mbhc_micbias_switched = true;
+ pr_debug("%s: VDDIO switch enabled\n", __func__);
+ } else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
+ if ((!checkpolling || mbhc->polling_active) &&
+ restartpolling)
+ wcd9xxx_pause_hs_polling(mbhc);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL,
+ 0x10, 0x10);
+ snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1,
+ 0x20, 0x20);
+ /* Reprogram thresholds */
+ if (d->micb_mv != VDDIO_MICBIAS_MV) {
+ cfilt_k_val =
+ __wcd9xxx_resmgr_get_k_val(mbhc,
+ d->micb_mv);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ usleep_range(10000, 10100);
+ /* Revert threshods for insertion/removal */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+ d->v_ins_hu[MBHC_V_IDX_CFILT] & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (d->v_ins_hu[MBHC_V_IDX_CFILT] >> 8) &
+ 0xFF);
+ if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
+ /* Revert threshods for button press */
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
+ d->v_b1_hu[MBHC_V_IDX_CFILT] & 0xFF);
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+ (d->v_b1_hu[MBHC_V_IDX_CFILT] >> 8) &
+ 0xFF);
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
+ d->v_b1_h[MBHC_V_IDX_CFILT] & 0xFF);
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+ (d->v_b1_h[MBHC_V_IDX_CFILT] >> 8) &
+ 0xFF);
+ /* Revert threshods for button release */
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
+ d->v_brh[MBHC_V_IDX_CFILT] & 0xFF);
+ snd_soc_write(codec,
+ WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+ (d->v_brh[MBHC_V_IDX_CFILT] >> 8) &
+ 0xFF);
+ }
+ pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
+ __func__);
+ }
+
+ /* Disable MIC BIAS Switch to VDDIO */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
+ 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
+ 0x00);
+
+ if ((!checkpolling || mbhc->polling_active) && restartpolling)
+ wcd9xxx_start_hs_polling(mbhc);
+
+ mbhc->mbhc_micbias_switched = false;
+ pr_debug("%s: VDDIO switch disabled\n", __func__);
+ }
+
+ return ret;
+}
+
+static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
+{
+ __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
+}
+
+static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
+ const enum wcd9xxx_current_v_idx idx)
+{
+ enum mbhc_v_index vidx;
+ s16 ret = -EINVAL;
+
+ if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+ mbhc->mbhc_micbias_switched)
+ vidx = MBHC_V_IDX_VDDIO;
+ else
+ vidx = MBHC_V_IDX_CFILT;
+
+ switch (idx) {
+ case WCD9XXX_CURRENT_V_INS_H:
+ ret = (s16)mbhc->mbhc_data.v_ins_h[vidx];
+ break;
+ case WCD9XXX_CURRENT_V_INS_HU:
+ ret = (s16)mbhc->mbhc_data.v_ins_hu[vidx];
+ break;
+ case WCD9XXX_CURRENT_V_B1_H:
+ ret = (s16)mbhc->mbhc_data.v_b1_h[vidx];
+ break;
+ case WCD9XXX_CURRENT_V_B1_HU:
+ ret = (s16)mbhc->mbhc_data.v_b1_hu[vidx];
+ break;
+ case WCD9XXX_CURRENT_V_BR_H:
+ ret = (s16)mbhc->mbhc_data.v_brh[vidx];
+ break;
+ }
+
+ return ret;
+}
+
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+ const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+ const enum wcd9xxx_mbhc_btn_det_mem mem)
+{
+ void *ret = (void *)&btn_det->_v_btn_low;
+
+ switch (mem) {
+ case MBHC_BTN_DET_GAIN:
+ ret += sizeof(btn_det->_n_cic);
+ case MBHC_BTN_DET_N_CIC:
+ ret += sizeof(btn_det->_n_ready);
+ case MBHC_BTN_DET_N_READY:
+ ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
+ case MBHC_BTN_DET_V_BTN_HIGH:
+ ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
+ case MBHC_BTN_DET_V_BTN_LOW:
+ /* do nothing */
+ break;
+ default:
+ ret = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_mbhc_cal_btn_det_mp);
+
+static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const s16 v_ins_hu = wcd9xxx_get_current_v(mbhc,
+ WCD9XXX_CURRENT_V_INS_HU);
+ const s16 v_b1_hu = wcd9xxx_get_current_v(mbhc,
+ WCD9XXX_CURRENT_V_B1_HU);
+ const s16 v_b1_h = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
+ const s16 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (v_ins_hu >> 8) & 0xFF);
+
+ if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu &
+ 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+ (v_b1_hu >> 8) & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, v_b1_h &
+ 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+ (v_b1_h >> 8) & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh &
+ 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+ (v_brh >> 8) & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
+ mbhc->mbhc_data.v_brl & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
+ (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
+ }
+}
+
+static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
+ bool fast)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx_cfilt_mode cfilt_mode;
+
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
+ cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
+ } else {
+ if (fast)
+ cfilt_mode.reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
+ else
+ cfilt_mode.reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
+
+ cfilt_mode.reg_mask = 0x40;
+ cfilt_mode.cur_mode_val =
+ snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
+ }
+
+ if (cfilt_mode.cur_mode_val
+ != cfilt_mode.reg_mode_val) {
+ if (mbhc->polling_active && wcd9xxx_mbhc_polling(mbhc))
+ wcd9xxx_pause_hs_polling(mbhc);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_ctl,
+ cfilt_mode.reg_mask,
+ cfilt_mode.reg_mode_val);
+ if (mbhc->polling_active && wcd9xxx_mbhc_polling(mbhc))
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+ cfilt_mode.cur_mode_val,
+ cfilt_mode.reg_mode_val);
+ } else {
+ pr_debug("%s: CFILT Value is already %x\n",
+ __func__, cfilt_mode.cur_mode_val);
+ }
+}
+
+static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
+ struct snd_soc_jack *jack, int status, int mask)
+{
+ if (jack == &mbhc->headset_jack) {
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH_MIC,
+ status & SND_JACK_MICROPHONE);
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH,
+ status & SND_JACK_HEADPHONE);
+ }
+
+ snd_soc_jack_report(jack, status, mask);
+}
+
+static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
+ int irq)
+{
+ struct snd_soc_codec *codec;
+
+ pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
+ codec = mbhc->codec;
+ if (mbhc->hph_status & jack_status) {
+ mbhc->hph_status &= ~jack_status;
+ wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
+ mbhc->hph_status, WCD9XXX_JACK_MASK);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ /*
+ * reset retry counter as PA is turned off signifying
+ * start of new OCP detection session
+ */
+ if (mbhc->intr_ids->hph_left_ocp)
+ mbhc->hphlocp_cnt = 0;
+ else
+ mbhc->hphrocp_cnt = 0;
+ wcd9xxx_enable_irq(mbhc->resmgr->core_res, irq);
+ }
+}
+
+static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+ __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
+ mbhc->intr_ids->hph_right_ocp);
+}
+
+static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+ __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
+ mbhc->intr_ids->hph_left_ocp);
+}
+
+static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
+ enum wcd9xxx_mbhc_micbias_type mb_type)
+{
+ unsigned int cfilt;
+ struct wcd9xxx_micbias_setting *micbias_pdata =
+ mbhc->resmgr->micbias_pdata;
+ struct mbhc_micbias_regs *micbias_regs;
+ enum wcd9xxx_micbias_num mb_num;
+
+ if (mb_type == MBHC_ANC_MIC_MB) {
+ micbias_regs = &mbhc->mbhc_anc_bias_regs;
+ mb_num = mbhc->mbhc_cfg->anc_micbias;
+ } else {
+ micbias_regs = &mbhc->mbhc_bias_regs;
+ mb_num = mbhc->mbhc_cfg->micbias;
+ }
+
+ switch (mb_num) {
+ case MBHC_MICBIAS1:
+ cfilt = micbias_pdata->bias1_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
+ break;
+ case MBHC_MICBIAS2:
+ cfilt = micbias_pdata->bias2_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
+ break;
+ case MBHC_MICBIAS3:
+ cfilt = micbias_pdata->bias3_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
+ break;
+ case MBHC_MICBIAS4:
+ cfilt = micbias_pdata->bias4_cfilt_sel;
+ micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
+ micbias_regs->int_rbias =
+ mbhc->resmgr->reg_addr->micb_4_int_rbias;
+ micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
+ break;
+ default:
+ /* Should never reach here */
+ pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
+ return;
+ }
+
+ micbias_regs->cfilt_sel = cfilt;
+
+ switch (cfilt) {
+ case WCD9XXX_CFILT1_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
+ break;
+ case WCD9XXX_CFILT2_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
+ break;
+ case WCD9XXX_CFILT3_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
+ break;
+ }
+
+ if (mb_type == MBHC_PRIMARY_MIC_MB) {
+ switch (cfilt) {
+ case WCD9XXX_CFILT1_SEL:
+ mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt1_mv;
+ break;
+ case WCD9XXX_CFILT2_SEL:
+ mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt2_mv;
+ break;
+ case WCD9XXX_CFILT3_SEL:
+ mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt3_mv;
+ break;
+ }
+ }
+
+}
+
+static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+ bool pa_turned_on = false;
+ struct snd_soc_codec *codec = mbhc->codec;
+ u8 wg_time;
+
+ wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
+ wg_time += 1;
+
+ if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
+ 0xC0, 0xC0);
+ }
+ if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
+ 0x80, 0x80);
+ }
+
+ if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
+ 1 << 4);
+ pa_turned_on = true;
+ }
+ if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
+ << 5);
+ pa_turned_on = true;
+ }
+
+ if (pa_turned_on) {
+ pr_debug("%s: PA was turned on by MBHC and not by DAPM\n",
+ __func__);
+ usleep_range(wg_time * 1000, wg_time * 1000 + 50);
+ }
+}
+
+static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
+{
+ int r;
+ r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
+ if (r)
+ /* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
+ * we have to unlock from here instead btn_work */
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
+ return r;
+}
+
+static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
+{
+ u8 hph_reg_val = 0;
+ if (left)
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
+ else
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
+
+ return (hph_reg_val & 0xC0) ? true : false;
+}
+
+static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
+{
+ u8 hph_reg_val = 0;
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
+
+ return (hph_reg_val & 0x30) ? true : false;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+ u8 wg_time;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
+ wg_time += 1;
+
+ /* If headphone PA is on, check if userspace receives
+ * removal event to sync-up PA's state */
+ if (wcd9xxx_is_hph_pa_on(codec)) {
+ pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
+ set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ } else {
+ pr_debug("%s PA is off\n", __func__);
+ }
+
+ if (wcd9xxx_is_hph_dac_on(codec, 1))
+ set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (wcd9xxx_is_hph_dac_on(codec, 0))
+ set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x80, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
+ usleep_range(wg_time * 1000, wg_time * 1000 + 50);
+}
+
+static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
+{
+ if (!mbhc->mbhc_cfg->insert_detect)
+ return;
+ pr_debug("%s: Setting up %s detection\n", __func__,
+ ins ? "insert" : "removal");
+ /* Disable detection to avoid glitch */
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
+ if (mbhc->mbhc_cfg->gpio_level_insert)
+ snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
+ (0x68 | (ins ? (1 << 1) : 0)));
+ else
+ snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
+ (0x6C | (ins ? (1 << 1) : 0)));
+ /* Re-enable detection */
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
+ enum snd_jack_types jack_type)
+{
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ pr_debug("%s: enter insertion %d hph_status %x\n",
+ __func__, insertion, mbhc->hph_status);
+ if (!insertion) {
+ /* Report removal */
+ mbhc->hph_status &= ~jack_type;
+ /*
+ * cancel possibly scheduled btn work and
+ * report release if we reported button press
+ */
+ if (wcd9xxx_cancel_btn_work(mbhc))
+ pr_debug("%s: button press is canceled\n", __func__);
+ else if (mbhc->buttons_pressed) {
+ pr_debug("%s: release of button press%d\n",
+ __func__, jack_type);
+ wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
+ mbhc->buttons_pressed);
+ mbhc->buttons_pressed &=
+ ~WCD9XXX_JACK_BUTTON_MASK;
+ }
+
+ if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
+ pr_debug("%s: Disabling micbias\n", __func__);
+ mbhc->micbias_enable = false;
+ mbhc->micbias_enable_cb(mbhc->codec, false,
+ mbhc->mbhc_cfg->micbias);
+ }
+ mbhc->zl = mbhc->zr = 0;
+ mbhc->hph_type = MBHC_HPH_NONE;
+ pr_debug("%s: Reporting removal %d(%x)\n", __func__,
+ jack_type, mbhc->hph_status);
+ wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status,
+ WCD9XXX_JACK_MASK);
+ wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+ hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
+ hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ mbhc->current_plug = PLUG_TYPE_NONE;
+ mbhc->polling_active = false;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->hph_auto_pulldown_ctrl)
+ mbhc->mbhc_cb->hph_auto_pulldown_ctrl(mbhc->codec,
+ false);
+ } else {
+ /*
+ * Report removal of current jack type.
+ * Headphone to headset shouldn't report headphone
+ * removal.
+ */
+ if (mbhc->mbhc_cfg->detect_extn_cable &&
+ !(mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
+ jack_type == SND_JACK_HEADSET) &&
+ (mbhc->hph_status && mbhc->hph_status != jack_type)) {
+ if (mbhc->micbias_enable && mbhc->micbias_enable_cb &&
+ mbhc->hph_status == SND_JACK_HEADSET) {
+ pr_debug("%s: Disabling micbias\n", __func__);
+ mbhc->micbias_enable = false;
+ mbhc->micbias_enable_cb(mbhc->codec, false,
+ mbhc->mbhc_cfg->micbias);
+ }
+
+ pr_debug("%s: Reporting removal (%x)\n",
+ __func__, mbhc->hph_status);
+ mbhc->zl = mbhc->zr = 0;
+ wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
+ 0, WCD9XXX_JACK_MASK);
+ mbhc->hph_status &= ~(SND_JACK_HEADSET |
+ SND_JACK_LINEOUT |
+ SND_JACK_ANC_HEADPHONE |
+ SND_JACK_UNSUPPORTED);
+ if (mbhc->mbhc_cb &&
+ mbhc->mbhc_cb->hph_auto_pulldown_ctrl)
+ mbhc->mbhc_cb->hph_auto_pulldown_ctrl(
+ mbhc->codec,
+ false);
+ }
+
+ /* Report insertion */
+ if (jack_type == SND_JACK_HEADPHONE) {
+ mbhc->current_plug = PLUG_TYPE_HEADPHONE;
+ } else if (jack_type == SND_JACK_UNSUPPORTED) {
+ mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
+ } else if (jack_type == SND_JACK_HEADSET) {
+ mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
+ mbhc->current_plug = PLUG_TYPE_HEADSET;
+ mbhc->update_z = true;
+ } else if (jack_type == SND_JACK_LINEOUT) {
+ mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
+ } else if (jack_type == SND_JACK_ANC_HEADPHONE) {
+ mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
+ mbhc->current_plug = PLUG_TYPE_ANC_HEADPHONE;
+ }
+
+ if (mbhc->impedance_detect && impedance_detect_en) {
+ wcd9xxx_detect_impedance(mbhc,
+ &mbhc->zl, &mbhc->zr);
+ if ((mbhc->zl > WCD9XXX_LINEIN_THRESHOLD) &&
+ (mbhc->zr > WCD9XXX_LINEIN_THRESHOLD)) {
+ jack_type = SND_JACK_LINEOUT;
+ mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
+ pr_debug("%s: Replace with SND_JACK_LINEOUT\n",
+ __func__);
+ }
+ }
+
+ mbhc->hph_status |= jack_type;
+
+ if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
+ pr_debug("%s: Enabling micbias\n", __func__);
+ mbhc->micbias_enable_cb(mbhc->codec, true,
+ mbhc->mbhc_cfg->micbias);
+ }
+
+ pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
+ jack_type, mbhc->hph_status);
+ wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
+ (mbhc->hph_status | SND_JACK_MECHANICAL),
+ WCD9XXX_JACK_MASK);
+ /*
+ * if PA is already on, switch micbias
+ * source to VDDIO
+ */
+ if (((mbhc->current_plug == PLUG_TYPE_HEADSET) ||
+ (mbhc->current_plug == PLUG_TYPE_ANC_HEADPHONE)) &&
+ ((mbhc->event_state & (1 << MBHC_EVENT_PA_HPHL |
+ 1 << MBHC_EVENT_PA_HPHR))))
+ __wcd9xxx_switch_micbias(mbhc, 1, false,
+ false);
+ wcd9xxx_clr_and_turnon_hph_padac(mbhc);
+ }
+ /* Setup insert detect */
+ wcd9xxx_insert_detect_setup(mbhc, !insertion);
+
+ pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
+}
+
+/* should be called under interrupt context that hold suspend */
+static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+ struct work_struct *work)
+{
+ pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ mbhc->hs_detect_work_stop = false;
+ wcd9xxx_lock_sleep(mbhc->resmgr->core_res);
+ schedule_work(work);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+ struct work_struct *work)
+{
+ pr_debug("%s: Canceling correct_plug_swch\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ mbhc->hs_detect_work_stop = true;
+ wmb();
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ if (cancel_work_sync(work)) {
+ pr_debug("%s: correct_plug_swch is canceled\n",
+ __func__);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
+ }
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+}
+
+static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
+{
+ int r;
+ int vddio_k, mb_k;
+ vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV);
+ mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv);
+ if (tovddio)
+ r = v * (vddio_k + 4) / (mb_k + 4);
+ else
+ r = v * (mb_k + 4) / (vddio_k + 4);
+ return r;
+}
+
+static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
+{
+ s16 v_hs_max;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+
+ plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+ mbhc->mbhc_micbias_switched)
+ v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
+ else
+ v_hs_max = plug_type->v_hs_max;
+ return v_hs_max;
+}
+
+static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
+{
+ u8 bias_msb, bias_lsb;
+ short bias_value;
+
+ bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
+ bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
+ bias_value = (bias_msb << 8) | bias_lsb;
+ return bias_value;
+}
+
+static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
+{
+ u8 bias_msb, bias_lsb;
+ short bias_value;
+
+ bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
+ bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
+ bias_value = (bias_msb << 8) | bias_lsb;
+ return bias_value;
+}
+
+static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
+ bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
+}
+
+static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+ bool override_bypass, bool noreldetection)
+{
+ short bias_value;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ wcd9xxx_disable_irq(mbhc->resmgr->core_res,
+ mbhc->intr_ids->dce_est_complete);
+ if (noreldetection)
+ wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+ if (mbhc->mbhc_cfg->do_recalibration)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
+ 0x0);
+ /* Turn on the override */
+ if (!override_bypass)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+ if (dce) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x0);
+ if (mbhc->mbhc_cfg->do_recalibration)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x2, 0x2);
+ usleep_range(mbhc->mbhc_data.t_sta_dce,
+ mbhc->mbhc_data.t_sta_dce + 50);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+ usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce + 50);
+ bias_value = wcd9xxx_read_dce_result(codec);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x0);
+ if (mbhc->mbhc_cfg->do_recalibration)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x2, 0x2);
+ usleep_range(mbhc->mbhc_data.t_sta_dce,
+ mbhc->mbhc_data.t_sta_dce + 50);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+ usleep_range(mbhc->mbhc_data.t_sta,
+ mbhc->mbhc_data.t_sta + 50);
+ bias_value = wcd9xxx_read_sta_result(codec);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
+ }
+ /* Turn off the override after measuring mic voltage */
+ if (!override_bypass)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
+ 0x00);
+
+ if (noreldetection)
+ wcd9xxx_turn_onoff_rel_detection(codec, true);
+ wcd9xxx_enable_irq(mbhc->resmgr->core_res,
+ mbhc->intr_ids->dce_est_complete);
+
+ return bias_value;
+}
+
+static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+ bool norel)
+{
+ bool override_bypass;
+
+ /* Bypass override if it is already enabled */
+ override_bypass = (snd_soc_read(mbhc->codec,
+ WCD9XXX_A_CDC_MBHC_B1_CTL) &
+ 0x04) ? true : false;
+
+ return __wcd9xxx_codec_sta_dce(mbhc, dce, override_bypass, norel);
+}
+
+static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
+ u16 bias_value, s16 z, u32 micb_mv)
+{
+ s16 value, mb;
+ s32 mv = 0;
+
+ value = bias_value;
+ if (dce) {
+ mb = (mbhc->mbhc_data.dce_mb);
+ if (mb - z)
+ mv = (value - z) * (s32)micb_mv / (mb - z);
+ } else {
+ mb = (mbhc->mbhc_data.sta_mb);
+ if (mb - z)
+ mv = (value - z) * (s32)micb_mv / (mb - z);
+ }
+
+ return mv;
+}
+
+static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
+ u16 bias_value)
+{
+ s16 z;
+ z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z;
+ return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z,
+ mbhc->mbhc_data.micb_mv);
+}
+
+/* To enable/disable bandgap and RC oscillator */
+static void wcd9xxx_mbhc_ctrl_clk_bandgap(struct wcd9xxx_mbhc *mbhc,
+ bool enable)
+{
+ if (enable) {
+ WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+ wcd9xxx_resmgr_get_bandgap(mbhc->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl) {
+ WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+ mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, true);
+ } else {
+ wcd9xxx_resmgr_get_clk_block(mbhc->resmgr,
+ WCD9XXX_CLK_RCO);
+ WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+ }
+ } else {
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl) {
+ mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, false);
+ WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+ } else {
+ WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+ wcd9xxx_resmgr_put_clk_block(mbhc->resmgr,
+ WCD9XXX_CLK_RCO);
+ }
+ wcd9xxx_resmgr_put_bandgap(mbhc->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+ }
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc,
+ struct mbhc_micbias_regs *mbhc_micb_regs,
+ bool is_cs_enable)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ short bias_value;
+ u8 cfilt_mode;
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->mbhc_cfg->calibration) {
+ pr_err("%s: Error, no calibration exists\n", __func__);
+ return -ENODEV;
+ }
+
+ /* Enable external voltage source to micbias if present */
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
+ mbhc->mbhc_cb->enable_mb_source(codec, true, true);
+
+ /*
+ * setup internal micbias if codec uses internal micbias for
+ * headset detection
+ */
+ if (mbhc->mbhc_cfg->use_int_rbias) {
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
+ mbhc->mbhc_cb->setup_int_rbias(codec, true);
+ else
+ pr_err("%s: internal bias requested but codec did not provide callback\n",
+ __func__);
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
+
+ /* Make sure CFILT is in fast mode, save current mode */
+ cfilt_mode = snd_soc_read(codec, mbhc_micb_regs->cfilt_ctl);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc_micb_regs->cfilt_ctl,
+ 0x70, 0x00);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ mbhc->scaling_mux_in);
+ pr_debug("%s: scaling_mux_input: %d\n", __func__,
+ mbhc->scaling_mux_in);
+
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+
+ if (!mbhc->mbhc_cfg->do_recalibration) {
+ if (!is_cs_enable)
+ wcd9xxx_calibrate_hs_polling(mbhc);
+ }
+
+ /* don't flip override */
+ bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+ snd_soc_write(codec, mbhc_micb_regs->cfilt_ctl, cfilt_mode);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+
+ return bias_value;
+}
+
+static void wcd9xxx_recalibrate(struct wcd9xxx_mbhc *mbhc,
+ struct mbhc_micbias_regs *mbhc_micb_regs,
+ bool is_cs_enable)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ s16 reg;
+ int change;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ s16 sta_z = 0, dce_z = 0;
+
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ if (mbhc->mbhc_cfg->do_recalibration) {
+ /* recalibrate dce_z and sta_z */
+ reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
+ change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x78, btn_det->mbhc_nsc << 3);
+ wcd9xxx_get_z(mbhc, &dce_z, &sta_z, mbhc_micb_regs, true);
+ if (change)
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
+ if (dce_z && sta_z) {
+ pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n",
+ __func__,
+ mbhc->mbhc_data.sta_z, sta_z & 0xffff,
+ mbhc->mbhc_data.dce_z, dce_z & 0xffff);
+ mbhc->mbhc_data.dce_z = dce_z;
+ mbhc->mbhc_data.sta_z = sta_z;
+ wcd9xxx_mbhc_calc_thres(mbhc);
+ wcd9xxx_calibrate_hs_polling(mbhc);
+ } else {
+ pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n",
+ __func__, dce_z, sta_z);
+ }
+
+ if (is_cs_enable) {
+ /* recalibrate dce_nsc_cs_z */
+ reg = snd_soc_read(mbhc->codec,
+ WCD9XXX_A_CDC_MBHC_B1_CTL);
+ snd_soc_update_bits(mbhc->codec,
+ WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x78, WCD9XXX_MBHC_NSC_CS << 3);
+ wcd9xxx_get_z(mbhc, &dce_z, NULL, mbhc_micb_regs,
+ true);
+ snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ reg);
+ if (dce_z) {
+ mbhc->mbhc_data.dce_nsc_cs_z = dce_z;
+ /* update v_cs_ins_h with new dce_nsc_cs_z */
+ mbhc->mbhc_data.v_cs_ins_h =
+ wcd9xxx_codec_v_sta_dce(
+ mbhc, DCE,
+ WCD9XXX_V_CS_HS_MAX,
+ is_cs_enable);
+ pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x, v_cs_ins_h 0x%x\n",
+ __func__,
+ mbhc->mbhc_data.dce_nsc_cs_z,
+ dce_z & 0xffff,
+ mbhc->mbhc_data.v_cs_ins_h);
+ } else {
+ pr_debug("%s: failed get new dce_nsc_cs_z\n",
+ __func__);
+ }
+ }
+ }
+}
+
+static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const struct wcd9xxx_mbhc_general_cfg *generic =
+ WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+
+ /* Need MBHC clock */
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl)
+ mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, true);
+ else {
+ WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+ wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+ WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
+ __wcd9xxx_switch_micbias(mbhc, 0, false, false);
+
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem + 50);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
+
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl)
+ mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, false);
+ else {
+ WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+ /* Put requested CLK back */
+ wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+ WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+ }
+
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
+}
+
+static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ wcd9xxx_shutdown_hs_removal_detect(mbhc);
+
+
+ /* Disable external voltage source to micbias if present */
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
+ mbhc->mbhc_cb->enable_mb_source(mbhc->codec, false, true);
+
+ mbhc->polling_active = false;
+ mbhc->mbhc_state = MBHC_STATE_NONE;
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
+ if (on)
+ usleep_range(5000, 5100);
+}
+
+static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+ pr_debug("%s: vddio %d\n", __func__, on);
+
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->pull_mb_to_vddio) {
+ mbhc->mbhc_cb->pull_mb_to_vddio(mbhc->codec, on);
+ goto exit;
+ }
+
+ if (on) {
+ snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 1 << 7, 1 << 7);
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+ 1 << 4, 0);
+ } else {
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+ 1 << 4, 1 << 4);
+ snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 1 << 7, 0);
+ }
+
+exit:
+ /*
+ * Wait for the micbias to settle down to vddio
+ * when the micbias to vddio switch is enabled.
+ */
+ if (on)
+ usleep_range(10000, 10100);
+}
+
+static int wcd9xxx_hphl_status(struct wcd9xxx_mbhc *mbhc)
+{
+ u16 hph, status;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ hph = snd_soc_read(codec, WCD9XXX_A_MBHC_HPH);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x02);
+ usleep_range(WCD9XXX_HPHL_STATUS_READY_WAIT_US,
+ WCD9XXX_HPHL_STATUS_READY_WAIT_US +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ status = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_STATUS);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_HPH, hph);
+ return status;
+}
+
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_cs_find_plug_type(struct wcd9xxx_mbhc *mbhc,
+ struct wcd9xxx_mbhc_detect *dt, const int size,
+ bool highhph,
+ unsigned long event_state)
+{
+ int i;
+ int vdce, mb_mv;
+ int ch, sz, delta_thr;
+ int minv = 0, maxv = INT_MIN;
+ struct wcd9xxx_mbhc_detect *d = dt;
+ struct wcd9xxx_mbhc_detect *dprev = d, *dmicbias = NULL, *dgnd = NULL;
+ enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
+
+ const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ s16 hs_max, no_mic, dce_z;
+ int highhph_cnt = 0;
+
+ pr_debug("%s: enter\n", __func__);
+ pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
+
+ sz = size - 1;
+ for (i = 0, d = dt, ch = 0; i < sz; i++, d++) {
+ if (d->mic_bias) {
+ dce_z = mbhc->mbhc_data.dce_z;
+ mb_mv = mbhc->mbhc_data.micb_mv;
+ hs_max = plug_type->v_hs_max;
+ no_mic = plug_type->v_no_mic;
+ } else {
+ dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
+ mb_mv = VDDIO_MICBIAS_MV;
+ hs_max = WCD9XXX_V_CS_HS_MAX;
+ no_mic = WCD9XXX_V_CS_NO_MIC;
+ }
+
+ vdce = __wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce,
+ dce_z, (u32)mb_mv);
+ d->_vdces = vdce;
+ if (d->_vdces < no_mic)
+ d->_type = PLUG_TYPE_HEADPHONE;
+ else if (d->_vdces >= hs_max) {
+ d->_type = PLUG_TYPE_HIGH_HPH;
+ highhph_cnt++;
+ } else
+ d->_type = PLUG_TYPE_HEADSET;
+
+ pr_debug("%s: DCE #%d, %04x, V %04d(%04d), HPHL %d TYPE %d\n",
+ __func__, i, d->dce, vdce, d->_vdces,
+ d->hphl_status & 0x01,
+ d->_type);
+
+ ch += d->hphl_status & 0x01;
+ if (!d->swap_gnd && !d->mic_bias) {
+ if (maxv < d->_vdces)
+ maxv = d->_vdces;
+ if (!minv || minv > d->_vdces)
+ minv = d->_vdces;
+ }
+ if ((!d->mic_bias &&
+ (d->_vdces >= WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV &&
+ d->_vdces <= WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV)) ||
+ (d->mic_bias &&
+ (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
+ d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV))) {
+ pr_debug("%s: within invalid range\n", __func__);
+ type = PLUG_TYPE_INVALID;
+ goto exit;
+ }
+ }
+
+ delta_thr = ((highhph_cnt == sz) || highhph) ?
+ WCD9XXX_MB_MEAS_DELTA_MAX_MV :
+ WCD9XXX_CS_MEAS_DELTA_MAX_MV;
+
+ for (i = 0, d = dt; i < sz; i++, d++) {
+ if ((i > 0) && !d->mic_bias && !d->swap_gnd &&
+ (d->_type != dprev->_type)) {
+ pr_debug("%s: Invalid, inconsistent types\n", __func__);
+ type = PLUG_TYPE_INVALID;
+ goto exit;
+ }
+
+ if (!d->swap_gnd && !d->mic_bias &&
+ (abs(minv - d->_vdces) > delta_thr ||
+ abs(maxv - d->_vdces) > delta_thr)) {
+ pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
+ __func__, d->_vdces, minv, maxv);
+ type = PLUG_TYPE_INVALID;
+ goto exit;
+ } else if (d->swap_gnd) {
+ dgnd = d;
+ }
+
+ if (!d->mic_bias && !d->swap_gnd)
+ dprev = d;
+ else if (d->mic_bias)
+ dmicbias = d;
+ }
+ if (dgnd && dt->_type != PLUG_TYPE_HEADSET &&
+ dt->_type != dgnd->_type) {
+ pr_debug("%s: Invalid, inconsistent types\n", __func__);
+ type = PLUG_TYPE_INVALID;
+ goto exit;
+ }
+
+ type = dt->_type;
+ if (dmicbias) {
+ if (dmicbias->_type == PLUG_TYPE_HEADSET &&
+ (dt->_type == PLUG_TYPE_HIGH_HPH ||
+ dt->_type == PLUG_TYPE_HEADSET)) {
+ type = PLUG_TYPE_HEADSET;
+ if (dt->_type == PLUG_TYPE_HIGH_HPH) {
+ pr_debug("%s: Headset with threshold on MIC detected\n",
+ __func__);
+ if (mbhc->mbhc_cfg->micbias_enable_flags &
+ (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
+ mbhc->micbias_enable = true;
+ }
+ }
+ }
+
+ if (type == PLUG_TYPE_HEADSET && dgnd && !dgnd->mic_bias) {
+ /* if plug type is Headphone report as GND_MIC_SWAP */
+ if (dgnd->_type == PLUG_TYPE_HEADPHONE) {
+ pr_debug("%s: GND_MIC_SWAP\n", __func__);
+ type = PLUG_TYPE_GND_MIC_SWAP;
+ /*
+ * if type is GND_MIC_SWAP we should not check
+ * HPHL status hence goto exit
+ */
+ goto exit;
+ } else if (dgnd->_type != PLUG_TYPE_HEADSET && !dmicbias) {
+ pr_debug("%s: Invalid, inconsistent types\n", __func__);
+ type = PLUG_TYPE_INVALID;
+ }
+ }
+
+ if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
+ pr_debug("%s: HPHL PA was ON\n", __func__);
+ } else if (ch != sz && ch > 0) {
+ pr_debug("%s: Invalid, inconsistent HPHL..\n", __func__);
+ type = PLUG_TYPE_INVALID;
+ goto exit;
+ }
+
+ if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
+ if (((type == PLUG_TYPE_HEADSET ||
+ type == PLUG_TYPE_HEADPHONE) && ch != sz)) {
+ pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
+ __func__, type);
+ type = PLUG_TYPE_INVALID;
+ }
+ }
+
+ if (type == PLUG_TYPE_HEADSET &&
+ (mbhc->mbhc_cfg->micbias_enable_flags &
+ (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET)))
+ mbhc->micbias_enable = true;
+
+exit:
+ pr_debug("%s: Plug type %d detected\n", __func__, type);
+ return type;
+}
+
+/*
+ * wcd9xxx_find_plug_type : Find out and return the best plug type with given
+ * list of wcd9xxx_mbhc_detect structure.
+ * param mbhc wcd9xxx_mbhc structure
+ * param dt collected measurements
+ * param size array size of dt
+ * param event_state mbhc->event_state when dt is collected
+ */
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc,
+ struct wcd9xxx_mbhc_detect *dt, const int size,
+ unsigned long event_state)
+{
+ int i;
+ int ch;
+ enum wcd9xxx_mbhc_plug_type type;
+ int vdce;
+ struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL;
+ int maxv = 0, minv = 0;
+ const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ const s16 hs_max = plug_type->v_hs_max;
+ const s16 no_mic = plug_type->v_no_mic;
+
+ pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
+
+ for (i = 0, d = dt, ch = 0; i < size; i++, d++) {
+ vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce);
+ if (d->vddio)
+ d->_vdces = scale_v_micb_vddio(mbhc, vdce, false);
+ else
+ d->_vdces = vdce;
+
+ if (d->_vdces >= no_mic && d->_vdces < hs_max)
+ d->_type = PLUG_TYPE_HEADSET;
+ else if (d->_vdces < no_mic)
+ d->_type = PLUG_TYPE_HEADPHONE;
+ else
+ d->_type = PLUG_TYPE_HIGH_HPH;
+
+ ch += d->hphl_status & 0x01;
+ if (!d->swap_gnd && !d->hwvalue && !d->vddio) {
+ if (maxv < d->_vdces)
+ maxv = d->_vdces;
+ if (!minv || minv > d->_vdces)
+ minv = d->_vdces;
+ }
+
+ pr_debug("%s: DCE #%d, %04x, V %04d(%04d), GND %d, VDDIO %d, HPHL %d TYPE %d\n",
+ __func__, i, d->dce, vdce, d->_vdces,
+ d->swap_gnd, d->vddio, d->hphl_status & 0x01,
+ d->_type);
+
+
+ /*
+ * If GND and MIC prongs are aligned to HPHR and GND of
+ * headphone, codec measures the voltage based on
+ * impedance between HPHR and GND which results in ~80mv.
+ * Avoid this.
+ */
+ if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
+ d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) {
+ pr_debug("%s: within invalid range\n", __func__);
+ type = PLUG_TYPE_INVALID;
+ goto exit;
+ }
+ }
+
+ if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
+ pr_debug("%s: HPHL PA was ON\n", __func__);
+ } else if (ch != size && ch > 0) {
+ pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
+ type = PLUG_TYPE_INVALID;
+ goto exit;
+ }
+
+ for (i = 0, dprev = NULL, d = dt; i < size; i++, d++) {
+ if (d->vddio) {
+ dvddio = d;
+ continue;
+ }
+
+ if ((i > 0) && (dprev != NULL) && (d->_type != dprev->_type)) {
+ pr_debug("%s: Invalid, inconsistent types\n", __func__);
+ type = PLUG_TYPE_INVALID;
+ goto exit;
+ }
+
+ if (!d->swap_gnd && !d->hwvalue &&
+ (abs(minv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV ||
+ abs(maxv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV)) {
+ pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
+ __func__, d->_vdces, minv, maxv);
+ type = PLUG_TYPE_INVALID;
+ goto exit;
+ } else if (d->swap_gnd) {
+ dgnd = d;
+ }
+ dprev = d;
+ }
+
+ WARN_ON(i != size);
+ type = dt->_type;
+ if (type == PLUG_TYPE_HEADSET && dgnd) {
+ if ((dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MIN_MV <
+ minv) &&
+ (dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MAX_MV >
+ maxv))
+ type = PLUG_TYPE_GND_MIC_SWAP;
+ }
+
+ /* if HPHL PA was on, we cannot use hphl status */
+ if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
+ if (((type == PLUG_TYPE_HEADSET ||
+ type == PLUG_TYPE_HEADPHONE) && ch != size) ||
+ (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
+ pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
+ __func__, type);
+ type = PLUG_TYPE_INVALID;
+ }
+ }
+
+ if (type == PLUG_TYPE_HEADSET) {
+ if (dvddio && ((dvddio->_vdces > hs_max) ||
+ (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD))) {
+ pr_debug("%s: Headset with threshold on MIC detected\n",
+ __func__);
+ if (mbhc->mbhc_cfg->micbias_enable_flags &
+ (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
+ mbhc->micbias_enable = true;
+ } else {
+ pr_debug("%s: Headset with regular MIC detected\n",
+ __func__);
+ if (mbhc->mbhc_cfg->micbias_enable_flags &
+ (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))
+ mbhc->micbias_enable = true;
+ }
+ }
+exit:
+ pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__,
+ type, mbhc->micbias_enable);
+ return type;
+}
+
+/*
+ * Pull down MBHC micbias for provided duration in microsecond.
+ */
+static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us)
+{
+ bool micbiasconn = false;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg;
+
+ /*
+ * Disable MBHC to micbias connection to pull down
+ * micbias and pull down micbias for a moment.
+ */
+ if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) {
+ WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n");
+ return -EFAULT;
+ }
+
+ if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) {
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+ 1 << 4, 0);
+ micbiasconn = true;
+ }
+
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+
+ /*
+ * Pull down for 1ms to discharge bias. Give small margin (10us) to be
+ * able to get consistent result across DCEs.
+ */
+ usleep_range(1000, 1000 + 10);
+
+ if (micbiasconn)
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+ 1 << 4, 1 << 4);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+ return 0;
+}
+
+/* Called under codec resource lock acquisition */
+void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc,
+ struct mbhc_micbias_regs *mbhc_micb_regs,
+ bool on, bool highhph)
+{
+ struct snd_soc_codec *codec;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+ WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ codec = mbhc->codec;
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ if ((on && mbhc->is_cs_enabled) ||
+ (!on && !mbhc->is_cs_enabled)) {
+ pr_debug("%s: Current source is already %s\n",
+ __func__, on ? "ON" : "OFF");
+ return;
+ }
+
+ if (on) {
+ pr_debug("%s: enabling current source\n", __func__);
+ /* Nsc to 9 */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x78, 0x48);
+ /* pull down diode bit to 0 */
+ snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
+ 0x01, 0x00);
+ /*
+ * Keep the low power insertion/removal
+ * detection (reg 0x3DD) disabled
+ */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL,
+ 0x01, 0x00);
+ /*
+ * Enable the Mic Bias current source
+ * Write bits[6:5] of register MICB_2_MBHC to 0x3 (V_20_UA)
+ * Write bit[7] of register MICB_2_MBHC to 1
+ * (INS_DET_ISRC_EN__ENABLE)
+ * MICB_2_MBHC__SCHT_TRIG_EN to 1
+ */
+ snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
+ 0xF0, 0xF0);
+ /* Disconnect MBHC Override from MicBias and LDOH */
+ snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00);
+ mbhc->is_cs_enabled = true;
+ } else {
+ pr_debug("%s: disabling current source\n", __func__);
+ /* Connect MBHC Override from MicBias and LDOH */
+ snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10);
+ /* INS_DET_ISRC_CTL to acdb value */
+ snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
+ 0x60, plug_det->mic_current << 5);
+ if (!highhph) {
+ /* INS_DET_ISRC_EN__ENABLE to 0 */
+ snd_soc_update_bits(codec,
+ mbhc_micb_regs->mbhc_reg,
+ 0x80, 0x00);
+ /* MICB_2_MBHC__SCHT_TRIG_EN to 0 */
+ snd_soc_update_bits(codec,
+ mbhc_micb_regs->mbhc_reg,
+ 0x10, 0x00);
+ }
+ /* Nsc to acdb value */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+ btn_det->mbhc_nsc << 3);
+ mbhc->is_cs_enabled = false;
+ }
+}
+
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_codec_cs_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
+ enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
+ int i;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ BUG_ON(NUM_DCE_PLUG_INS_DETECT < 4);
+
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
+ rt[0].swap_gnd = false;
+ rt[0].vddio = false;
+ rt[0].hwvalue = true;
+ rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
+ rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, &mbhc->mbhc_bias_regs,
+ true);
+ rt[0].mic_bias = false;
+
+ for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
+ rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 3);
+ rt[i].mic_bias = ((i == NUM_DCE_PLUG_INS_DETECT - 4) &&
+ highhph);
+ rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
+ if (rt[i].swap_gnd)
+ wcd9xxx_codec_hphr_gnd_switch(codec, true);
+
+ if (rt[i].mic_bias)
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_bias_regs,
+ false, false);
+
+ rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true);
+ if (rt[i].mic_bias)
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_bias_regs,
+ true, false);
+ if (rt[i].swap_gnd)
+ wcd9xxx_codec_hphr_gnd_switch(codec, false);
+ }
+
+ /* recalibrate DCE/STA GND voltages */
+ wcd9xxx_recalibrate(mbhc, &mbhc->mbhc_bias_regs, true);
+
+ type = wcd9xxx_cs_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), highhph,
+ mbhc->event_state);
+
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+ pr_debug("%s: plug_type:%d\n", __func__, type);
+
+ return type;
+}
+
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
+{
+ int i;
+ bool vddioon;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
+ struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
+ enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ /* make sure override is on */
+ WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
+
+ /* GND and MIC swap detection requires at least 2 rounds of DCE */
+ BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2);
+ detect_use_vddio_switch = mbhc->mbhc_cfg->use_vddio_meas;
+
+ /*
+ * There are chances vddio switch is on and cfilt voltage is adjusted
+ * to vddio voltage even after plug type removal reported.
+ */
+ vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
+ pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
+
+ plug_type_ptr =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+ /*
+ * cfilter in fast mode requires 1ms to charge up and down micbias
+ * fully.
+ */
+ (void) wcd9xxx_pull_down_micbias(mbhc,
+ WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
+
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
+ rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
+ rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, &mbhc->mbhc_bias_regs,
+ false);
+ rt[0].swap_gnd = false;
+ rt[0].vddio = false;
+ rt[0].hwvalue = true;
+ for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
+ rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
+ if (detect_use_vddio_switch)
+ rt[i].vddio = (i == 1);
+ else
+ rt[i].vddio = false;
+ rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
+ rt[i].hwvalue = false;
+ if (rt[i].swap_gnd)
+ wcd9xxx_codec_hphr_gnd_switch(codec, true);
+ if (rt[i].vddio)
+ wcd9xxx_onoff_vddio_switch(mbhc, true);
+ /*
+ * Pull down micbias to detect headset with mic which has
+ * threshold and to have more consistent voltage measurements.
+ *
+ * cfilter in fast mode requires 1ms to charge up and down
+ * micbias fully.
+ */
+ (void) wcd9xxx_pull_down_micbias(mbhc,
+ WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
+ rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+ if (rt[i].vddio)
+ wcd9xxx_onoff_vddio_switch(mbhc, false);
+ if (rt[i].swap_gnd)
+ wcd9xxx_codec_hphr_gnd_switch(codec, false);
+ }
+ /* recalibrate DCE/STA GND voltages */
+ wcd9xxx_recalibrate(mbhc, &mbhc->mbhc_bias_regs, false);
+
+ if (vddioon)
+ __wcd9xxx_switch_micbias(mbhc, 1, false, false);
+
+ type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
+ mbhc->event_state);
+
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+ pr_debug("%s: leave\n", __func__);
+ return type;
+}
+
+static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
+{
+ if (mbhc->mbhc_cfg->gpio)
+ return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
+ mbhc->mbhc_cfg->gpio_level_insert);
+ else if (mbhc->mbhc_cfg->insert_detect) {
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->insert_rem_status)
+ return mbhc->mbhc_cb->insert_rem_status(mbhc->codec);
+ else
+ return snd_soc_read(mbhc->codec,
+ WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
+ (1 << 2);
+ } else
+ WARN(1, "Invalid jack detection configuration\n");
+
+ return true;
+}
+
+static bool is_clk_active(struct snd_soc_codec *codec)
+{
+ return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
+}
+
+static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
+ int insertion, int trigger, bool padac_off)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ int central_bias_enabled = 0;
+ const struct wcd9xxx_mbhc_general_cfg *generic =
+ WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+ const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+ WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
+ __func__, insertion, trigger);
+
+ if (!mbhc->mbhc_cfg->calibration) {
+ pr_err("Error, no wcd9xxx calibration\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
+
+ /*
+ * Make sure mic bias and Mic line schmitt trigger
+ * are turned OFF
+ */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+
+ if (insertion) {
+ wcd9xxx_switch_micbias(mbhc, 0);
+
+ /* DAPM can manipulate PA/DAC bits concurrently */
+ if (padac_off == true)
+ wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+
+ if (trigger & MBHC_USE_HPHL_TRIGGER) {
+ /* Enable HPH Schmitt Trigger */
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
+ 0x11);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
+ plug_det->hph_current << 2);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
+ 0x02);
+ }
+ if (trigger & MBHC_USE_MB_TRIGGER) {
+ /* enable the mic line schmitt trigger */
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x60, plug_det->mic_current << 5);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x00);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x10);
+ }
+
+ /* setup for insetion detection */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
+ } else {
+ pr_debug("setup for removal detection\n");
+ /* Make sure the HPH schmitt trigger is OFF */
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
+
+ /* enable the mic line schmitt trigger */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
+ plug_det->mic_current << 5);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x10);
+
+ /* Setup for low power removal detection */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
+ 0x2);
+ }
+
+ if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
+ /* called by interrupt */
+ if (!is_clk_active(codec)) {
+ wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x06, 0);
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
+ } else
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x06, 0);
+ }
+
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
+
+ /* If central bandgap disabled */
+ if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
+ usleep_range(generic->t_bg_fast_settle,
+ generic->t_bg_fast_settle +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ central_bias_enabled = 1;
+ }
+
+ /* If LDO_H disabled */
+ if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
+ usleep_range(generic->t_ldoh, generic->t_ldoh +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
+
+ if (central_bias_enabled)
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
+ 0);
+ }
+
+ if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc)
+ snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
+ 0x3, mbhc->mbhc_cfg->micbias);
+
+ wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
+ pr_debug("%s: leave\n", __func__);
+
+ return 0;
+}
+
+/*
+ * Function to determine whether anc microphone is preset or not.
+ * Return true if anc microphone is detected or false if not detected.
+ */
+static bool wcd9xxx_detect_anc_plug_type(struct wcd9xxx_mbhc *mbhc)
+{
+ struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT - 1];
+ bool anc_mic_found = true;
+ int i, mb_mv;
+ const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ s16 hs_max, dce_z;
+ s16 no_mic;
+ bool override_en;
+ bool timedout;
+ unsigned long timeout, retry = 0;
+ enum wcd9xxx_mbhc_plug_type type;
+ bool cs_enable;
+
+ if (mbhc->mbhc_cfg->anc_micbias != MBHC_MICBIAS3 &&
+ mbhc->mbhc_cfg->anc_micbias != MBHC_MICBIAS2)
+ return false;
+
+ pr_debug("%s: enter\n", __func__);
+
+ override_en = (snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
+ 0x04) ? true : false;
+ cs_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
+ (1 << MBHC_CS_ENABLE_DET_ANC)) != 0) &&
+ (!(snd_soc_read(mbhc->codec,
+ mbhc->mbhc_anc_bias_regs.ctl_reg) & 0x80)) &&
+ (mbhc->mbhc_cfg->micbias != mbhc->mbhc_cfg->anc_micbias);
+
+ if (cs_enable) {
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_anc_bias_regs,
+ true, false);
+ } else {
+ if (mbhc->mbhc_cfg->anc_micbias == MBHC_MICBIAS3) {
+ if (mbhc->micbias_enable_cb)
+ mbhc->micbias_enable_cb(mbhc->codec, true,
+ mbhc->mbhc_cfg->anc_micbias);
+ else
+ return false;
+ } else {
+ /* Enable override */
+ if (!override_en)
+ wcd9xxx_turn_onoff_override(mbhc, true);
+ }
+ }
+
+ if (!cs_enable) {
+ hs_max = plug_type->v_hs_max;
+ no_mic = plug_type->v_no_mic;
+ dce_z = mbhc->mbhc_data.dce_z;
+ mb_mv = mbhc->mbhc_data.micb_mv;
+ } else {
+ hs_max = WCD9XXX_V_CS_HS_MAX;
+ no_mic = WCD9XXX_V_CS_NO_MIC;
+ mb_mv = VDDIO_MICBIAS_MV;
+ dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
+ }
+
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
+
+ timeout = jiffies + msecs_to_jiffies(ANC_HPH_DETECT_PLUG_TIME_MS);
+ anc_mic_found = true;
+
+ while (!(timedout = time_after(jiffies, timeout))) {
+ retry++;
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low\n", __func__);
+ anc_mic_found = false;
+ break;
+ }
+
+ pr_debug("%s: Retry attempt %lu", __func__, retry - 1);
+
+ rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
+ rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc,
+ &mbhc->mbhc_anc_bias_regs,
+ cs_enable);
+ rt[0]._vdces = __wcd9xxx_codec_sta_dce_v(mbhc, true, rt[0].dce,
+ dce_z, (u32)mb_mv);
+
+ if (rt[0]._vdces >= no_mic && rt[0]._vdces < hs_max)
+ rt[0]._type = PLUG_TYPE_HEADSET;
+ else if (rt[0]._vdces < no_mic)
+ rt[0]._type = PLUG_TYPE_HEADPHONE;
+ else
+ rt[0]._type = PLUG_TYPE_HIGH_HPH;
+
+ pr_debug("%s: DCE #%d, V %04d, HPHL %d TYPE %d\n",
+ __func__, 0, rt[0]._vdces,
+ rt[0].hphl_status & 0x01,
+ rt[0]._type);
+
+ for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
+ rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1,
+ true, true);
+ rt[i]._vdces = __wcd9xxx_codec_sta_dce_v(mbhc, true,
+ rt[i].dce, dce_z,
+ (u32) mb_mv);
+
+ if (rt[i]._vdces >= no_mic && rt[i]._vdces < hs_max)
+ rt[i]._type = PLUG_TYPE_HEADSET;
+ else if (rt[i]._vdces < no_mic)
+ rt[i]._type = PLUG_TYPE_HEADPHONE;
+ else
+ rt[i]._type = PLUG_TYPE_HIGH_HPH;
+
+ rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
+
+ pr_debug("%s: DCE #%d, V %04d, HPHL %d TYPE %d\n",
+ __func__, i, rt[i]._vdces,
+ rt[i].hphl_status & 0x01,
+ rt[i]._type);
+ }
+
+ /*
+ * Check for the "type" of all the 4 measurements
+ * If all 4 measurements have the Type as PLUG_TYPE_HEADSET
+ * then it is proper mic and declare that the plug has two mics
+ */
+ for (i = 0; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
+ if (i > 0 && (rt[i - 1]._type != rt[i]._type)) {
+ type = PLUG_TYPE_INVALID;
+ break;
+ } else {
+ type = rt[0]._type;
+ }
+ }
+
+ pr_debug("%s: Plug type found in ANC detection :%d",
+ __func__, type);
+
+ if (type != PLUG_TYPE_HEADSET)
+ anc_mic_found = false;
+ if (anc_mic_found || (type == PLUG_TYPE_HEADPHONE &&
+ mbhc->mbhc_cfg->hw_jack_type == FIVE_POLE_JACK) ||
+ (type == PLUG_TYPE_HIGH_HPH &&
+ mbhc->mbhc_cfg->hw_jack_type == SIX_POLE_JACK))
+ break;
+ }
+
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+ if (cs_enable) {
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_anc_bias_regs,
+ false, false);
+ } else {
+ if (mbhc->mbhc_cfg->anc_micbias == MBHC_MICBIAS3) {
+ if (mbhc->micbias_enable_cb)
+ mbhc->micbias_enable_cb(mbhc->codec, false,
+ mbhc->mbhc_cfg->anc_micbias);
+ } else {
+ /* Disable override */
+ if (!override_en)
+ wcd9xxx_turn_onoff_override(mbhc, false);
+ }
+ }
+ pr_debug("%s: leave\n", __func__);
+ return anc_mic_found;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
+ enum wcd9xxx_mbhc_plug_type plug_type)
+{
+ bool anc_mic_found = false;
+
+ pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
+ __func__, mbhc->current_plug, plug_type);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ if (plug_type == PLUG_TYPE_HEADPHONE &&
+ mbhc->current_plug == PLUG_TYPE_NONE) {
+ /*
+ * Nothing was reported previously
+ * report a headphone or unsupported
+ */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ if (!mbhc->mbhc_cfg->detect_extn_cable) {
+ if (mbhc->current_plug == PLUG_TYPE_HEADSET)
+ wcd9xxx_report_plug(mbhc, 0,
+ SND_JACK_HEADSET);
+ else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
+ wcd9xxx_report_plug(mbhc, 0,
+ SND_JACK_HEADPHONE);
+ }
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_HEADSET) {
+
+ if (mbhc->mbhc_cfg->enable_anc_mic_detect) {
+ /*
+ * Do not report Headset, because at this point
+ * it could be a ANC headphone having two mics.
+ * So, proceed further to detect if there is a
+ * second mic.
+ */
+ mbhc->scaling_mux_in = 0x08;
+ anc_mic_found = wcd9xxx_detect_anc_plug_type(mbhc);
+ }
+
+ if (anc_mic_found) {
+ /* Report ANC headphone */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_ANC_HEADPHONE);
+ } else {
+ /*
+ * If Headphone was reported previously, this will
+ * only report the mic line
+ */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
+ }
+ /* Button detection required RC oscillator */
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
+ /*
+ * sleep so that audio path completely tears down
+ * before report plug insertion to the user space
+ */
+ msleep(100);
+
+ wcd9xxx_start_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ /* High impedance device found. Report as LINEOUT*/
+ if (mbhc->current_plug == PLUG_TYPE_NONE)
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ pr_debug("%s: setup mic trigger for further detection\n",
+ __func__);
+ mbhc->lpi_enabled = true;
+ /*
+ * Do not enable HPHL trigger. If playback is active,
+ * it might lead to continuous false HPHL triggers
+ */
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
+ false);
+ } else {
+ if (mbhc->current_plug == PLUG_TYPE_NONE)
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ pr_debug("setup mic trigger for further detection\n");
+ mbhc->lpi_enabled = true;
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER,
+ false);
+ }
+ } else {
+ WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
+ mbhc->current_plug, plug_type);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
+{
+ enum wcd9xxx_mbhc_plug_type plug_type;
+ bool current_source_enable;
+
+ pr_debug("%s: enter\n", __func__);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
+ (1 << MBHC_CS_ENABLE_INSERTION)) != 0) &&
+ (!(snd_soc_read(mbhc->codec,
+ mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
+
+ mbhc->scaling_mux_in = 0x04;
+
+ if (current_source_enable) {
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ true, false);
+ plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
+ /*
+ * For other plug types, the current source disable
+ * will be done from wcd9xxx_correct_swch_plug
+ */
+ if (plug_type == PLUG_TYPE_HEADSET)
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_bias_regs,
+ false, false);
+ } else {
+ wcd9xxx_turn_onoff_override(mbhc, true);
+ plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+ wcd9xxx_turn_onoff_override(mbhc, false);
+ }
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ if (current_source_enable && mbhc->is_cs_enabled) {
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_bias_regs,
+ false, false);
+ }
+ pr_debug("%s: Switch level is low when determining plug\n",
+ __func__);
+ return;
+ }
+
+ if (plug_type == PLUG_TYPE_INVALID ||
+ plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_schedule_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ } else if (plug_type == PLUG_TYPE_HEADPHONE) {
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_schedule_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_schedule_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ } else {
+ pr_debug("%s: Valid plug found, determine plug type %d\n",
+ __func__, plug_type);
+ wcd9xxx_find_plug_and_report(mbhc, plug_type);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
+{
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ if (wcd9xxx_swch_level_remove(mbhc))
+ pr_debug("%s: Switch level low when determining plug\n",
+ __func__);
+ else
+ wcd9xxx_mbhc_decide_swch_plug(mbhc);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
+ bool is_removal)
+{
+ if (!is_removal) {
+ pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
+
+ rmb();
+ if (mbhc->lpi_enabled)
+ msleep(100);
+
+ rmb();
+ if (!mbhc->lpi_enabled) {
+ pr_debug("%s: lpi is disabled\n", __func__);
+ } else if (!wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Valid insertion, detect plug type\n",
+ __func__);
+ wcd9xxx_mbhc_decide_swch_plug(mbhc);
+ } else {
+ pr_debug("%s: Invalid insertion stop plug detection\n",
+ __func__);
+ }
+ } else if (mbhc->mbhc_cfg->detect_extn_cable) {
+ pr_debug("%s: Removal\n", __func__);
+ if (!wcd9xxx_swch_level_remove(mbhc)) {
+ /*
+ * Switch indicates, something is still inserted.
+ * This could be extension cable i.e. headset is
+ * removed from extension cable.
+ */
+ /* cancel detect plug */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ wcd9xxx_mbhc_decide_swch_plug(mbhc);
+ }
+ } else {
+ pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
+ }
+}
+
+static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv,
+ bool cs_enable)
+{
+ const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+
+ if (cs_enable)
+ return ((mic_mv > WCD9XXX_V_CS_NO_MIC) &&
+ (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false;
+ else
+ return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
+ mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) &&
+ (mic_mv > plug_type->v_no_mic) &&
+ (mic_mv < v_hs_max)) ? true : false;
+}
+
+/*
+ * called under codec_resource_lock acquisition
+ * returns true if mic voltage range is back to normal insertion
+ * returns false either if timedout or removed
+ */
+static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
+{
+ int i;
+ bool timedout, settled = false;
+ s32 mic_mv[NUM_DCE_PLUG_DETECT];
+ short mb_v[NUM_DCE_PLUG_DETECT];
+ unsigned long retry = 0, timeout;
+ bool cs_enable;
+
+ cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
+ (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
+ (!(snd_soc_read(mbhc->codec,
+ mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
+ if (cs_enable)
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ true, false);
+
+ timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+ while (!(timedout = time_after(jiffies, timeout))) {
+ retry++;
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch indicates removal\n", __func__);
+ break;
+ }
+
+ if (retry > 1)
+ msleep(250);
+ else
+ msleep(50);
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch indicates removal\n", __func__);
+ break;
+ }
+
+ if (cs_enable) {
+ for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
+ mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1,
+ true, true);
+ mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc,
+ true,
+ mb_v[i],
+ mbhc->mbhc_data.dce_nsc_cs_z,
+ (u32)VDDIO_MICBIAS_MV);
+ pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
+ __func__, retry, mic_mv[i], mb_v[i]);
+ }
+ } else {
+ for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
+ mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,
+ true);
+ mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1,
+ mb_v[i]);
+ pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
+ __func__, retry, mic_mv[i],
+ mb_v[i]);
+ }
+ }
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switcn indicates removal\n", __func__);
+ break;
+ }
+
+ if (mbhc->current_plug == PLUG_TYPE_NONE) {
+ pr_debug("%s : headset/headphone is removed\n",
+ __func__);
+ break;
+ }
+
+ for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
+ if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable))
+ break;
+
+ if (i == NUM_DCE_PLUG_DETECT) {
+ pr_debug("%s: MIC voltage settled\n", __func__);
+ settled = true;
+ msleep(200);
+ break;
+ }
+ }
+
+ if (cs_enable)
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ false, false);
+
+ if (timedout)
+ pr_debug("%s: Microphone did not settle in %d seconds\n",
+ __func__, HS_DETECT_PLUG_TIME_MS);
+ return settled;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
+{
+ pr_debug("%s: enter\n", __func__);
+ if (wcd9xxx_hs_remove_settle(mbhc))
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
+{
+ s16 dce, dcez;
+ unsigned long timeout;
+ bool removed = true;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const struct wcd9xxx_mbhc_general_cfg *generic =
+ WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+ bool cs_enable;
+ s16 cur_v_ins_h;
+ u32 mb_mv;
+
+ pr_debug("%s: enter\n", __func__);
+ if (mbhc->current_plug != PLUG_TYPE_HEADSET &&
+ mbhc->current_plug != PLUG_TYPE_ANC_HEADPHONE) {
+ pr_debug("%s(): Headset is not inserted, ignore removal\n",
+ __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x08);
+ return;
+ }
+
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+ /* If micbias is enabled, don't enable current source */
+ cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
+ (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
+ (!(snd_soc_read(codec,
+ mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
+ if (cs_enable)
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ true, false);
+
+ timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
+ do {
+ if (cs_enable) {
+ dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+ dcez = mbhc->mbhc_data.dce_nsc_cs_z;
+ mb_mv = VDDIO_MICBIAS_MV;
+ } else {
+ dce = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+ dcez = mbhc->mbhc_data.dce_z;
+ mb_mv = mbhc->mbhc_data.micb_mv;
+ }
+
+ pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
+ __wcd9xxx_codec_sta_dce_v(mbhc, true, dce,
+ dcez, mb_mv));
+
+ cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h :
+ (wcd9xxx_get_current_v(mbhc,
+ WCD9XXX_CURRENT_V_INS_H));
+
+ if (dce < cur_v_ins_h) {
+ removed = false;
+ break;
+ }
+ } while (!time_after(jiffies, timeout));
+ pr_debug("%s: headset %sactually removed\n", __func__,
+ removed ? "" : "not ");
+
+ if (cs_enable)
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ false, false);
+
+ if (removed) {
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ if (!wcd9xxx_swch_level_remove(mbhc)) {
+ /*
+ * extension cable is still plugged in
+ * report it as LINEOUT device
+ */
+ if (mbhc->hph_status == SND_JACK_HEADSET)
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc,
+ false);
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_enable_hs_detect(mbhc, 1,
+ MBHC_USE_MB_TRIGGER,
+ false);
+ }
+ } else {
+ /* Cancel possibly running hs_detect_work */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_noswch);
+ /*
+ * If this removal is not false, first check the micbias
+ * switch status and switch it to LDOH if it is already
+ * switched to VDDIO.
+ */
+ wcd9xxx_switch_micbias(mbhc, 0);
+
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER,
+ true);
+ }
+ } else {
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
+ bool is_mb_trigger)
+{
+ /* Cancel possibly running hs_detect_work */
+ wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+
+ if (is_mb_trigger) {
+ pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
+ } else {
+ pr_debug("%s: HPHL trigger received, detecting plug type\n",
+ __func__);
+ wcd9xxx_mbhc_detect_plug_type(mbhc);
+ }
+}
+
+static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
+{
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter, removal interrupt\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ /*
+ * While we don't know whether MIC is there or not, let the resmgr know
+ * so micbias can be disabled temporarily
+ */
+ if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH_MIC, false);
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH, false);
+ } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH, false);
+ }
+
+ if (mbhc->mbhc_cfg->detect_extn_cable &&
+ !wcd9xxx_swch_level_remove(mbhc))
+ wcd9xxx_hs_remove_irq_noswch(mbhc);
+ else
+ wcd9xxx_hs_remove_irq_swch(mbhc);
+
+ if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH, true);
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH_MIC, true);
+ } else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+ wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+ WCD9XXX_COND_HPH, true);
+ }
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
+{
+ bool is_mb_trigger, is_removal;
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
+
+ is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
+ 0x10);
+ is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
+
+ /* Turn off both HPH and MIC line schmitt triggers */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+
+ if (mbhc->mbhc_cfg->detect_extn_cable &&
+ mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
+ wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
+ else
+ wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
+
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ short bias_value;
+ int dce_mv, sta_mv;
+ struct wcd9xxx_mbhc *mbhc;
+
+ pr_debug("%s:\n", __func__);
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
+
+ bias_value = wcd9xxx_read_sta_result(mbhc->codec);
+ sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
+
+ bias_value = wcd9xxx_read_dce_result(mbhc->codec);
+ dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
+ pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
+
+ pr_debug("%s: Reporting long button press event\n", __func__);
+ wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+
+ pr_debug("%s: leave\n", __func__);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
+}
+
+static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ struct wcd9xxx_core_resource *core_res;
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
+ codec = mbhc->codec;
+ core_res = mbhc->resmgr->core_res;
+
+ pr_debug("%s:\n", __func__);
+
+ /* Turn off both HPH and MIC line schmitt triggers */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion);
+ wcd9xxx_mbhc_detect_plug_type(mbhc);
+ wcd9xxx_unlock_sleep(core_res);
+}
+
+static bool wcd9xxx_mbhc_fw_validate(const void *data, size_t size)
+{
+ u32 cfg_offset;
+ struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+ struct firmware_cal fw;
+
+ fw.data = (void *)data;
+ fw.size = size;
+
+ if (fw.size < WCD9XXX_MBHC_CAL_MIN_SIZE)
+ return false;
+
+ /*
+ * Previous check guarantees that there is enough fw data up
+ * to num_btn
+ */
+ btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw.data);
+ cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data);
+ if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
+ return false;
+
+ /*
+ * Previous check guarantees that there is enough fw data up
+ * to start of impedance detection configuration
+ */
+ imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw.data);
+ cfg_offset = (u32) ((void *) imped_cfg - (void *) fw.data);
+
+ if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
+ return false;
+
+ if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
+ return false;
+
+ return true;
+}
+
+static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
+ enum meas_type dce, s16 vin_mv,
+ bool cs_enable)
+{
+ s16 diff, zero;
+ u32 mb_mv, in;
+ u16 value;
+ s16 dce_z;
+
+ mb_mv = mbhc->mbhc_data.micb_mv;
+ dce_z = mbhc->mbhc_data.dce_z;
+
+ if (mb_mv == 0) {
+ pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
+ return -EINVAL;
+ }
+ if (cs_enable) {
+ mb_mv = VDDIO_MICBIAS_MV;
+ dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
+ }
+
+ if (dce) {
+ diff = (mbhc->mbhc_data.dce_mb) - (dce_z);
+ zero = (dce_z);
+ } else {
+ diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
+ zero = (mbhc->mbhc_data.sta_z);
+ }
+ in = (u32) diff * vin_mv;
+
+ value = (u16) (in / mb_mv) + zero;
+ return value;
+}
+
+static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec;
+ s16 adj_v_hs_max;
+ s16 btn_mv = 0, btn_mv_sta[MBHC_V_IDX_NUM], btn_mv_dce[MBHC_V_IDX_NUM];
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+ u16 *btn_high;
+ int i;
+
+ pr_debug("%s: enter\n", __func__);
+ codec = mbhc->codec;
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+ mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false);
+ mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false);
+
+ mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
+ mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
+
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
+ true);
+ mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false);
+ mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false);
+ mbhc->mbhc_data.v_inval_ins_low =
+ scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
+ false);
+ mbhc->mbhc_data.v_inval_ins_high =
+ scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
+ false);
+ }
+ mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE,
+ WCD9XXX_V_CS_HS_MAX,
+ true);
+ pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__,
+ mbhc->mbhc_data.v_cs_ins_h);
+
+ btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_HIGH);
+ for (i = 0; i < btn_det->num_btn; i++)
+ btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
+
+ btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
+ btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
+ btn_mv_sta[MBHC_V_IDX_VDDIO] =
+ scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
+ btn_mv_dce[MBHC_V_IDX_VDDIO] =
+ scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
+
+ mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT],
+ false);
+ mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT],
+ false);
+ mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO],
+ false);
+ mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO],
+ false);
+
+ mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
+ mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
+ mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
+ mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
+
+ mbhc->mbhc_data.v_brl = BUTTON_MIN;
+
+ mbhc->mbhc_data.v_no_mic =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+ /*
+ * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
+ * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
+ * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
+ */
+ if (mbhc && mbhc->mbhc_cfg && mbhc->mbhc_cfg->mclk_cb_fn)
+ mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
+}
+
+/*
+ * Mic Bias Enable Decision
+ * Return true if high_hph_cnt is a power of 2 (!= 2)
+ * otherwise return false
+ */
+static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt)
+{
+ return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1));
+}
+
+static inline void wcd9xxx_handle_gnd_mic_swap(struct wcd9xxx_mbhc *mbhc,
+ int pt_gnd_mic_swap_cnt,
+ enum wcd9xxx_mbhc_plug_type plug_type)
+{
+ if (mbhc->mbhc_cfg->swap_gnd_mic &&
+ (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD)) {
+ /*
+ * if switch is toggled, check again,
+ * otherwise report unsupported plug
+ */
+ mbhc->mbhc_cfg->swap_gnd_mic(mbhc->codec);
+ } else if (pt_gnd_mic_swap_cnt >= GND_MIC_SWAP_THRESHOLD) {
+ /* Report UNSUPPORTED plug
+ * and continue polling
+ */
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ if (!mbhc->mbhc_cfg->detect_extn_cable) {
+ if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
+ wcd9xxx_report_plug(mbhc, 0,
+ SND_JACK_HEADPHONE);
+ else if (mbhc->current_plug == PLUG_TYPE_HEADSET)
+ wcd9xxx_report_plug(mbhc, 0,
+ SND_JACK_HEADSET);
+ }
+ if (mbhc->current_plug != plug_type)
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_UNSUPPORTED);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ }
+}
+
+static void wcd9xxx_correct_swch_plug(struct work_struct *work)
+{
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
+ unsigned long timeout;
+ int retry = 0, pt_gnd_mic_swap_cnt = 0;
+ int highhph_cnt = 0;
+ bool correction = false;
+ bool current_source_enable;
+ bool wrk_complete = true, highhph = false;
+
+ pr_debug("%s: enter\n", __func__);
+
+ mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
+ codec = mbhc->codec;
+
+ current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
+ (1 << MBHC_CS_ENABLE_POLLING)) != 0) &&
+ (!(snd_soc_read(codec,
+ mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
+
+ wcd9xxx_onoff_ext_mclk(mbhc, true);
+
+ /*
+ * Keep override on during entire plug type correction work.
+ *
+ * This is okay under the assumption that any switch irqs which use
+ * MBHC block cancel and sync this work so override is off again
+ * prior to switch interrupt handler's MBHC block usage.
+ * Also while this correction work is running, we can guarantee
+ * DAPM doesn't use any MBHC block as this work only runs with
+ * headphone detection.
+ */
+ if (current_source_enable) {
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ true, false);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ } else {
+ wcd9xxx_turn_onoff_override(mbhc, true);
+ }
+
+ timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+ while (!time_after(jiffies, timeout)) {
+ ++retry;
+ rmb();
+ if (mbhc->hs_detect_work_stop) {
+ wrk_complete = false;
+ pr_debug("%s: stop requested\n", __func__);
+ break;
+ }
+
+ msleep(HS_DETECT_PLUG_INERVAL_MS);
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ wrk_complete = false;
+ pr_debug("%s: Switch level is low\n", __func__);
+ break;
+ }
+
+ /* can race with removal interrupt */
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ if (current_source_enable)
+ plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc,
+ highhph);
+ else
+ plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+ pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
+ __func__, retry, mbhc->current_plug, plug_type);
+
+ highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ?
+ (highhph_cnt + 1) :
+ 0;
+ highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt);
+ if (plug_type == PLUG_TYPE_INVALID) {
+ pr_debug("Invalid plug in attempt # %d\n", retry);
+ if (!mbhc->mbhc_cfg->detect_extn_cable &&
+ retry == NUM_ATTEMPTS_TO_REPORT &&
+ mbhc->current_plug == PLUG_TYPE_NONE) {
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ }
+ } else if (plug_type == PLUG_TYPE_HEADPHONE) {
+ pr_debug("Good headphone detected, continue polling\n");
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ if (mbhc->current_plug != plug_type)
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ }
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+ pr_debug("%s: High HPH detected, continue polling\n",
+ __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ if (mbhc->current_plug != plug_type)
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_LINEOUT);
+ } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ }
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ } else {
+ if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ pt_gnd_mic_swap_cnt++;
+ if (pt_gnd_mic_swap_cnt >=
+ GND_MIC_SWAP_THRESHOLD)
+ wcd9xxx_handle_gnd_mic_swap(mbhc,
+ pt_gnd_mic_swap_cnt,
+ plug_type);
+ pr_debug("%s: unsupported HS detected, continue polling\n",
+ __func__);
+ continue;
+ } else {
+ pt_gnd_mic_swap_cnt = 0;
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ /* Turn off override/current source */
+ if (current_source_enable)
+ wcd9xxx_turn_onoff_current_source(mbhc,
+ &mbhc->mbhc_bias_regs,
+ false, false);
+ else
+ wcd9xxx_turn_onoff_override(mbhc,
+ false);
+ /*
+ * The valid plug also includes
+ * PLUG_TYPE_GND_MIC_SWAP
+ */
+ wcd9xxx_find_plug_and_report(mbhc, plug_type);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ pr_debug("Attempt %d found correct plug %d\n",
+ retry,
+ plug_type);
+ correction = true;
+ }
+ break;
+ }
+ }
+
+ highhph = false;
+ if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) {
+ pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
+ __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ wcd9xxx_find_plug_and_report(mbhc, plug_type);
+ highhph = true;
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ }
+
+ if (plug_type == PLUG_TYPE_HEADPHONE) {
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->hph_auto_pulldown_ctrl)
+ mbhc->mbhc_cb->hph_auto_pulldown_ctrl(codec, true);
+ }
+
+ if (!correction && current_source_enable) {
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+ false, highhph);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ } else if (!correction) {
+ wcd9xxx_turn_onoff_override(mbhc, false);
+ }
+
+ wcd9xxx_onoff_ext_mclk(mbhc, false);
+
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
+ wrk_complete) ||
+ mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
+ mbhc->current_plug == PLUG_TYPE_INVALID ||
+ (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
+ /* Enable removal detection */
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
+ }
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ }
+ pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
+ /* unlock sleep */
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
+}
+
+static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
+{
+ bool insert;
+ bool is_removed = false;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ mbhc->in_swch_irq_handler = true;
+ /* Wait here for debounce time */
+ usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+
+ /* cancel pending button press */
+ if (wcd9xxx_cancel_btn_work(mbhc))
+ pr_debug("%s: button press is canceled\n", __func__);
+
+ insert = !wcd9xxx_swch_level_remove(mbhc);
+ pr_debug("%s: Current plug type %d, insert %d\n", __func__,
+ mbhc->current_plug, insert);
+ if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
+
+ mbhc->lpi_enabled = false;
+ wmb();
+ /* cancel detect plug */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+
+ if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
+ (mbhc->current_plug != PLUG_TYPE_HIGH_HPH) &&
+ !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
+ (1 << 1))) {
+ pr_debug("%s: current plug: %d\n", __func__,
+ mbhc->current_plug);
+ goto exit;
+ }
+
+ /* Disable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
+ wcd9xxx_mbhc_detect_plug_type(mbhc);
+ } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
+ mbhc->lpi_enabled = false;
+ wmb();
+ /* cancel detect plug */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+
+ if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+ wcd9xxx_pause_hs_polling(mbhc);
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_ANC_HEADPHONE) {
+ wcd9xxx_pause_hs_polling(mbhc);
+ wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_ANC_HEADPHONE);
+ is_removed = true;
+ }
+
+ if (is_removed) {
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x02, 0x00);
+
+ /* Enable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
+ 0x01);
+ /* Make sure mic trigger is turned off */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x90, 0x00);
+ /* Reset MBHC State Machine */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x08);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x00);
+ /* Turn off override */
+ wcd9xxx_turn_onoff_override(mbhc, false);
+ }
+ }
+exit:
+ mbhc->in_swch_irq_handler = false;
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
+{
+ int r = IRQ_HANDLED;
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter\n", __func__);
+ if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) {
+ pr_warn("%s: failed to hold suspend\n", __func__);
+ r = IRQ_NONE;
+ } else {
+ /* Call handler */
+ wcd9xxx_swch_irq_handler(mbhc);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
+ }
+
+ pr_debug("%s: leave %d\n", __func__, r);
+ return r;
+}
+
+static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc)
+{
+ s16 mb_v;
+ int i = 0;
+ int r = 0;
+ const s16 v_ins_hu =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
+ const s16 v_ins_h =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
+ const s16 v_b1_hu =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
+ const s16 v_b1_h =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
+ const unsigned long timeout =
+ jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS);
+
+ while (time_before(jiffies, timeout)) {
+ /*
+ * This function needs to run measurements just few times during
+ * release debounce time. Make 1ms interval to avoid
+ * unnecessary excessive measurements.
+ */
+ usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ if (i == 0) {
+ mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
+ pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
+ if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
+ r = 1;
+ break;
+ }
+ } else {
+ mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+ pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
+ if (mb_v < v_b1_h || mb_v > v_ins_h) {
+ r = 1;
+ break;
+ }
+ }
+ i++;
+ }
+
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
+ const s32 micmv)
+{
+ s16 *v_btn_low, *v_btn_high;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ int i, btn = -1;
+
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_LOW);
+ v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_HIGH);
+
+ for (i = 0; i < btn_det->num_btn; i++) {
+ if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
+ btn = i;
+ break;
+ }
+ }
+
+ if (btn == -1)
+ pr_debug("%s: couldn't find button number for mic mv %d\n",
+ __func__, micmv);
+
+ return btn;
+}
+
+static int wcd9xxx_get_button_mask(const int btn)
+{
+ int mask = 0;
+ switch (btn) {
+ case 0:
+ mask = SND_JACK_BTN_0;
+ break;
+ case 1:
+ mask = SND_JACK_BTN_1;
+ break;
+ case 2:
+ mask = SND_JACK_BTN_2;
+ break;
+ case 3:
+ mask = SND_JACK_BTN_3;
+ break;
+ case 4:
+ mask = SND_JACK_BTN_4;
+ break;
+ case 5:
+ mask = SND_JACK_BTN_5;
+ break;
+ }
+ return mask;
+}
+
+static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z,
+ struct mbhc_micbias_regs *micb_regs,
+ bool norel_detection)
+{
+ s16 reg0, reg1;
+ int change;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ /* Pull down micbias to ground and disconnect vddio switch */
+ reg0 = snd_soc_read(codec, micb_regs->ctl_reg);
+ snd_soc_update_bits(codec, micb_regs->ctl_reg, 0x81, 0x1);
+ reg1 = snd_soc_read(codec, micb_regs->mbhc_reg);
+ snd_soc_update_bits(codec, micb_regs->mbhc_reg, 1 << 7, 0);
+
+ /* Disconnect override from micbias */
+ change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
+ 1 << 0);
+ usleep_range(1000, 1000 + 1000);
+ if (sta_z) {
+ *sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, norel_detection);
+ pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
+ }
+ if (dce_z) {
+ *dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, norel_detection);
+ pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
+ }
+
+ /* Connect override from micbias */
+ if (change)
+ snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
+ 1 << 4);
+ /* Disable pull down micbias to ground */
+ snd_soc_write(codec, micb_regs->mbhc_reg, reg1);
+ snd_soc_write(codec, micb_regs->ctl_reg, reg0);
+}
+
+/*
+ * This function recalibrates dce_z and sta_z parameters.
+ * No release detection will be false when this function is
+ * used.
+ */
+void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
+{
+ const u16 sta_z = mbhc->mbhc_data.sta_z;
+ const u16 dce_z = mbhc->mbhc_data.dce_z;
+
+ wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z,
+ &mbhc->mbhc_bias_regs, false);
+ pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
+ __func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
+ mbhc->mbhc_data.sta_z & 0xFFFF,
+ mbhc->mbhc_data.dce_z & 0xFFFF);
+
+ wcd9xxx_mbhc_calc_thres(mbhc);
+ wcd9xxx_calibrate_hs_polling(mbhc);
+}
+
+/*
+ * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
+ * to ceilmv + buffer
+ */
+static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv,
+ bool vddio)
+{
+ u16 v_brh, v_b1_hu;
+ int mv;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ void *calibration = mbhc->mbhc_cfg->calibration;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
+ mv = ceilmv + btn_det->v_btn_press_delta_cic;
+ if (vddio)
+ mv = scale_v_micb_vddio(mbhc, mv, true);
+ pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
+
+ if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
+ /*
+ * update LSB first so mbhc hardware block
+ * doesn't see too low value.
+ */
+ v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu &
+ 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+ (v_b1_hu >> 8) & 0xFF);
+ v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh &
+ 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+ (v_brh >> 8) & 0xFF);
+ }
+ return 0;
+}
+
+irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
+{
+ int i, mask;
+ bool vddio;
+ u8 mbhc_status;
+ s16 dce_z, sta_z;
+ s32 stamv, stamv_s;
+ s16 *v_btn_high;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ int btn = -1, meas = 0;
+ struct wcd9xxx_mbhc *mbhc = data;
+ const struct wcd9xxx_mbhc_btn_detect_cfg *d =
+ WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ short btnmeas[d->n_btn_meas + 1];
+ short dce[d->n_btn_meas + 1], sta;
+ s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
+ int n_btn_meas = d->n_btn_meas;
+ void *calibration = mbhc->mbhc_cfg->calibration;
+
+ pr_debug("%s: enter\n", __func__);
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ mutex_lock(&mbhc->mbhc_lock);
+ mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
+
+ if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
+ pr_debug("%s: mbhc is being recovered, skip button press\n",
+ __func__);
+ goto done;
+ }
+
+ mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
+
+ if (!mbhc->polling_active) {
+ pr_warn("%s: mbhc polling is not active, skip button press\n",
+ __func__);
+ goto done;
+ }
+
+ /* If switch nterrupt already kicked in, ignore button press */
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug("%s: Swtich level changed, ignore button press\n",
+ __func__);
+ btn = -1;
+ goto done;
+ }
+
+ /*
+ * setup internal micbias if codec uses internal micbias for
+ * headset detection
+ */
+ if (mbhc->mbhc_cfg->use_int_rbias) {
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
+ mbhc->mbhc_cb->setup_int_rbias(codec, true);
+ else
+ pr_err("%s: internal bias requested but codec did not provide callback\n",
+ __func__);
+ }
+
+
+ /* Measure scaled HW DCE */
+ vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ mbhc->mbhc_micbias_switched);
+
+ dce_z = mbhc->mbhc_data.dce_z;
+ sta_z = mbhc->mbhc_data.sta_z;
+
+ /* Measure scaled HW STA */
+ dce[0] = wcd9xxx_read_dce_result(codec);
+ sta = wcd9xxx_read_sta_result(codec);
+ if (mbhc_status != STATUS_REL_DETECTION) {
+ if (mbhc->mbhc_last_resume &&
+ !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
+ pr_debug("%s: Button is released after resume\n",
+ __func__);
+ n_btn_meas = 0;
+ } else {
+ pr_debug("%s: Button is released without resume",
+ __func__);
+ if (mbhc->update_z) {
+ wcd9xxx_update_z(mbhc);
+ dce_z = mbhc->mbhc_data.dce_z;
+ sta_z = mbhc->mbhc_data.sta_z;
+ mbhc->update_z = true;
+ }
+ stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
+ mbhc->mbhc_data.micb_mv);
+ if (vddio)
+ stamv_s = scale_v_micb_vddio(mbhc, stamv,
+ false);
+ else
+ stamv_s = stamv;
+ mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
+ dce_z, mbhc->mbhc_data.micb_mv);
+ mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
+ false) : mv[0];
+ btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
+ if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
+ btn = -1;
+ goto done;
+ }
+ }
+
+ for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
+ meas++)
+ dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
+
+ if (mbhc->update_z) {
+ wcd9xxx_update_z(mbhc);
+ dce_z = mbhc->mbhc_data.dce_z;
+ sta_z = mbhc->mbhc_data.sta_z;
+ mbhc->update_z = true;
+ }
+
+ stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
+ mbhc->mbhc_data.micb_mv);
+ if (vddio)
+ stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
+ else
+ stamv_s = stamv;
+ pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
+ sta & 0xFFFF, stamv, stamv_s);
+
+ /* determine pressed button */
+ mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z,
+ mbhc->mbhc_data.micb_mv);
+ mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
+ btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
+ pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
+ dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
+ if (n_btn_meas == 0)
+ btn = btnmeas[0];
+ for (meas = 1; (n_btn_meas && d->n_btn_meas &&
+ (meas < (d->n_btn_meas + 1))); meas++) {
+ mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z,
+ mbhc->mbhc_data.micb_mv);
+ mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
+ mv[meas];
+ btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
+ pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
+ __func__, meas, dce[meas] & 0xFFFF, mv[meas],
+ mv_s[meas], btnmeas[meas]);
+ /*
+ * if large enough measurements are collected,
+ * start to check if last all n_btn_con measurements were
+ * in same button low/high range
+ */
+ if (meas + 1 >= d->n_btn_con) {
+ for (i = 0; i < d->n_btn_con; i++)
+ if ((btnmeas[meas] < 0) ||
+ (btnmeas[meas] != btnmeas[meas - i]))
+ break;
+ if (i == d->n_btn_con) {
+ /* button pressed */
+ btn = btnmeas[meas];
+ break;
+ } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
+ /*
+ * if left measurements are less than n_btn_con,
+ * it's impossible to find button number
+ */
+ break;
+ }
+ }
+ }
+
+ if (btn >= 0) {
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug(
+ "%s: Switch irq triggered, ignore button press\n",
+ __func__);
+ goto done;
+ }
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
+ v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_HIGH);
+ WARN_ON(btn >= btn_det->num_btn);
+ /* reprogram release threshold to catch voltage ramp up early */
+ wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn], vddio);
+
+ mask = wcd9xxx_get_button_mask(btn);
+ mbhc->buttons_pressed |= mask;
+ wcd9xxx_lock_sleep(core_res);
+ if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
+ msecs_to_jiffies(400)) == 0) {
+ WARN(1, "Button pressed twice without release event\n");
+ wcd9xxx_unlock_sleep(core_res);
+ }
+ } else {
+ pr_debug("%s: bogus button press, too short press?\n",
+ __func__);
+ }
+
+ done:
+ pr_debug("%s: leave\n", __func__);
+ mutex_unlock(&mbhc->mbhc_lock);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
+{
+ int ret;
+ bool waitdebounce = true;
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ mbhc->mbhc_state = MBHC_STATE_RELEASE;
+
+ if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
+ ret = wcd9xxx_cancel_btn_work(mbhc);
+ if (ret == 0) {
+ pr_debug("%s: Reporting long button release event\n",
+ __func__);
+ wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
+ mbhc->buttons_pressed);
+ } else {
+ if (wcd9xxx_is_false_press(mbhc)) {
+ pr_debug("%s: Fake button press interrupt\n",
+ __func__);
+ } else {
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug("%s: Switch irq kicked in, ignore\n",
+ __func__);
+ } else {
+ pr_debug("%s: Reporting btn press\n",
+ __func__);
+ wcd9xxx_jack_report(mbhc,
+ &mbhc->button_jack,
+ mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+ pr_debug("%s: Reporting btn release\n",
+ __func__);
+ wcd9xxx_jack_report(mbhc,
+ &mbhc->button_jack,
+ 0, mbhc->buttons_pressed);
+ waitdebounce = false;
+ }
+ }
+ }
+
+ mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
+ }
+
+ wcd9xxx_calibrate_hs_polling(mbhc);
+
+ if (waitdebounce)
+ msleep(SWCH_REL_DEBOUNCE_TIME_MS);
+ wcd9xxx_start_hs_polling(mbhc);
+
+ pr_debug("%s: leave\n", __func__);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
+{
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec;
+
+ pr_info("%s: received HPHL OCP irq\n", __func__);
+
+ if (mbhc) {
+ codec = mbhc->codec;
+ if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
+ (!mbhc->hphrocp_cnt)) {
+ pr_info("%s: retry\n", __func__);
+ mbhc->hphlocp_cnt++;
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 0x10, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 0x10, 0x10);
+ } else {
+ wcd9xxx_disable_irq(mbhc->resmgr->core_res,
+ mbhc->intr_ids->hph_left_ocp);
+ mbhc->hph_status |= SND_JACK_OC_HPHL;
+ wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
+ mbhc->hph_status,
+ WCD9XXX_JACK_MASK);
+ }
+ } else {
+ pr_err("%s: Bad wcd9xxx private data\n", __func__);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
+{
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec;
+
+ pr_info("%s: received HPHR OCP irq\n", __func__);
+ codec = mbhc->codec;
+ if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
+ (!mbhc->hphlocp_cnt)) {
+ pr_info("%s: retry\n", __func__);
+ mbhc->hphrocp_cnt++;
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ } else {
+ wcd9xxx_disable_irq(mbhc->resmgr->core_res,
+ mbhc->intr_ids->hph_right_ocp);
+ mbhc->hph_status |= SND_JACK_OC_HPHR;
+ wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
+ mbhc->hph_status, WCD9XXX_JACK_MASK);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int wcd9xxx_acdb_mclk_index(const int rate)
+{
+ if (rate == MCLK_RATE_12288KHZ)
+ return 0;
+ else if (rate == MCLK_RATE_9600KHZ)
+ return 1;
+ else {
+ BUG_ON(1);
+ return -EINVAL;
+ }
+}
+
+static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
+{
+ u32 dce_wait, sta_wait;
+ u8 ncic, nmeas, navg;
+ void *calibration;
+ u8 *n_cic, *n_ready;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ u8 npoll = 4, nbounce_wait = 30;
+ struct snd_soc_codec *codec = mbhc->codec;
+ int idx = wcd9xxx_acdb_mclk_index(rate);
+ int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+ pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
+ rate);
+ calibration = mbhc->mbhc_cfg->calibration;
+
+ /*
+ * First compute the DCE / STA wait times depending on tunable
+ * parameters. The value is computed in microseconds
+ */
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
+ n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
+ n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
+ nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
+ navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
+
+ /* ncic stays with the same what we had during calibration */
+ ncic = n_cic[idxmclk];
+ dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
+ sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
+ mbhc->mbhc_data.t_dce = dce_wait;
+ /* give extra margin to sta for safety */
+ mbhc->mbhc_data.t_sta = sta_wait + 250;
+ mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
+ n_ready[idx]) + 10;
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
+
+ if (rate == MCLK_RATE_12288KHZ) {
+ npoll = 4;
+ nbounce_wait = 30;
+ } else if (rate == MCLK_RATE_9600KHZ) {
+ npoll = 3;
+ nbounce_wait = 23;
+ }
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
+{
+ u8 cfilt_mode;
+ u16 reg0, reg1, reg2;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ wcd9xxx_disable_irq(mbhc->resmgr->core_res,
+ mbhc->intr_ids->dce_est_complete);
+ wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+ /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
+ WARN_ON(!mbhc->mbhc_data.t_dce);
+ WARN_ON(!mbhc->mbhc_data.t_sta);
+
+ /*
+ * LDOH and CFILT are already configured during pdata handling.
+ * Only need to make sure CFILT and bandgap are in Fast mode.
+ * Need to restore defaults once calculation is done.
+ *
+ * In case when Micbias is powered by external source, request
+ * turn on the external voltage source for Calibration.
+ */
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
+ mbhc->mbhc_cb->enable_mb_source(codec, true, false);
+
+ cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, 0x00);
+
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->micbias_pulldown_ctrl)
+ mbhc->mbhc_cb->micbias_pulldown_ctrl(mbhc, false);
+
+ /*
+ * Micbias, CFILT, LDOH, MBHC MUX mode settings
+ * to perform ADC calibration
+ */
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
+ mbhc->mbhc_cb->select_cfilt(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
+ mbhc->mbhc_cfg->micbias << 5);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
+ snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
+ mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x04, 0x04);
+
+ /* Pull down micbias to ground */
+ reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
+ /* Disconnect override from micbias */
+ reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
+ snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
+ /* Connect the MUX to micbias */
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
+ /*
+ * Hardware that has external cap can delay mic bias ramping down up
+ * to 50ms.
+ */
+ msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
+ /* DCE measurement for 0 voltage */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false);
+
+ /* compute dce_z for current source */
+ reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+ WCD9XXX_MBHC_NSC_CS << 3);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true,
+ false);
+ pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__,
+ mbhc->mbhc_data.dce_nsc_cs_z);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2);
+
+ /* STA measurement for 0 voltage */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
+
+ /* Restore registers */
+ snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
+ snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1);
+
+ /* DCE measurment for MB voltage */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
+ /*
+ * Hardware that has external cap can delay mic bias ramping down up
+ * to 50ms.
+ */
+ msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+ usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
+
+ /* STA Measurement for MB Voltage */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
+ /*
+ * Hardware that has external cap can delay mic bias ramping down up
+ * to 50ms.
+ */
+ msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+ usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
+
+ /* Restore default settings. */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+ snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
+ usleep_range(100, 110);
+
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
+ mbhc->mbhc_cb->enable_mb_source(codec, false, false);
+
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->micbias_pulldown_ctrl)
+ mbhc->mbhc_cb->micbias_pulldown_ctrl(mbhc, true);
+
+ wcd9xxx_enable_irq(mbhc->resmgr->core_res,
+ mbhc->intr_ids->dce_est_complete);
+ wcd9xxx_turn_onoff_rel_detection(codec, true);
+
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
+{
+ int n;
+ u8 *gain;
+ struct wcd9xxx_mbhc_general_cfg *generic;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+ pr_debug("%s: enter\n", __func__);
+ generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ for (n = 0; n < 8; n++) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
+ 0x07, n);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
+ btn_det->c[n]);
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
+ btn_det->nc);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
+ generic->mbhc_nsa << 4);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
+ btn_det->n_meas);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
+ generic->mbhc_navg);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+ btn_det->mbhc_nsc << 3);
+
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->get_cdc_type &&
+ mbhc->mbhc_cb->get_cdc_type() !=
+ WCD9XXX_CDC_TYPE_HELICON) {
+ if (mbhc->resmgr->reg_addr->micb_4_mbhc)
+ snd_soc_update_bits(codec,
+ mbhc->resmgr->reg_addr->micb_4_mbhc,
+ 0x03, MBHC_MICBIAS2);
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
+
+ gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
+ gain[idx] << 3);
+ snd_soc_update_bits(codec, WCD9XXX_A_MICB_2_MBHC, 0x04, 0x04);
+
+ pr_debug("%s: leave\n", __func__);
+}
+
+static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
+{
+ int ret = 0;
+ void *core_res = mbhc->resmgr->core_res;
+
+ if (mbhc->mbhc_cfg->gpio) {
+ ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
+ wcd9xxx_mech_plug_detect_irq,
+ (IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING),
+ "headset detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request gpio irq %d\n", __func__,
+ mbhc->mbhc_cfg->gpio_irq);
+ } else {
+ ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
+ if (ret)
+ pr_err("%s: Failed to enable wake up irq %d\n",
+ __func__, mbhc->mbhc_cfg->gpio_irq);
+ }
+ } else if (mbhc->mbhc_cfg->insert_detect) {
+ /* Enable HPHL_10K_SW */
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 1 << 1, 1 << 1);
+
+ ret = wcd9xxx_request_irq(core_res,
+ mbhc->intr_ids->hs_jack_switch,
+ wcd9xxx_mech_plug_detect_irq,
+ "Jack Detect",
+ mbhc);
+ if (ret)
+ pr_err("%s: Failed to request insert detect irq %d\n",
+ __func__, mbhc->intr_ids->hs_jack_switch);
+ }
+
+ return ret;
+}
+
+static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ /* Enable MCLK during calibration */
+ wcd9xxx_onoff_ext_mclk(mbhc, true);
+ wcd9xxx_mbhc_setup(mbhc);
+ wcd9xxx_mbhc_cal(mbhc);
+ wcd9xxx_mbhc_calc_thres(mbhc);
+ wcd9xxx_onoff_ext_mclk(mbhc, false);
+ wcd9xxx_calibrate_hs_polling(mbhc);
+
+ /* Enable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
+ INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
+
+ if (!IS_ERR_VALUE(ret)) {
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ wcd9xxx_enable_irq(mbhc->resmgr->core_res,
+ mbhc->intr_ids->hph_left_ocp);
+ wcd9xxx_enable_irq(mbhc->resmgr->core_res,
+ mbhc->intr_ids->hph_right_ocp);
+
+ /* Initialize mechanical mbhc */
+ ret = wcd9xxx_setup_jack_detect_irq(mbhc);
+
+ if (!ret && mbhc->mbhc_cfg->gpio) {
+ /* Requested with IRQF_DISABLED */
+ enable_irq(mbhc->mbhc_cfg->gpio_irq);
+
+ /* Bootup time detection */
+ wcd9xxx_swch_irq_handler(mbhc);
+ } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
+ pr_debug("%s: Setting up codec own insert detection\n",
+ __func__);
+ /* Setup for insertion detection */
+ wcd9xxx_insert_detect_setup(mbhc, true);
+ }
+ }
+
+ pr_debug("%s: leave\n", __func__);
+
+ return ret;
+}
+
+static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ const struct firmware *fw;
+ struct firmware_cal *fw_data = NULL;
+ int ret = -1, retry = 0;
+ bool use_default_cal = false;
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
+ codec = mbhc->codec;
+
+ while (retry < FW_READ_ATTEMPTS) {
+ retry++;
+ pr_info("%s:Attempt %d to request MBHC firmware\n",
+ __func__, retry);
+ if (mbhc->mbhc_cb->get_hwdep_fw_cal)
+ fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(codec,
+ WCD9XXX_MBHC_CAL);
+ if (!fw_data)
+ ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
+ codec->dev);
+ /*
+ * if request_firmware and hwdep cal both fail then
+ * retry for few times before bailing out
+ */
+ if ((ret != 0) && !fw_data) {
+ usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ } else {
+ pr_info("%s: MBHC Firmware read succesful\n",
+ __func__);
+ break;
+ }
+ }
+ if (!fw_data)
+ pr_info("%s: using request_firmware\n", __func__);
+ else
+ pr_info("%s: using hwdep cal\n", __func__);
+ if (ret != 0 && !fw_data) {
+ pr_err("%s: Cannot load MBHC firmware use default cal\n",
+ __func__);
+ use_default_cal = true;
+ }
+ if (!use_default_cal) {
+ const void *data;
+ size_t size;
+
+ if (fw_data) {
+ data = fw_data->data;
+ size = fw_data->size;
+ } else {
+ data = fw->data;
+ size = fw->size;
+ }
+ if (wcd9xxx_mbhc_fw_validate(data, size) == false) {
+ pr_err("%s: Invalid MBHC cal data size use default cal\n",
+ __func__);
+ if (!fw_data)
+ release_firmware(fw);
+ } else {
+ if (fw_data) {
+ mbhc->mbhc_cfg->calibration =
+ (void *)fw_data->data;
+ mbhc->mbhc_cal = fw_data;
+ } else {
+ mbhc->mbhc_cfg->calibration =
+ (void *)fw->data;
+ mbhc->mbhc_fw = fw;
+ }
+ }
+ }
+
+ (void) wcd9xxx_init_and_calibrate(mbhc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ const int size = 768;
+ char buffer[size];
+ int n = 0;
+ struct wcd9xxx_mbhc *mbhc = file->private_data;
+ const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
+ const s16 v_ins_hu =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
+ const s16 v_ins_h =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
+ const s16 v_b1_hu =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
+ const s16 v_b1_h =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
+ const s16 v_br_h =
+ wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
+
+ n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
+ p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
+ n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
+ p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
+ n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
+ p->dce_nsc_cs_z,
+ __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
+ p->dce_nsc_cs_z,
+ VDDIO_MICBIAS_MV));
+ n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
+ p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
+ n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
+ p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
+ n += scnprintf(buffer + n, size - n, "t_dce = %d\n", p->t_dce);
+ n += scnprintf(buffer + n, size - n, "t_sta = %d\n", p->t_sta);
+ n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
+ n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
+ v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
+ n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
+ v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
+ n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
+ v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
+ n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
+ v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
+ n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
+ v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
+ n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
+ n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
+ p->v_no_mic,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
+ n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
+ p->v_inval_ins_low);
+ n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
+ p->v_inval_ins_high);
+ n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
+ !wcd9xxx_swch_level_remove(mbhc));
+ buffer[n] = 0;
+
+ return simple_read_from_buffer(buf, count, pos, buffer, n);
+}
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ char lbuf[32];
+ char *buf;
+ int rc;
+ struct wcd9xxx_mbhc *mbhc = filp->private_data;
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+ buf = (char *)lbuf;
+ mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
+ false : true;
+ return rc;
+}
+
+static const struct file_operations mbhc_trrs_debug_ops = {
+ .open = codec_debug_open,
+ .write = codec_debug_write,
+};
+
+static const struct file_operations mbhc_debug_ops = {
+ .open = codec_debug_open,
+ .read = codec_mbhc_debug_read,
+};
+
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+ mbhc->debugfs_poke =
+ debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
+ &mbhc_trrs_debug_ops);
+ mbhc->debugfs_mbhc =
+ debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
+ NULL, mbhc, &mbhc_debug_ops);
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+ debugfs_remove(mbhc->debugfs_poke);
+ debugfs_remove(mbhc->debugfs_mbhc);
+}
+#else
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+#endif
+
+int wcd9xxx_mbhc_set_keycode(struct wcd9xxx_mbhc *mbhc)
+{
+ enum snd_jack_types type = SND_JACK_BTN_0;
+ int i, ret, result = 0;
+ int *btn_key_code;
+
+ btn_key_code = mbhc->mbhc_cfg->key_code;
+
+ for (i = 0 ; i < 8 ; i++) {
+ if (btn_key_code[i] != 0) {
+ switch (i) {
+ case 0:
+ type = SND_JACK_BTN_0;
+ break;
+ case 1:
+ type = SND_JACK_BTN_1;
+ break;
+ case 2:
+ type = SND_JACK_BTN_2;
+ break;
+ case 3:
+ type = SND_JACK_BTN_3;
+ break;
+ case 4:
+ type = SND_JACK_BTN_4;
+ break;
+ case 5:
+ type = SND_JACK_BTN_5;
+ break;
+ default:
+ WARN_ONCE(1, "Wrong button number:%d\n", i);
+ result = -1;
+ break;
+ }
+ ret = snd_jack_set_key(mbhc->button_jack.jack,
+ type,
+ btn_key_code[i]);
+ if (ret) {
+ pr_err("%s: Failed to set code for %d\n",
+ __func__, btn_key_code[i]);
+ result = -1;
+ }
+ input_set_capability(
+ mbhc->button_jack.jack->input_dev,
+ EV_KEY, btn_key_code[i]);
+ pr_debug("%s: set btn%d key code:%d\n", __func__,
+ i, btn_key_code[i]);
+ }
+ }
+ return result;
+}
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+ struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+ int rc = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ if (!codec) {
+ pr_err("%s: no codec\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
+ mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
+ pr_err("Error: unsupported clock rate %d\n",
+ mbhc_cfg->mclk_rate);
+ return -EINVAL;
+ }
+
+ /* Save mbhc config */
+ mbhc->mbhc_cfg = mbhc_cfg;
+
+ /* Set btn key code */
+ if (wcd9xxx_mbhc_set_keycode(mbhc))
+ pr_err("Set btn key code error!!!\n");
+
+ /* Get HW specific mbhc registers' address */
+ wcd9xxx_get_mbhc_micbias_regs(mbhc, MBHC_PRIMARY_MIC_MB);
+
+ /* Get HW specific mbhc registers' address for anc */
+ wcd9xxx_get_mbhc_micbias_regs(mbhc, MBHC_ANC_MIC_MB);
+
+ /* Put CFILT in fast mode by default */
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, WCD9XXX_CFILT_FAST_MODE);
+
+ /*
+ * setup internal micbias if codec uses internal micbias for
+ * headset detection
+ */
+ if (mbhc->mbhc_cfg->use_int_rbias) {
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) {
+ mbhc->mbhc_cb->setup_int_rbias(codec, true);
+ } else {
+ pr_info("%s: internal bias requested but codec did not provide callback\n",
+ __func__);
+ }
+ }
+
+ /*
+ * If codec has specific clock gating for MBHC,
+ * remove the clock gate
+ */
+ if (mbhc->mbhc_cb &&
+ mbhc->mbhc_cb->enable_clock_gate)
+ mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
+
+ if (!mbhc->mbhc_cfg->read_fw_bin ||
+ (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) ||
+ (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) {
+ rc = wcd9xxx_init_and_calibrate(mbhc);
+ } else {
+ if (!mbhc->mbhc_fw || !mbhc->mbhc_cal)
+ schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
+ usecs_to_jiffies(FW_READ_TIMEOUT));
+ else
+ pr_debug("%s: Skipping to read mbhc fw, 0x%pK %pK\n",
+ __func__, mbhc->mbhc_fw, mbhc->mbhc_cal);
+ }
+
+ pr_debug("%s: leave %d\n", __func__, rc);
+ return rc;
+}
+EXPORT_SYMBOL(wcd9xxx_mbhc_start);
+
+void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
+{
+ if (mbhc->mbhc_fw || mbhc->mbhc_cal) {
+ cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
+ if (!mbhc->mbhc_cal)
+ release_firmware(mbhc->mbhc_fw);
+ mbhc->mbhc_fw = NULL;
+ mbhc->mbhc_cal = NULL;
+ }
+}
+EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
+
+static enum wcd9xxx_micbias_num
+wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
+{
+ enum wcd9xxx_micbias_num ret;
+ switch (event) {
+ case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
+ ret = MBHC_MICBIAS1;
+ break;
+ case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
+ ret = MBHC_MICBIAS2;
+ break;
+ case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
+ ret = MBHC_MICBIAS3;
+ break;
+ case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
+ ret = MBHC_MICBIAS4;
+ break;
+ default:
+ WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
+ ret = MBHC_MICBIAS_INVALID;
+ break;
+ }
+ return ret;
+}
+
+static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
+{
+ int ret;
+ switch (event) {
+ case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+ case WCD9XXX_EVENT_POST_CFILT_1_ON:
+ ret = WCD9XXX_CFILT1_SEL;
+ break;
+ case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+ case WCD9XXX_EVENT_POST_CFILT_2_ON:
+ ret = WCD9XXX_CFILT2_SEL;
+ break;
+ case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+ case WCD9XXX_EVENT_POST_CFILT_3_ON:
+ ret = WCD9XXX_CFILT3_SEL;
+ break;
+ default:
+ ret = -1;
+ }
+ return ret;
+}
+
+static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
+{
+ int cfilt;
+ const struct wcd9xxx_micbias_setting *mb_pdata =
+ mbhc->resmgr->micbias_pdata;
+
+ switch (mbhc->mbhc_cfg->micbias) {
+ case MBHC_MICBIAS1:
+ cfilt = mb_pdata->bias1_cfilt_sel;
+ break;
+ case MBHC_MICBIAS2:
+ cfilt = mb_pdata->bias2_cfilt_sel;
+ break;
+ case MBHC_MICBIAS3:
+ cfilt = mb_pdata->bias3_cfilt_sel;
+ break;
+ case MBHC_MICBIAS4:
+ cfilt = mb_pdata->bias4_cfilt_sel;
+ break;
+ default:
+ cfilt = MBHC_MICBIAS_INVALID;
+ break;
+ }
+ return cfilt;
+}
+
+static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe)
+ mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on);
+ else
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL,
+ 0x40, on ? 0x40 : 0x00);
+}
+
+static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
+ void *data)
+{
+ int ret = 0;
+ struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
+ struct snd_soc_codec *codec;
+ enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
+
+ pr_debug("%s: enter event %s(%d)\n", __func__,
+ wcd9xxx_get_event_string(event), event);
+
+ if (!mbhc || !mbhc->mbhc_cfg) {
+ pr_debug("mbhc not initialized\n");
+ return 0;
+ }
+ codec = mbhc->codec;
+ mutex_lock(&mbhc->mbhc_lock);
+ switch (event) {
+ /* MICBIAS usage change */
+ case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+ if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
+ wcd9xxx_event_to_micbias(event)) {
+ wcd9xxx_switch_micbias(mbhc, 0);
+ /*
+ * Enable MBHC TxFE whenever micbias is
+ * turned ON and polling is active
+ */
+ if (mbhc->polling_active)
+ wcd9xxx_enable_mbhc_txfe(mbhc, true);
+ }
+ break;
+ case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
+ if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
+ wcd9xxx_event_to_micbias(event) &&
+ wcd9xxx_mbhc_polling(mbhc)) {
+ /* if polling is on, restart it */
+ wcd9xxx_pause_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
+ if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
+ wcd9xxx_event_to_micbias(event)) {
+ if (mbhc->event_state &
+ (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
+ wcd9xxx_switch_micbias(mbhc, 1);
+ /*
+ * Disable MBHC TxFE, in case it was enabled earlier
+ * when micbias was enabled and polling is not active.
+ */
+ if (!mbhc->polling_active)
+ wcd9xxx_enable_mbhc_txfe(mbhc, false);
+ }
+ if (mbhc->micbias_enable && mbhc->polling_active &&
+ !(snd_soc_read(mbhc->codec, mbhc->mbhc_bias_regs.ctl_reg)
+ & 0x80)) {
+ pr_debug("%s:Micbias turned off by recording, set up again",
+ __func__);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+ 0x80, 0x80);
+ }
+ break;
+ /* PA usage change */
+ case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
+ set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+ if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
+ /* if micbias is not enabled, switch to vddio */
+ wcd9xxx_switch_micbias(mbhc, 1);
+ break;
+ case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
+ set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+ break;
+ case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
+ clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+ /* if HPH PAs are off, report OCP and switch back to CFILT */
+ clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (mbhc->hph_status & SND_JACK_OC_HPHL)
+ hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ if (!(mbhc->event_state &
+ (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
+ 1 << MBHC_EVENT_PRE_TX_3_ON)))
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
+ clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+ /* if HPH PAs are off, report OCP and switch back to CFILT */
+ clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (mbhc->hph_status & SND_JACK_OC_HPHR)
+ hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ if (!(mbhc->event_state &
+ (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
+ 1 << MBHC_EVENT_PRE_TX_3_ON)))
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ /* Clock usage change */
+ case WCD9XXX_EVENT_PRE_MCLK_ON:
+ break;
+ case WCD9XXX_EVENT_POST_MCLK_ON:
+ /* Change to lower TxAAF frequency */
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+ 1 << 4);
+ /* Re-calibrate clock rate dependent values */
+ wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc)) {
+ wcd9xxx_calibrate_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_PRE_MCLK_OFF:
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc))
+ wcd9xxx_pause_hs_polling(mbhc);
+ break;
+ case WCD9XXX_EVENT_POST_MCLK_OFF:
+ break;
+ case WCD9XXX_EVENT_PRE_RCO_ON:
+ break;
+ case WCD9XXX_EVENT_POST_RCO_ON:
+ /* Change to higher TxAAF frequency */
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+ 0 << 4);
+ /* Re-calibrate clock rate dependent values */
+ wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc)) {
+ wcd9xxx_calibrate_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_PRE_RCO_OFF:
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc))
+ wcd9xxx_pause_hs_polling(mbhc);
+ break;
+ case WCD9XXX_EVENT_POST_RCO_OFF:
+ break;
+ /* CFILT usage change */
+ case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+ case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+ case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+ if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+ wcd9xxx_event_to_cfilt(event))
+ /*
+ * Switch CFILT to slow mode if MBHC CFILT is being
+ * used.
+ */
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
+ break;
+ case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+ if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+ wcd9xxx_event_to_cfilt(event))
+ /*
+ * Switch CFILT to fast mode if MBHC CFILT is not
+ * used anymore.
+ */
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
+ break;
+ /* System resume */
+ case WCD9XXX_EVENT_POST_RESUME:
+ mbhc->mbhc_last_resume = jiffies;
+ break;
+ /* BG mode chage */
+ case WCD9XXX_EVENT_PRE_BG_OFF:
+ case WCD9XXX_EVENT_POST_BG_OFF:
+ case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
+ case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
+ case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
+ case WCD9XXX_EVENT_POST_BG_MBHC_ON:
+ /* Not used for now */
+ break;
+ case WCD9XXX_EVENT_PRE_TX_3_ON:
+ /*
+ * if polling is ON, mbhc micbias not enabled
+ * switch micbias source to VDDIO
+ */
+ set_bit(MBHC_EVENT_PRE_TX_3_ON, &mbhc->event_state);
+ if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg)
+ & 0x80) &&
+ mbhc->polling_active && !mbhc->mbhc_micbias_switched)
+ wcd9xxx_switch_micbias(mbhc, 1);
+ break;
+ case WCD9XXX_EVENT_POST_TX_3_OFF:
+ /*
+ * Switch back to micbias if HPH PA or TX3 path
+ * is disabled
+ */
+ clear_bit(MBHC_EVENT_PRE_TX_3_ON, &mbhc->event_state);
+ if (mbhc->polling_active && mbhc->mbhc_micbias_switched &&
+ !(mbhc->event_state & (1 << MBHC_EVENT_PA_HPHL |
+ 1 << MBHC_EVENT_PA_HPHR)))
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ default:
+ WARN(1, "Unknown event %d\n", event);
+ ret = -EINVAL;
+ }
+ mutex_unlock(&mbhc->mbhc_lock);
+
+ pr_debug("%s: leave\n", __func__);
+
+ return ret;
+}
+
+static s16 wcd9xxx_read_impedance_regs(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ short bias_value;
+ int i;
+ s32 z_t = 0;
+ s32 z_loop = z_det_box_car_avg;
+
+ /* Box Car avrg of less than a particular loop count will not be
+ * accomodated. Similarly if the count is more than a particular number
+ * it will not be counted. Set z_loop counter to a limit, if its more
+ * or less than the value in WCD9XXX_BOX_CAR_AVRG_MAX or
+ * WCD9XXX_BOX_CAR_AVRG_MIN
+ */
+ if (z_loop < WCD9XXX_BOX_CAR_AVRG_MIN) {
+ dev_dbg(codec->dev,
+ "%s: Box Car avrg counter < %d. Limiting it to %d\n",
+ __func__, WCD9XXX_BOX_CAR_AVRG_MIN,
+ WCD9XXX_BOX_CAR_AVRG_MIN);
+ z_loop = WCD9XXX_BOX_CAR_AVRG_MIN;
+ } else if (z_loop > WCD9XXX_BOX_CAR_AVRG_MAX) {
+ dev_dbg(codec->dev,
+ "%s: Box Car avrg counter > %d. Limiting it to %d\n",
+ __func__, WCD9XXX_BOX_CAR_AVRG_MAX,
+ WCD9XXX_BOX_CAR_AVRG_MAX);
+ z_loop = WCD9XXX_BOX_CAR_AVRG_MAX;
+ }
+
+ /* Take box car average if needed */
+ for (i = 0; i < z_loop; i++) {
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+ /* Wait for atleast 1800uS to let register write to settle */
+ usleep_range(1800, 1800 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ z_t += wcd9xxx_read_sta_result(codec);
+ }
+ /* Take average of the Z values read */
+ bias_value = (s16) (z_t / z_loop);
+ return bias_value;
+}
+
+static int wcd9xxx_remeasure_z_values(struct wcd9xxx_mbhc *mbhc,
+ s16 l[3], s16 r[3],
+ uint32_t *zl, uint32_t *zr,
+ u32 *zl_stereo, u32 *zl_mono)
+{
+ s16 l_t[3] = {0}, r_t[3] = {0};
+ s16 l2_stereo, l2_mono;
+ bool left, right;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
+ !mbhc->mbhc_cb->compute_impedance) {
+ dev_err(codec->dev, "%s: Invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ left = !!(l);
+ right = !!(r);
+
+ dev_dbg(codec->dev, "%s: Remeasuring impedance values\n", __func__);
+ dev_dbg(codec->dev, "%s: l: %pK, r: %pK, left=%d, right=%d\n", __func__,
+ l, r, left, right);
+
+ /* Remeasure V2 values */
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0);
+ if (right)
+ r_t[2] = wcd9xxx_read_impedance_regs(mbhc);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0);
+ if (left)
+ l_t[2] = wcd9xxx_read_impedance_regs(mbhc);
+
+ /* Ramp down HPHR */
+ mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_HPHR_RAMP_DISABLE);
+
+ if (right) {
+ /* Take R0'/R1' */
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2,
+ 0xFF, 0xF8);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0xFF, 0xA0);
+ r_t[1] = wcd9xxx_read_impedance_regs(mbhc);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2,
+ 0xFF, 0xF0);
+ r_t[0] = wcd9xxx_read_impedance_regs(mbhc);
+ }
+
+ /* Put back gain to 1x */
+ if (!left && right)
+ mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_GAIN_0);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0);
+ /* Take L2'' measurement */
+ l2_stereo = wcd9xxx_read_impedance_regs(mbhc);
+
+ /* Turn off HPHR PA and take L2''' */
+ mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_HPHR_PA_DISABLE);
+ l2_mono = wcd9xxx_read_impedance_regs(mbhc);
+
+ /* Ramp HPHL from -15mV to 0V */
+ mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_HPHL_RAMP_DISABLE);
+
+ /* Take L0' and L1' with iCal */
+ l_t[0] = wcd9xxx_read_impedance_regs(mbhc);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8);
+ l_t[1] = wcd9xxx_read_impedance_regs(mbhc);
+
+ if (left) {
+ l[0] = l_t[0];
+ l[1] = l_t[1];
+ l[2] = l_t[2];
+ }
+ if (right) {
+ r[0] = r_t[0];
+ r[1] = r_t[1];
+ r[2] = r_t[2];
+ }
+
+ /* compute the new impedance values */
+ mbhc->mbhc_cb->compute_impedance(mbhc, l, r, zl, zr);
+
+ if (!left && right)
+ mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_GAIN_UPDATE_1X);
+ /* compute the new ZL'' value */
+ l_t[2] = l2_stereo;
+ mbhc->mbhc_cb->compute_impedance(mbhc, l_t, NULL, zl_stereo, NULL);
+ /* compute the new ZL''' value */
+ l_t[2] = l2_mono;
+ mbhc->mbhc_cb->compute_impedance(mbhc, l_t, NULL, zl_mono, NULL);
+
+ pr_debug("%s: L0': 0x%x, L1': 0x%x L2_stereo: 0x%x, L2_mono: 0x%x\n",
+ __func__, l_t[0] & 0xffff, l_t[1] & 0xffff,
+ l2_stereo & 0xffff, l2_mono & 0xffff);
+ pr_debug("%s: ZL_stereo = %u, ZL_mono = %u\n",
+ __func__, *zl_stereo, *zl_mono);
+
+ return 0;
+}
+
+static enum mbhc_zdet_zones wcd9xxx_assign_zdet_zone(uint32_t zl, uint32_t zr,
+ int32_t *gain)
+{
+ enum mbhc_zdet_zones zdet_zone;
+
+ if (WCD9XXX_IS_IN_ZDET_ZONE_1(zl) &&
+ WCD9XXX_IS_IN_ZDET_ZONE_1(zr)) {
+ zdet_zone = ZL_ZONE1__ZR_ZONE1;
+ *gain = 0;
+ } else if (WCD9XXX_IS_IN_ZDET_ZONE_2(zl) &&
+ WCD9XXX_IS_IN_ZDET_ZONE_2(zr)) {
+ zdet_zone = ZL_ZONE2__ZR_ZONE2;
+ *gain = MBHC_ZDET_GAIN_1;
+ } else if (WCD9XXX_IS_IN_ZDET_ZONE_3(zl) &&
+ WCD9XXX_IS_IN_ZDET_ZONE_3(zr)) {
+ zdet_zone = ZL_ZONE3__ZR_ZONE3;
+ *gain = MBHC_ZDET_GAIN_2;
+ } else if (WCD9XXX_IS_IN_ZDET_ZONE_2(zl) &&
+ WCD9XXX_IS_IN_ZDET_ZONE_1(zr)) {
+ zdet_zone = ZL_ZONE2__ZR_ZONE1;
+ *gain = MBHC_ZDET_GAIN_1;
+ } else if (WCD9XXX_IS_IN_ZDET_ZONE_3(zl) &&
+ WCD9XXX_IS_IN_ZDET_ZONE_1(zr)) {
+ zdet_zone = ZL_ZONE3__ZR_ZONE1;
+ *gain = MBHC_ZDET_GAIN_2;
+ } else if (WCD9XXX_IS_IN_ZDET_ZONE_1(zl) &&
+ WCD9XXX_IS_IN_ZDET_ZONE_2(zr)) {
+ zdet_zone = ZL_ZONE1__ZR_ZONE2;
+ *gain = MBHC_ZDET_GAIN_1;
+ } else if (WCD9XXX_IS_IN_ZDET_ZONE_1(zl) &&
+ WCD9XXX_IS_IN_ZDET_ZONE_3(zr)) {
+ zdet_zone = ZL_ZONE1__ZR_ZONE3;
+ *gain = MBHC_ZDET_GAIN_2;
+ } else {
+ zdet_zone = ZL_ZR_NOT_IN_ZONE1;
+ *gain = MBHC_ZDET_GAIN_1;
+ }
+
+ return zdet_zone;
+}
+
+static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr)
+{
+ int i;
+ int ret = 0;
+ u8 micb_mbhc_val;
+ s16 l[3], r[3];
+ s16 *z[] = {
+ &l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
+ };
+ u32 zl_stereo, zl_mono;
+ u32 zl_diff_1, zl_diff_2;
+ bool override_en;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const int mux_wait_us = 25;
+ const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
+ /* Phase 1 */
+ /* Set MBHC_MUX for HPHL without ical */
+ {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
+ /* Set MBHC_MUX for HPHR without ical */
+ {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
+ /* Set MBHC_MUX for HPHR with ical */
+ {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
+ /* Set MBHC_MUX for HPHL with ical */
+ {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
+
+ /* Phase 2 */
+ {WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
+ /* Set MBHC_MUX for HPHR without ical and wait for 25us */
+ {WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
+ };
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
+ !mbhc->mbhc_cb->compute_impedance || !zl || !zr) {
+ return -EINVAL;
+ }
+
+ /*
+ * Impedance detection is an intrusive function as it mutes RX paths,
+ * enable PAs and etc. Therefore codec drvier including ALSA
+ * shouldn't read and write hardware registers during detection.
+ */
+ wcd9xxx_onoff_ext_mclk(mbhc, true);
+
+ /*
+ * For impedance detection, make sure to disable micbias from
+ * override signal so that override does not cause micbias
+ * to be enabled. This setting will be undone after completing
+ * impedance measurement.
+ */
+ micb_mbhc_val = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
+ snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL,
+ 0x10, 0x00);
+
+ override_en = (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04) ?
+ true : false;
+ if (!override_en)
+ wcd9xxx_turn_onoff_override(mbhc, true);
+ pr_debug("%s: Setting impedance detection\n", __func__);
+
+ /* Codec specific setup for L0, R0, L1 and R1 measurements */
+ mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_PRE_MEASURE);
+
+ pr_debug("%s: Performing impedance detection\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
+ snd_soc_update_bits(codec, reg_set_mux[i].reg,
+ reg_set_mux[i].mask,
+ reg_set_mux[i].val);
+ if (mbhc->mbhc_cb->get_cdc_type &&
+ mbhc->mbhc_cb->get_cdc_type() ==
+ WCD9XXX_CDC_TYPE_TOMTOM) {
+ *(z[i]) = wcd9xxx_read_impedance_regs(mbhc);
+ } else {
+ if (mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
+ /* 25us is required after mux change to settle down */
+ usleep_range(mux_wait_us,
+ mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0,
+ true, false);
+ }
+ }
+
+ /* Codec specific setup for L2 and R2 measurements */
+ mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_POST_MEASURE);
+
+ for (; i < ARRAY_SIZE(reg_set_mux); i++) {
+ snd_soc_update_bits(codec, reg_set_mux[i].reg,
+ reg_set_mux[i].mask,
+ reg_set_mux[i].val);
+ if (mbhc->mbhc_cb->get_cdc_type &&
+ mbhc->mbhc_cb->get_cdc_type() ==
+ WCD9XXX_CDC_TYPE_TOMTOM) {
+ *(z[i]) = wcd9xxx_read_impedance_regs(mbhc);
+ } else {
+ if (mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
+ /* 25us is required after mux change to settle down */
+ usleep_range(mux_wait_us,
+ mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ *(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0,
+ true, false);
+ }
+ }
+
+ mbhc->mbhc_cb->compute_impedance(mbhc, l, r, zl, zr);
+
+ /*
+ * For some codecs, an additional step of zdet is needed
+ * to overcome effects of noise and for better accuracy of
+ * z values
+ */
+ if (mbhc->mbhc_cb->get_cdc_type &&
+ mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_TOMTOM) {
+ uint32_t zl_t = 0, zr_t = 0;
+ s16 *l_p, *r_p;
+ enum mbhc_zdet_zones zdet_zone;
+ int32_t gain;
+
+ zdet_zone = wcd9xxx_assign_zdet_zone(*zl, *zr, &gain);
+ switch (zdet_zone) {
+ case ZL_ZONE1__ZR_ZONE1:
+ l_p = NULL;
+ r_p = NULL;
+ break;
+ case ZL_ZONE2__ZR_ZONE2:
+ case ZL_ZONE3__ZR_ZONE3:
+ case ZL_ZR_NOT_IN_ZONE1:
+ l_p = l;
+ r_p = r;
+ break;
+ case ZL_ZONE2__ZR_ZONE1:
+ case ZL_ZONE3__ZR_ZONE1:
+ /* If ZR falls in Zone 1, further computations with
+ * gain update are not required
+ */
+ l_p = l;
+ r_p = NULL;
+ break;
+ case ZL_ZONE1__ZR_ZONE2:
+ case ZL_ZONE1__ZR_ZONE3:
+ /* If ZL falls in Zone 1, further computations with
+ * gain update are not required
+ */
+ l_p = NULL;
+ r_p = r;
+ break;
+ }
+ pr_debug("%s:zdet_zone = %d, gain = %d\n", __func__,
+ zdet_zone, gain);
+ if (gain)
+ mbhc->mbhc_cb->setup_zdet(mbhc, gain);
+
+ wcd9xxx_remeasure_z_values(mbhc, l_p, r_p, &zl_t, &zr_t,
+ &zl_stereo, &zl_mono);
+
+ *zl = (zl_t) ? zl_t : *zl;
+ *zr = (zr_t) ? zr_t : *zr;
+
+ /* Check for Mono/Stereo Type
+ * Conditions to classify Mono/Stereo
+ * i. Difference of zl_stereo and zl_mono > (1/2) of zl_mono
+ * ii. Absolute difference of zl and zr above a threshold
+ */
+ zl_diff_1 = (zl_mono > zl_stereo) ? (zl_mono - zl_stereo) :
+ (zl_stereo - zl_mono);
+ zl_diff_2 = (*zl > *zr) ? (*zl - *zr) : (*zr - *zl);
+
+ mbhc->hph_type = MBHC_HPH_NONE;
+ if (mbhc->current_plug != PLUG_TYPE_HIGH_HPH) {
+ if ((zl_diff_1 > (zl_mono >> 1)) ||
+ (zl_diff_2 > WCD9XXX_MONO_HS_DIFF_THR) ||
+ ((*zl < WCD9XXX_MONO_HS_MIN_THR) &&
+ (*zr > WCD9XXX_MONO_HS_MIN_THR)) ||
+ ((*zr < WCD9XXX_MONO_HS_MIN_THR) &&
+ (*zl > WCD9XXX_MONO_HS_MIN_THR))) {
+ pr_debug("%s: MONO plug type detected\n",
+ __func__);
+ mbhc->hph_type = MBHC_HPH_MONO;
+ *zl = zl_mono;
+ } else {
+ pr_debug("%s: STEREO plug type detected\n",
+ __func__);
+ mbhc->hph_type = MBHC_HPH_STEREO;
+ }
+ }
+ }
+
+ mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_PA_DISABLE);
+
+ /* Calculate z values based on the Q-fuse registers, if used */
+ if (mbhc->mbhc_cb->zdet_error_approx)
+ mbhc->mbhc_cb->zdet_error_approx(mbhc, zl, zr);
+
+ wcd9xxx_onoff_ext_mclk(mbhc, false);
+
+ if (!override_en)
+ wcd9xxx_turn_onoff_override(mbhc, false);
+
+ /* Undo the micbias disable for override */
+ snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, micb_mbhc_val);
+
+ pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
+ __func__,
+ l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
+ pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
+ __func__,
+ r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
+ pr_debug("%s: RL %u milliohm, RR %u milliohm\n", __func__, *zl, *zr);
+ pr_debug("%s: Impedance detection completed\n", __func__);
+
+ return ret;
+}
+
+int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr)
+{
+ *zl = mbhc->zl;
+ *zr = mbhc->zr;
+
+ if (*zl && *zr)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+/*
+ * wcd9xxx_mbhc_init : initialize MBHC internal structures.
+ *
+ * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
+ */
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec,
+ int (*micbias_enable_cb) (struct snd_soc_codec*, bool,
+ enum wcd9xxx_micbias_num),
+ const struct wcd9xxx_mbhc_cb *mbhc_cb,
+ const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
+ int rco_clk_rate,
+ bool impedance_det_en)
+{
+ int ret;
+ void *core_res;
+
+ pr_debug("%s: enter\n", __func__);
+ memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
+ memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
+
+ mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
+ mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
+ mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
+ mbhc->mbhc_micbias_switched = false;
+ mbhc->polling_active = false;
+ mbhc->mbhc_state = MBHC_STATE_NONE;
+ mbhc->in_swch_irq_handler = false;
+ mbhc->current_plug = PLUG_TYPE_NONE;
+ mbhc->lpi_enabled = false;
+ mbhc->no_mic_headset_override = false;
+ mbhc->mbhc_last_resume = 0;
+ mbhc->codec = codec;
+ mbhc->resmgr = resmgr;
+ mbhc->resmgr->mbhc = mbhc;
+ mbhc->micbias_enable_cb = micbias_enable_cb;
+ mbhc->rco_clk_rate = rco_clk_rate;
+ mbhc->mbhc_cb = mbhc_cb;
+ mbhc->intr_ids = mbhc_cdc_intr_ids;
+ mbhc->impedance_detect = impedance_det_en;
+ mbhc->hph_type = MBHC_HPH_NONE;
+
+ if (mbhc->intr_ids == NULL) {
+ pr_err("%s: Interrupt mapping not provided\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mbhc->headset_jack.jack == NULL) {
+ ret = snd_soc_card_jack_new(codec->component.card,
+ "Headset Jack", WCD9XXX_JACK_MASK,
+ &mbhc->headset_jack, NULL, 0);
+ if (ret) {
+ pr_err("%s: Failed to create new jack\n", __func__);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new(codec->component.card,
+ "Button Jack",
+ WCD9XXX_JACK_BUTTON_MASK,
+ &mbhc->button_jack, NULL, 0);
+ if (ret) {
+ pr_err("Failed to create new jack\n");
+ return ret;
+ }
+
+ ret = snd_jack_set_key(mbhc->button_jack.jack,
+ SND_JACK_BTN_0,
+ KEY_MEDIA);
+ if (ret) {
+ pr_err("%s: Failed to set code for btn-0\n",
+ __func__);
+ return ret;
+ }
+
+ set_bit(INPUT_PROP_NO_DUMMY_RELEASE,
+ mbhc->button_jack.jack->input_dev->propbit);
+
+ INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
+ wcd9xxx_mbhc_fw_read);
+ INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
+ INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
+ wcd9xxx_mbhc_insert_work);
+ }
+
+ mutex_init(&mbhc->mbhc_lock);
+
+ /* Register event notifier */
+ mbhc->nblock.notifier_call = wcd9xxx_event_notify;
+ ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
+ if (ret) {
+ pr_err("%s: Failed to register notifier %d\n", __func__, ret);
+ mutex_destroy(&mbhc->mbhc_lock);
+ return ret;
+ }
+
+ wcd9xxx_init_debugfs(mbhc);
+
+ /* Disable Impedance detection by default for certain codec types */
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->get_cdc_type &&
+ (mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON))
+ impedance_detect_en = 0;
+ else
+ impedance_detect_en = impedance_det_en ? 1 : 0;
+
+ core_res = mbhc->resmgr->core_res;
+ ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion,
+ wcd9xxx_hs_insert_irq,
+ "Headset insert detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
+ mbhc->intr_ids->insertion, ret);
+ goto err_insert_irq;
+ }
+ wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion);
+
+ ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem,
+ wcd9xxx_hs_remove_irq,
+ "Headset remove detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ mbhc->intr_ids->poll_plug_rem);
+ goto err_remove_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete,
+ wcd9xxx_dce_handler, "DC Estimation detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ mbhc->intr_ids->dce_est_complete);
+ goto err_potential_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release,
+ wcd9xxx_release_handler,
+ "Button Release detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ mbhc->intr_ids->button_release);
+ goto err_release_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp,
+ wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ mbhc->intr_ids->hph_left_ocp);
+ goto err_hphl_ocp_irq;
+ }
+ wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp);
+
+ ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp,
+ wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ mbhc->intr_ids->hph_right_ocp);
+ goto err_hphr_ocp_irq;
+ }
+ wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp);
+
+ wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
+ 1 << WCD9XXX_COND_HPH);
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+
+err_hphr_ocp_irq:
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
+err_hphl_ocp_irq:
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
+err_release_irq:
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
+err_potential_irq:
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
+err_remove_irq:
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
+err_insert_irq:
+ wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+ mutex_destroy(&mbhc->mbhc_lock);
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_mbhc_init);
+
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
+{
+ struct wcd9xxx_core_resource *core_res =
+ mbhc->resmgr->core_res;
+
+ wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
+ 1 << WCD9XXX_COND_HPH);
+
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc);
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
+ wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
+
+ mutex_destroy(&mbhc->mbhc_lock);
+ wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+ wcd9xxx_cleanup_debugfs(mbhc);
+}
+EXPORT_SYMBOL(wcd9xxx_mbhc_deinit);
+
+MODULE_DESCRIPTION("wcd9xxx MBHC module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
new file mode 100644
index 000000000000..47fe64965654
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -0,0 +1,492 @@
+/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_MBHC_H__
+#define __WCD9XXX_MBHC_H__
+
+#include "wcd9xxx-resmgr.h"
+#include "wcdcal-hwdep.h"
+
+#define WCD9XXX_CFILT_FAST_MODE 0x00
+#define WCD9XXX_CFILT_SLOW_MODE 0x40
+#define WCD9XXX_CFILT_EXT_PRCHG_EN 0x30
+#define WCD9XXX_CFILT_EXT_PRCHG_DSBL 0x00
+
+#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
+
+struct mbhc_micbias_regs {
+ u16 cfilt_val;
+ u16 cfilt_ctl;
+ u16 mbhc_reg;
+ u16 int_rbias;
+ u16 ctl_reg;
+ u8 cfilt_sel;
+};
+
+enum mbhc_v_index {
+ MBHC_V_IDX_CFILT,
+ MBHC_V_IDX_VDDIO,
+ MBHC_V_IDX_NUM,
+};
+
+enum mbhc_cal_type {
+ MBHC_CAL_MCLK,
+ MBHC_CAL_RCO,
+ MBHC_CAL_NUM,
+};
+
+enum mbhc_impedance_detect_stages {
+ MBHC_ZDET_PRE_MEASURE,
+ MBHC_ZDET_POST_MEASURE,
+ MBHC_ZDET_GAIN_0,
+ MBHC_ZDET_GAIN_1,
+ MBHC_ZDET_GAIN_2,
+ MBHC_ZDET_HPHR_RAMP_DISABLE,
+ MBHC_ZDET_HPHL_RAMP_DISABLE,
+ MBHC_ZDET_RAMP_DISABLE,
+ MBHC_ZDET_HPHR_PA_DISABLE,
+ MBHC_ZDET_PA_DISABLE,
+ MBHC_ZDET_GAIN_UPDATE_1X,
+};
+
+/* Zone assignments used in WCD9330 for Zdet */
+enum mbhc_zdet_zones {
+ ZL_ZONE1__ZR_ZONE1,
+ ZL_ZONE2__ZR_ZONE2,
+ ZL_ZONE3__ZR_ZONE3,
+ ZL_ZONE2__ZR_ZONE1,
+ ZL_ZONE3__ZR_ZONE1,
+ ZL_ZONE1__ZR_ZONE2,
+ ZL_ZONE1__ZR_ZONE3,
+ ZL_ZR_NOT_IN_ZONE1,
+};
+
+/* Data used by MBHC */
+struct mbhc_internal_cal_data {
+ u16 dce_z;
+ u16 dce_nsc_cs_z;
+ u16 dce_mb;
+ u16 sta_z;
+ u16 sta_mb;
+ u32 t_sta_dce;
+ u32 t_dce;
+ u32 t_sta;
+ u32 micb_mv;
+ u16 v_ins_hu[MBHC_V_IDX_NUM];
+ u16 v_ins_h[MBHC_V_IDX_NUM];
+ u16 v_b1_hu[MBHC_V_IDX_NUM];
+ u16 v_b1_h[MBHC_V_IDX_NUM];
+ u16 v_brh[MBHC_V_IDX_NUM];
+ u16 v_brl;
+ u16 v_no_mic;
+ s16 v_inval_ins_low;
+ s16 v_inval_ins_high;
+ u16 v_cs_ins_h;
+};
+
+enum wcd9xxx_mbhc_plug_type {
+ PLUG_TYPE_INVALID = -1,
+ PLUG_TYPE_NONE,
+ PLUG_TYPE_HEADSET,
+ PLUG_TYPE_HEADPHONE,
+ PLUG_TYPE_HIGH_HPH,
+ PLUG_TYPE_GND_MIC_SWAP,
+ PLUG_TYPE_ANC_HEADPHONE,
+};
+
+enum wcd9xxx_mbhc_micbias_type {
+ MBHC_PRIMARY_MIC_MB,
+ MBHC_ANC_MIC_MB,
+};
+
+enum wcd9xxx_micbias_num {
+ MBHC_MICBIAS_INVALID = -1,
+ MBHC_MICBIAS1,
+ MBHC_MICBIAS2,
+ MBHC_MICBIAS3,
+ MBHC_MICBIAS4,
+};
+
+enum hw_jack_type {
+ FOUR_POLE_JACK = 0,
+ FIVE_POLE_JACK,
+ SIX_POLE_JACK,
+};
+
+enum wcd9xx_mbhc_micbias_enable_bits {
+ MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET,
+ MBHC_MICBIAS_ENABLE_REGULAR_HEADSET,
+};
+
+enum wcd9xx_mbhc_cs_enable_bits {
+ MBHC_CS_ENABLE_POLLING,
+ MBHC_CS_ENABLE_INSERTION,
+ MBHC_CS_ENABLE_REMOVAL,
+ MBHC_CS_ENABLE_DET_ANC,
+};
+
+enum wcd9xxx_mbhc_state {
+ MBHC_STATE_NONE = -1,
+ MBHC_STATE_POTENTIAL,
+ MBHC_STATE_POTENTIAL_RECOVERY,
+ MBHC_STATE_RELEASE,
+};
+
+enum wcd9xxx_mbhc_btn_det_mem {
+ MBHC_BTN_DET_V_BTN_LOW,
+ MBHC_BTN_DET_V_BTN_HIGH,
+ MBHC_BTN_DET_N_READY,
+ MBHC_BTN_DET_N_CIC,
+ MBHC_BTN_DET_GAIN
+};
+
+enum wcd9xxx_mbhc_clk_freq {
+ TAIKO_MCLK_12P2MHZ = 0,
+ TAIKO_MCLK_9P6MHZ,
+ TAIKO_NUM_CLK_FREQS,
+};
+
+enum wcd9xxx_mbhc_event_state {
+ MBHC_EVENT_PA_HPHL,
+ MBHC_EVENT_PA_HPHR,
+ MBHC_EVENT_PRE_TX_3_ON,
+ MBHC_EVENT_POST_TX_3_OFF,
+};
+
+enum mbhc_hph_type {
+ MBHC_HPH_NONE = 0,
+ MBHC_HPH_MONO,
+ MBHC_HPH_STEREO,
+};
+
+struct wcd9xxx_mbhc_general_cfg {
+ u8 t_ldoh;
+ u8 t_bg_fast_settle;
+ u8 t_shutdown_plug_rem;
+ u8 mbhc_nsa;
+ u8 mbhc_navg;
+ u8 v_micbias_l;
+ u8 v_micbias;
+ u8 mbhc_reserved;
+ u16 settle_wait;
+ u16 t_micbias_rampup;
+ u16 t_micbias_rampdown;
+ u16 t_supply_bringup;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_detect_cfg {
+ u32 mic_current;
+ u32 hph_current;
+ u16 t_mic_pid;
+ u16 t_ins_complete;
+ u16 t_ins_retry;
+ u16 v_removal_delta;
+ u8 micbias_slow_ramp;
+ u8 reserved0;
+ u8 reserved1;
+ u8 reserved2;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_type_cfg {
+ u8 av_detect;
+ u8 mono_detect;
+ u8 num_ins_tries;
+ u8 reserved0;
+ s16 v_no_mic;
+ s16 v_av_min;
+ s16 v_av_max;
+ s16 v_hs_min;
+ s16 v_hs_max;
+ u16 reserved1;
+} __packed;
+
+struct wcd9xxx_mbhc_btn_detect_cfg {
+ s8 c[8];
+ u8 nc;
+ u8 n_meas;
+ u8 mbhc_nsc;
+ u8 n_btn_meas;
+ u8 n_btn_con;
+ u8 num_btn;
+ u8 reserved0;
+ u8 reserved1;
+ u16 t_poll;
+ u16 t_bounce_wait;
+ u16 t_rel_timeout;
+ s16 v_btn_press_delta_sta;
+ s16 v_btn_press_delta_cic;
+ u16 t_btn0_timeout;
+ s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
+ s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
+ u8 _n_ready[TAIKO_NUM_CLK_FREQS];
+ u8 _n_cic[TAIKO_NUM_CLK_FREQS];
+ u8 _gain[TAIKO_NUM_CLK_FREQS];
+} __packed;
+
+struct wcd9xxx_mbhc_imped_detect_cfg {
+ u8 _hs_imped_detect;
+ u8 _n_rload;
+ u8 _hph_keep_on;
+ u8 _repeat_rload_calc;
+ u16 _t_dac_ramp_time;
+ u16 _rhph_high;
+ u16 _rhph_low;
+ u16 _rload[0]; /* rload[n_rload] */
+ u16 _alpha[0]; /* alpha[n_rload] */
+ u16 _beta[3];
+} __packed;
+
+struct wcd9xxx_mbhc_config {
+ bool read_fw_bin;
+ /*
+ * void* calibration contains:
+ * struct wcd9xxx_mbhc_general_cfg generic;
+ * struct wcd9xxx_mbhc_plug_detect_cfg plug_det;
+ * struct wcd9xxx_mbhc_plug_type_cfg plug_type;
+ * struct wcd9xxx_mbhc_btn_detect_cfg btn_det;
+ * struct wcd9xxx_mbhc_imped_detect_cfg imped_det;
+ * Note: various size depends on btn_det->num_btn
+ */
+ void *calibration;
+ enum wcd9xxx_micbias_num micbias;
+ enum wcd9xxx_micbias_num anc_micbias;
+ int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
+ unsigned int mclk_rate;
+ unsigned int gpio;
+ unsigned int gpio_irq;
+ int gpio_level_insert;
+ bool insert_detect; /* codec has own MBHC_INSERT_DETECT */
+ bool detect_extn_cable;
+ /* bit mask of enum wcd9xx_mbhc_micbias_enable_bits */
+ unsigned long micbias_enable_flags;
+ /* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
+ bool (*swap_gnd_mic) (struct snd_soc_codec *);
+ unsigned long cs_enable_flags;
+ bool use_int_rbias;
+ bool do_recalibration;
+ bool use_vddio_meas;
+ bool enable_anc_mic_detect;
+ enum hw_jack_type hw_jack_type;
+ int key_code[8];
+};
+
+struct wcd9xxx_cfilt_mode {
+ u8 reg_mode_val;
+ u8 cur_mode_val;
+ u8 reg_mask;
+};
+
+struct wcd9xxx_mbhc_intr {
+ int poll_plug_rem;
+ int shortavg_complete;
+ int potential_button_press;
+ int button_release;
+ int dce_est_complete;
+ int insertion;
+ int hph_left_ocp;
+ int hph_right_ocp;
+ int hs_jack_switch;
+};
+
+struct wcd9xxx_mbhc_cb {
+ void (*enable_mux_bias_block) (struct snd_soc_codec *);
+ void (*cfilt_fast_mode) (struct snd_soc_codec *, struct wcd9xxx_mbhc *);
+ void (*codec_specific_cal) (struct snd_soc_codec *,
+ struct wcd9xxx_mbhc *);
+ struct wcd9xxx_cfilt_mode (*switch_cfilt_mode) (struct wcd9xxx_mbhc *,
+ bool);
+ void (*select_cfilt) (struct snd_soc_codec *, struct wcd9xxx_mbhc *);
+ enum wcd9xxx_cdc_type (*get_cdc_type) (void);
+ void (*enable_clock_gate) (struct snd_soc_codec *, bool);
+ int (*setup_zdet) (struct wcd9xxx_mbhc *,
+ enum mbhc_impedance_detect_stages stage);
+ void (*compute_impedance) (struct wcd9xxx_mbhc *, s16 *, s16 *,
+ uint32_t *, uint32_t *);
+ void (*zdet_error_approx) (struct wcd9xxx_mbhc *, uint32_t *,
+ uint32_t *);
+ void (*enable_mbhc_txfe) (struct snd_soc_codec *, bool);
+ int (*enable_mb_source) (struct snd_soc_codec *, bool, bool);
+ void (*setup_int_rbias) (struct snd_soc_codec *, bool);
+ void (*pull_mb_to_vddio) (struct snd_soc_codec *, bool);
+ bool (*insert_rem_status) (struct snd_soc_codec *);
+ void (*micbias_pulldown_ctrl) (struct wcd9xxx_mbhc *, bool);
+ int (*codec_rco_ctrl) (struct snd_soc_codec *, bool);
+ void (*hph_auto_pulldown_ctrl) (struct snd_soc_codec *, bool);
+ struct firmware_cal * (*get_hwdep_fw_cal) (struct snd_soc_codec *,
+ enum wcd_cal_type);
+};
+
+struct wcd9xxx_mbhc {
+ bool polling_active;
+ /* Delayed work to report long button press */
+ struct delayed_work mbhc_btn_dwork;
+ int buttons_pressed;
+ enum wcd9xxx_mbhc_state mbhc_state;
+ struct wcd9xxx_mbhc_config *mbhc_cfg;
+ const struct wcd9xxx_mbhc_cb *mbhc_cb;
+
+ struct mbhc_internal_cal_data mbhc_data;
+
+ struct mbhc_micbias_regs mbhc_bias_regs;
+ struct mbhc_micbias_regs mbhc_anc_bias_regs;
+
+ bool mbhc_micbias_switched;
+
+ u32 hph_status; /* track headhpone status */
+ u8 hphlocp_cnt; /* headphone left ocp retry */
+ u8 hphrocp_cnt; /* headphone right ocp retry */
+
+ /* Work to perform MBHC Firmware Read */
+ struct delayed_work mbhc_firmware_dwork;
+ const struct firmware *mbhc_fw;
+ struct firmware_cal *mbhc_cal;
+
+ struct delayed_work mbhc_insert_dwork;
+
+ u8 current_plug;
+ struct work_struct correct_plug_swch;
+ /*
+ * Work to perform polling on microphone voltage
+ * in order to correct plug type once plug type
+ * is detected as headphone
+ */
+ struct work_struct correct_plug_noswch;
+ bool hs_detect_work_stop;
+
+ bool lpi_enabled; /* low power insertion detection */
+ bool in_swch_irq_handler;
+
+ struct wcd9xxx_resmgr *resmgr;
+ struct snd_soc_codec *codec;
+
+ bool no_mic_headset_override;
+
+ /* track PA/DAC state to sync with userspace */
+ unsigned long hph_pa_dac_state;
+ /*
+ * save codec's state with resmgr event notification
+ * bit flags of enum wcd9xxx_mbhc_event_state
+ */
+ unsigned long event_state;
+
+ unsigned long mbhc_last_resume; /* in jiffies */
+
+ bool insert_detect_level_insert;
+
+ struct snd_soc_jack headset_jack;
+ struct snd_soc_jack button_jack;
+
+ struct notifier_block nblock;
+
+ bool micbias_enable;
+ int (*micbias_enable_cb) (struct snd_soc_codec*, bool,
+ enum wcd9xxx_micbias_num);
+
+ bool impedance_detect;
+ /* impedance of hphl and hphr */
+ uint32_t zl, zr;
+
+ u32 rco_clk_rate;
+
+ bool update_z;
+
+ u8 scaling_mux_in;
+ /* Holds codec specific interrupt mapping */
+ const struct wcd9xxx_mbhc_intr *intr_ids;
+
+ /* Indicates status of current source switch */
+ bool is_cs_enabled;
+
+ /* Holds type of Headset - Mono/Stereo */
+ enum mbhc_hph_type hph_type;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_poke;
+ struct dentry *debugfs_mbhc;
+#endif
+
+ struct mutex mbhc_lock;
+};
+
+#define WCD9XXX_MBHC_CAL_SIZE(buttons, rload) ( \
+ sizeof(enum wcd9xxx_micbias_num) + \
+ sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+ ((sizeof(s16) + sizeof(s16)) * buttons) + \
+ sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ ((sizeof(u16) + sizeof(u16)) * rload) \
+ )
+
+#define WCD9XXX_MBHC_CAL_GENERAL_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_general_cfg *) cali)
+#define WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_plug_detect_cfg *) \
+ &(WCD9XXX_MBHC_CAL_GENERAL_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_plug_type_cfg *) \
+ &(WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_btn_detect_cfg *) \
+ &(WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_IMPED_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_imped_detect_cfg *) \
+ (((void *)&WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
+ (WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
+ (sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
+ sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
+ )
+
+/* minimum size of calibration data assuming there is only one button and
+ * one rload.
+ */
+#define WCD9XXX_MBHC_CAL_MIN_SIZE ( \
+ sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ (sizeof(u16) * 2) \
+ )
+
+#define WCD9XXX_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
+ sizeof(cfg_ptr->_v_btn_high[0]))))
+
+#define WCD9XXX_MBHC_CAL_IMPED_MIN_SZ ( \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + sizeof(u16) * 2)
+
+#define WCD9XXX_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ (cfg_ptr->_n_rload * \
+ (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0]))))
+
+int wcd9xxx_mbhc_set_keycode(struct wcd9xxx_mbhc *mbhc);
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+ struct wcd9xxx_mbhc_config *mbhc_cfg);
+void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc);
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec,
+ int (*micbias_enable_cb) (struct snd_soc_codec*, bool,
+ enum wcd9xxx_micbias_num),
+ const struct wcd9xxx_mbhc_cb *mbhc_cb,
+ const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
+ int rco_clk_rate,
+ bool impedance_det_en);
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+ const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+ const enum wcd9xxx_mbhc_btn_det_mem mem);
+int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr);
+#endif /* __WCD9XXX_MBHC_H__ */
diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.c b/sound/soc/codecs/wcd9xxx-resmgr-v2.c
new file mode 100644
index 000000000000..f16fc05a5eaa
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr-v2.c
@@ -0,0 +1,682 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9335/registers.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <sound/soc.h>
+#include "wcd9xxx-resmgr-v2.h"
+
+#define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 5000
+#define WCD93XX_ANA_BIAS 0x0601
+#define WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41
+#define WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42
+
+
+static const char *wcd_resmgr_clk_type_to_str(enum wcd_clock_type clk_type)
+{
+ if (clk_type == WCD_CLK_OFF)
+ return "WCD_CLK_OFF";
+ else if (clk_type == WCD_CLK_RCO)
+ return "WCD_CLK_RCO";
+ else if (clk_type == WCD_CLK_MCLK)
+ return "WCD_CLK_MCLK";
+ else
+ return "WCD_CLK_UNDEFINED";
+}
+
+static int wcd_resmgr_codec_reg_update_bits(struct wcd9xxx_resmgr_v2 *resmgr,
+ u16 reg, u8 mask, u8 val)
+{
+ bool change;
+ int ret;
+
+ if (resmgr->codec_type == WCD934X) {
+ /* Tavil does not support ANA_CLK_TOP register */
+ if (reg == WCD9335_ANA_CLK_TOP)
+ return 0;
+ } else {
+ /* Tasha does not support CLK_SYS_MCLK_PRG register */
+ if (reg == WCD934X_CLK_SYS_MCLK_PRG)
+ return 0;
+ }
+ if (resmgr->codec) {
+ ret = snd_soc_update_bits(resmgr->codec, reg, mask, val);
+ } else if (resmgr->core_res->wcd_core_regmap) {
+ ret = regmap_update_bits_check(
+ resmgr->core_res->wcd_core_regmap,
+ reg, mask, val, &change);
+ if (!ret)
+ ret = change;
+ } else {
+ pr_err("%s: codec/regmap not defined\n", __func__);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int wcd_resmgr_codec_reg_read(struct wcd9xxx_resmgr_v2 *resmgr,
+ unsigned int reg)
+{
+ int val, ret;
+
+ if (resmgr->codec_type == WCD934X) {
+ if (reg == WCD9335_ANA_CLK_TOP)
+ return 0;
+ } else {
+ if (reg == WCD934X_CLK_SYS_MCLK_PRG)
+ return 0;
+ }
+ if (resmgr->codec) {
+ val = snd_soc_read(resmgr->codec, reg);
+ } else if (resmgr->core_res->wcd_core_regmap) {
+ ret = regmap_read(resmgr->core_res->wcd_core_regmap,
+ reg, &val);
+ if (ret)
+ val = ret;
+ } else {
+ pr_err("%s: wcd regmap is null\n", __func__);
+ return -EINVAL;
+ }
+
+ return val;
+}
+
+/*
+ * wcd_resmgr_get_clk_type()
+ * Returns clk type that is currently enabled
+ */
+int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+ if (!resmgr) {
+ pr_err("%s: resmgr not initialized\n", __func__);
+ return -EINVAL;
+ }
+ return resmgr->clk_type;
+}
+
+static void wcd_resmgr_cdc_specific_get_clk(struct wcd9xxx_resmgr_v2 *resmgr,
+ int clk_users)
+{
+ /* Caller of this function should have acquired BG_CLK lock */
+ if (clk_users) {
+ if (resmgr->resmgr_cb &&
+ resmgr->resmgr_cb->cdc_rco_ctrl) {
+ while (clk_users--)
+ resmgr->resmgr_cb->cdc_rco_ctrl(resmgr->codec,
+ true);
+ }
+ }
+}
+
+void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+ int old_bg_audio_users;
+ int old_clk_rco_users, old_clk_mclk_users;
+
+ WCD9XXX_V2_BG_CLK_LOCK(resmgr);
+
+ old_bg_audio_users = resmgr->master_bias_users;
+ old_clk_mclk_users = resmgr->clk_mclk_users;
+ old_clk_rco_users = resmgr->clk_rco_users;
+ resmgr->master_bias_users = 0;
+ resmgr->clk_mclk_users = 0;
+ resmgr->clk_rco_users = 0;
+ resmgr->clk_type = WCD_CLK_OFF;
+
+ pr_debug("%s: old_bg_audio_users=%d old_clk_mclk_users=%d old_clk_rco_users=%d\n",
+ __func__, old_bg_audio_users,
+ old_clk_mclk_users, old_clk_rco_users);
+
+ if (old_bg_audio_users) {
+ while (old_bg_audio_users--)
+ wcd_resmgr_enable_master_bias(resmgr);
+ }
+
+ if (old_clk_mclk_users) {
+ while (old_clk_mclk_users--)
+ wcd_resmgr_enable_clk_block(resmgr, WCD_CLK_MCLK);
+ }
+
+ if (old_clk_rco_users)
+ wcd_resmgr_cdc_specific_get_clk(resmgr, old_clk_rco_users);
+
+ WCD9XXX_V2_BG_CLK_UNLOCK(resmgr);
+}
+
+
+/*
+ * wcd_resmgr_enable_master_bias: enable codec master bias
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ */
+int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+ mutex_lock(&resmgr->master_bias_lock);
+
+ resmgr->master_bias_users++;
+ if (resmgr->master_bias_users == 1) {
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
+ 0x80, 0x80);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
+ 0x40, 0x40);
+ /*
+ * 1ms delay is required after pre-charge is enabled
+ * as per HW requirement
+ */
+ usleep_range(1000, 1100);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
+ 0x40, 0x00);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD93XX_ANA_BIAS, 0x20, 0x00);
+ }
+
+ pr_debug("%s: current master bias users: %d\n", __func__,
+ resmgr->master_bias_users);
+
+ mutex_unlock(&resmgr->master_bias_lock);
+ return 0;
+}
+
+/*
+ * wcd_resmgr_disable_master_bias: disable codec master bias
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ */
+int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+ mutex_lock(&resmgr->master_bias_lock);
+ if (resmgr->master_bias_users <= 0) {
+ mutex_unlock(&resmgr->master_bias_lock);
+ return -EINVAL;
+ }
+
+ resmgr->master_bias_users--;
+ if (resmgr->master_bias_users == 0) {
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
+ 0x80, 0x00);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD93XX_ANA_BIAS, 0x20, 0x00);
+ }
+ mutex_unlock(&resmgr->master_bias_lock);
+ return 0;
+}
+
+static int wcd_resmgr_enable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+ /* Enable mclk requires master bias to be enabled first */
+ if (resmgr->master_bias_users <= 0) {
+ pr_err("%s: Cannot turn on MCLK, BG is not enabled\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (((resmgr->clk_mclk_users == 0) &&
+ (resmgr->clk_type == WCD_CLK_MCLK)) ||
+ ((resmgr->clk_mclk_users > 0) &&
+ (resmgr->clk_type != WCD_CLK_MCLK))) {
+ pr_err("%s: Error enabling MCLK, clk_type: %s\n",
+ __func__,
+ wcd_resmgr_clk_type_to_str(resmgr->clk_type));
+ return -EINVAL;
+ }
+
+ if (++resmgr->clk_mclk_users == 1) {
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD9335_ANA_CLK_TOP, 0x80, 0x80);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD9335_ANA_CLK_TOP, 0x08, 0x00);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD9335_ANA_CLK_TOP, 0x04, 0x04);
+ if (resmgr->codec_type == WCD934X) {
+ /*
+ * In tavil clock contrl register is changed
+ * to CLK_SYS_MCLK_PRG
+ */
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CLK_SYS_MCLK_PRG, 0x80, 0x80);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CLK_SYS_MCLK_PRG, 0x30, 0x10);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x00);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CLK_SYS_MCLK_PRG, 0x01, 0x01);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x00);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+ 0x01, 0x01);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x01, 0x01);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x01, 0x01);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CODEC_RPM_CLK_GATE, 0x03, 0x00);
+ } else {
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+ 0x01, 0x01);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x01, 0x01);
+ }
+ /*
+ * 10us sleep is required after clock is enabled
+ * as per HW requirement
+ */
+ usleep_range(10, 15);
+ }
+
+ resmgr->clk_type = WCD_CLK_MCLK;
+
+ pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__,
+ resmgr->clk_mclk_users,
+ wcd_resmgr_clk_type_to_str(resmgr->clk_type));
+
+ return 0;
+}
+
+static int wcd_resmgr_disable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+ if (resmgr->clk_mclk_users <= 0) {
+ pr_err("%s: No mclk users, cannot disable mclk\n", __func__);
+ return -EINVAL;
+ }
+
+ if (--resmgr->clk_mclk_users == 0) {
+ if (resmgr->clk_rco_users > 0) {
+ /* MCLK to RCO switch */
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD9335_ANA_CLK_TOP,
+ 0x08, 0x08);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x02);
+ /* Disable clock buffer */
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CLK_SYS_MCLK_PRG, 0x80, 0x00);
+ resmgr->clk_type = WCD_CLK_RCO;
+ } else {
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD9335_ANA_CLK_TOP,
+ 0x04, 0x00);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CLK_SYS_MCLK_PRG, 0x81, 0x00);
+ resmgr->clk_type = WCD_CLK_OFF;
+ }
+
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP,
+ 0x80, 0x00);
+ }
+
+ if ((resmgr->codec_type == WCD934X) &&
+ (resmgr->clk_type == WCD_CLK_OFF))
+ wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL);
+
+ pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__,
+ resmgr->clk_mclk_users,
+ wcd_resmgr_clk_type_to_str(resmgr->clk_type));
+
+ return 0;
+}
+
+static void wcd_resmgr_set_buck_accuracy(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+ 0x02, 0x02);
+ /* 100us sleep needed after HIGH_ACCURACY_PRE_EN1 */
+ usleep_range(100, 110);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+ 0x01, 0x01);
+ /* 100us sleep needed after HIGH_ACCURACY_PRE_EN2 */
+ usleep_range(100, 110);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+ 0x04, 0x04);
+ /* 100us sleep needed after HIGH_ACCURACY_EN */
+ usleep_range(100, 110);
+}
+
+static int wcd_resmgr_enable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+ bool rco_cal_done = true;
+
+ resmgr->clk_rco_users++;
+ if ((resmgr->clk_rco_users == 1) &&
+ ((resmgr->clk_type == WCD_CLK_OFF) ||
+ (resmgr->clk_mclk_users == 0))) {
+ pr_warn("%s: RCO enable requires MCLK to be ON first\n",
+ __func__);
+ resmgr->clk_rco_users--;
+ return -EINVAL;
+ } else if ((resmgr->clk_rco_users == 1) &&
+ (resmgr->clk_mclk_users)) {
+ /* RCO Enable */
+ if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) {
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD9335_ANA_RCO,
+ 0x80, 0x80);
+ if (resmgr->codec_type == WCD934X)
+ wcd_resmgr_set_buck_accuracy(resmgr);
+ }
+
+ /*
+ * 20us required after RCO BG is enabled as per HW
+ * requirements
+ */
+ usleep_range(20, 25);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO,
+ 0x40, 0x40);
+ /*
+ * 20us required after RCO is enabled as per HW
+ * requirements
+ */
+ usleep_range(20, 25);
+ /* RCO Calibration */
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO,
+ 0x04, 0x04);
+ if (resmgr->codec_type == WCD934X)
+ /*
+ * For wcd934x codec, 20us sleep is needed
+ * after enabling RCO calibration
+ */
+ usleep_range(20, 25);
+
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO,
+ 0x04, 0x00);
+ if (resmgr->codec_type == WCD934X)
+ /*
+ * For wcd934x codec, 20us sleep is needed
+ * after disabling RCO calibration
+ */
+ usleep_range(20, 25);
+
+ /* RCO calibration takes app. 5ms to complete */
+ usleep_range(WCD9XXX_RCO_CALIBRATION_DELAY_INC_US,
+ WCD9XXX_RCO_CALIBRATION_DELAY_INC_US + 100);
+ if (wcd_resmgr_codec_reg_read(resmgr, WCD9335_ANA_RCO) & 0x02)
+ rco_cal_done = false;
+
+ WARN((!rco_cal_done), "RCO Calibration failed\n");
+
+ /* Switch MUX to RCO */
+ if (resmgr->clk_mclk_users == 1) {
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD9335_ANA_CLK_TOP,
+ 0x08, 0x08);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CLK_SYS_MCLK_PRG,
+ 0x02, 0x02);
+ resmgr->clk_type = WCD_CLK_RCO;
+ }
+ }
+ pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__,
+ resmgr->clk_rco_users,
+ wcd_resmgr_clk_type_to_str(resmgr->clk_type));
+
+ return 0;
+}
+
+static int wcd_resmgr_disable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+ if ((resmgr->clk_rco_users <= 0) ||
+ (resmgr->clk_type == WCD_CLK_OFF)) {
+ pr_err("%s: rco_clk_users = %d, clk_type = %d, cannot disable\n",
+ __func__, resmgr->clk_rco_users, resmgr->clk_type);
+ return -EINVAL;
+ }
+
+ resmgr->clk_rco_users--;
+
+ if ((resmgr->clk_rco_users == 0) &&
+ (resmgr->clk_type == WCD_CLK_RCO)) {
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP,
+ 0x08, 0x00);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CLK_SYS_MCLK_PRG,
+ 0x02, 0x00);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP,
+ 0x04, 0x00);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO,
+ 0x40, 0x00);
+ if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL)
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD9335_ANA_RCO,
+ 0x80, 0x00);
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD934X_CLK_SYS_MCLK_PRG,
+ 0x01, 0x00);
+ resmgr->clk_type = WCD_CLK_OFF;
+ } else if ((resmgr->clk_rco_users == 0) &&
+ (resmgr->clk_mclk_users)) {
+ /* Disable RCO while MCLK is ON */
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO,
+ 0x40, 0x00);
+ if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL)
+ wcd_resmgr_codec_reg_update_bits(resmgr,
+ WCD9335_ANA_RCO,
+ 0x80, 0x00);
+ }
+
+ if ((resmgr->codec_type == WCD934X) &&
+ (resmgr->clk_type == WCD_CLK_OFF))
+ wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL);
+
+ pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__,
+ resmgr->clk_rco_users,
+ wcd_resmgr_clk_type_to_str(resmgr->clk_type));
+
+ return 0;
+}
+
+/*
+ * wcd_resmgr_enable_clk_block: enable MCLK or RCO
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ * @type: Clock type to enable
+ */
+int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr,
+ enum wcd_clock_type type)
+{
+ int ret;
+
+ switch (type) {
+ case WCD_CLK_MCLK:
+ ret = wcd_resmgr_enable_clk_mclk(resmgr);
+ break;
+ case WCD_CLK_RCO:
+ ret = wcd_resmgr_enable_clk_rco(resmgr);
+ break;
+ default:
+ pr_err("%s: Unknown Clock type: %s\n", __func__,
+ wcd_resmgr_clk_type_to_str(type));
+ ret = -EINVAL;
+ break;
+ };
+
+ if (ret)
+ pr_err("%s: Enable clock %s failed\n", __func__,
+ wcd_resmgr_clk_type_to_str(type));
+
+ return ret;
+}
+
+void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr,
+ int sido_src)
+{
+ if (!resmgr)
+ return;
+
+ if (sido_src == resmgr->sido_input_src)
+ return;
+
+ if (sido_src == SIDO_SOURCE_INTERNAL) {
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+ 0x04, 0x00);
+ usleep_range(100, 110);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+ 0x03, 0x00);
+ usleep_range(100, 110);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_RCO,
+ 0x80, 0x00);
+ usleep_range(100, 110);
+ resmgr->sido_input_src = SIDO_SOURCE_INTERNAL;
+ pr_debug("%s: sido input src to internal\n", __func__);
+ } else if (sido_src == SIDO_SOURCE_RCO_BG) {
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_RCO,
+ 0x80, 0x80);
+ usleep_range(100, 110);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+ 0x02, 0x02);
+ usleep_range(100, 110);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+ 0x01, 0x01);
+ usleep_range(100, 110);
+ wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+ 0x04, 0x04);
+ usleep_range(100, 110);
+ resmgr->sido_input_src = SIDO_SOURCE_RCO_BG;
+ pr_debug("%s: sido input src to external\n", __func__);
+ }
+}
+EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src);
+
+/*
+ * wcd_resmgr_set_sido_input_src_locked:
+ * Set SIDO input in BG_CLK locked context
+ *
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ * @sido_src: Select the SIDO input source
+ */
+void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr,
+ int sido_src)
+{
+ if (!resmgr)
+ return;
+
+ WCD9XXX_V2_BG_CLK_LOCK(resmgr);
+ wcd_resmgr_set_sido_input_src(resmgr, sido_src);
+ WCD9XXX_V2_BG_CLK_UNLOCK(resmgr);
+}
+EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src_locked);
+
+/*
+ * wcd_resmgr_disable_clk_block: disable MCLK or RCO
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ * @type: Clock type to disable
+ */
+int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr,
+ enum wcd_clock_type type)
+{
+ int ret;
+
+ switch (type) {
+ case WCD_CLK_MCLK:
+ ret = wcd_resmgr_disable_clk_mclk(resmgr);
+ break;
+ case WCD_CLK_RCO:
+ ret = wcd_resmgr_disable_clk_rco(resmgr);
+ break;
+ default:
+ pr_err("%s: Unknown Clock type: %s\n", __func__,
+ wcd_resmgr_clk_type_to_str(type));
+ ret = -EINVAL;
+ break;
+ };
+
+ if (ret)
+ pr_err("%s: Disable clock %s failed\n", __func__,
+ wcd_resmgr_clk_type_to_str(type));
+
+ return ret;
+}
+
+/*
+ * wcd_resmgr_init: initialize wcd resource manager
+ * @core_res: handle to struct wcd9xxx_core_resource
+ *
+ * Early init call without a handle to snd_soc_codec *
+ */
+struct wcd9xxx_resmgr_v2 *wcd_resmgr_init(
+ struct wcd9xxx_core_resource *core_res,
+ struct snd_soc_codec *codec)
+{
+ struct wcd9xxx_resmgr_v2 *resmgr;
+ struct wcd9xxx *wcd9xxx;
+
+ resmgr = kzalloc(sizeof(struct wcd9xxx_resmgr_v2), GFP_KERNEL);
+ if (!resmgr) {
+ pr_err("%s: Cannot allocate memory for wcd resmgr\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ wcd9xxx = container_of(core_res, struct wcd9xxx, core_res);
+ if (!wcd9xxx) {
+ kfree(resmgr);
+ pr_err("%s: Cannot get wcd9xx pointer\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ mutex_init(&resmgr->codec_bg_clk_lock);
+ mutex_init(&resmgr->master_bias_lock);
+ resmgr->master_bias_users = 0;
+ resmgr->clk_mclk_users = 0;
+ resmgr->clk_rco_users = 0;
+ resmgr->master_bias_users = 0;
+ resmgr->codec = codec;
+ resmgr->core_res = core_res;
+ resmgr->sido_input_src = SIDO_SOURCE_INTERNAL;
+ resmgr->codec_type = wcd9xxx->type;
+
+ return resmgr;
+}
+
+/*
+ * wcd_resmgr_remove: Clean-up wcd resource manager
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ */
+void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+ mutex_destroy(&resmgr->master_bias_lock);
+ kfree(resmgr);
+}
+
+/*
+ * wcd_resmgr_post_init: post init call to assign codec handle
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2 created during early init
+ * @resmgr_cb: codec callback function for resmgr
+ * @codec: handle to struct snd_soc_codec
+ */
+int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr,
+ const struct wcd_resmgr_cb *resmgr_cb,
+ struct snd_soc_codec *codec)
+{
+ if (!resmgr) {
+ pr_err("%s: resmgr not allocated\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!codec) {
+ pr_err("%s: Codec memory is NULL, nothing to post init\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ resmgr->codec = codec;
+ resmgr->resmgr_cb = resmgr_cb;
+
+ return 0;
+}
+MODULE_DESCRIPTION("wcd9xxx resmgr v2 module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.h b/sound/soc/codecs/wcd9xxx-resmgr-v2.h
new file mode 100644
index 000000000000..e831ba61e9c2
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr-v2.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_COMMON_V2_H__
+#define __WCD9XXX_COMMON_V2_H__
+
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+enum wcd_clock_type {
+ WCD_CLK_OFF,
+ WCD_CLK_RCO,
+ WCD_CLK_MCLK,
+};
+
+enum {
+ SIDO_SOURCE_INTERNAL,
+ SIDO_SOURCE_RCO_BG,
+};
+
+struct wcd_resmgr_cb {
+ int (*cdc_rco_ctrl)(struct snd_soc_codec *, bool);
+};
+
+struct wcd9xxx_resmgr_v2 {
+ struct snd_soc_codec *codec;
+ struct wcd9xxx_core_resource *core_res;
+
+ int master_bias_users;
+ int clk_mclk_users;
+ int clk_rco_users;
+
+ struct mutex codec_bg_clk_lock;
+ struct mutex master_bias_lock;
+
+ enum codec_variant codec_type;
+ enum wcd_clock_type clk_type;
+
+ const struct wcd_resmgr_cb *resmgr_cb;
+ int sido_input_src;
+};
+
+#define WCD9XXX_V2_BG_CLK_LOCK(resmgr) \
+{ \
+ struct wcd9xxx_resmgr_v2 *__resmgr = resmgr; \
+ pr_debug("%s: Acquiring BG_CLK\n", __func__); \
+ mutex_lock(&__resmgr->codec_bg_clk_lock); \
+ pr_debug("%s: Acquiring BG_CLK done\n", __func__); \
+}
+
+#define WCD9XXX_V2_BG_CLK_UNLOCK(resmgr) \
+{ \
+ struct wcd9xxx_resmgr_v2 *__resmgr = resmgr; \
+ pr_debug("%s: Releasing BG_CLK\n", __func__); \
+ mutex_unlock(&__resmgr->codec_bg_clk_lock); \
+}
+
+#define WCD9XXX_V2_BG_CLK_ASSERT_LOCKED(resmgr) \
+{ \
+ WARN_ONCE(!mutex_is_locked(&resmgr->codec_bg_clk_lock), \
+ "%s: BG_CLK lock should have acquired\n", __func__); \
+}
+
+int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr);
+int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr);
+struct wcd9xxx_resmgr_v2 *wcd_resmgr_init(
+ struct wcd9xxx_core_resource *core_res,
+ struct snd_soc_codec *codec);
+void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr);
+int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr,
+ const struct wcd_resmgr_cb *resmgr_cb,
+ struct snd_soc_codec *codec);
+int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr,
+ enum wcd_clock_type type);
+int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr,
+ enum wcd_clock_type type);
+int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr);
+void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr);
+void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr,
+ int sido_src);
+void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr,
+ int sido_src);
+
+#endif
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
new file mode 100644
index 000000000000..1f11725e8843
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -0,0 +1,1099 @@
+/* Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <uapi/linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9330_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9xxx-resmgr.h"
+
+static char wcd9xxx_event_string[][64] = {
+ "WCD9XXX_EVENT_INVALID",
+
+ "WCD9XXX_EVENT_PRE_RCO_ON",
+ "WCD9XXX_EVENT_POST_RCO_ON",
+ "WCD9XXX_EVENT_PRE_RCO_OFF",
+ "WCD9XXX_EVENT_POST_RCO_OFF",
+
+ "WCD9XXX_EVENT_PRE_MCLK_ON",
+ "WCD9XXX_EVENT_POST_MCLK_ON",
+ "WCD9XXX_EVENT_PRE_MCLK_OFF",
+ "WCD9XXX_EVENT_POST_MCLK_OFF",
+
+ "WCD9XXX_EVENT_PRE_BG_OFF",
+ "WCD9XXX_EVENT_POST_BG_OFF",
+ "WCD9XXX_EVENT_PRE_BG_AUDIO_ON",
+ "WCD9XXX_EVENT_POST_BG_AUDIO_ON",
+ "WCD9XXX_EVENT_PRE_BG_MBHC_ON",
+ "WCD9XXX_EVENT_POST_BG_MBHC_ON",
+
+ "WCD9XXX_EVENT_PRE_MICBIAS_1_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_1_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_2_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_2_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_3_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_3_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_4_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_4_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_1_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_1_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_2_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_2_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_3_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_3_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_4_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_4_ON",
+
+ "WCD9XXX_EVENT_PRE_CFILT_1_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_1_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_2_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_2_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_3_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_3_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_1_ON",
+ "WCD9XXX_EVENT_POST_CFILT_1_ON",
+ "WCD9XXX_EVENT_PRE_CFILT_2_ON",
+ "WCD9XXX_EVENT_POST_CFILT_2_ON",
+ "WCD9XXX_EVENT_PRE_CFILT_3_ON",
+ "WCD9XXX_EVENT_POST_CFILT_3_ON",
+
+ "WCD9XXX_EVENT_PRE_HPHL_PA_ON",
+ "WCD9XXX_EVENT_POST_HPHL_PA_OFF",
+ "WCD9XXX_EVENT_PRE_HPHR_PA_ON",
+ "WCD9XXX_EVENT_POST_HPHR_PA_OFF",
+
+ "WCD9XXX_EVENT_POST_RESUME",
+
+ "WCD9XXX_EVENT_PRE_TX_3_ON",
+ "WCD9XXX_EVENT_POST_TX_3_OFF",
+
+ "WCD9XXX_EVENT_LAST",
+};
+
+#define WCD9XXX_RCO_CALIBRATION_RETRY_COUNT 5
+#define WCD9XXX_RCO_CALIBRATION_DELAY_US 5000
+#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
+#define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 1000
+
+struct wcd9xxx_resmgr_cond_entry {
+ unsigned short reg;
+ int shift;
+ bool invert;
+ enum wcd9xxx_resmgr_cond cond;
+ struct list_head list;
+};
+
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr
+ *resmgr);
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type)
+{
+ return wcd9xxx_event_string[type];
+}
+
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_notify_event e)
+{
+ pr_debug("%s: notifier call event %d\n", __func__, e);
+ blocking_notifier_call_chain(&resmgr->notifier, e, resmgr);
+}
+
+static void wcd9xxx_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+ /* Notify bg mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_OFF);
+ /* Disable bg */
+ snd_soc_update_bits(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
+ 0x03, 0x00);
+ usleep_range(100, 110);
+ /* Notify bg mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
+}
+
+/*
+ * BG enablement should always enable in slow mode.
+ * The fast mode doesn't need to be enabled as fast mode BG is to be driven
+ * by MBHC override.
+ */
+static void wcd9xxx_enable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ /* Enable BG in slow mode and precharge */
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+ usleep_range(1000, 1100);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+}
+
+static void wcd9xxx_enable_bg_audio(struct wcd9xxx_resmgr *resmgr)
+{
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_AUDIO_ON);
+ wcd9xxx_enable_bg(resmgr);
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_AUDIO_ON);
+}
+
+static void wcd9xxx_enable_bg_mbhc(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_MBHC_ON);
+
+ /*
+ * mclk should be off or clk buff source souldn't be VBG
+ * Let's turn off mclk always
+ */
+ WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+
+ wcd9xxx_enable_bg(resmgr);
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_MBHC_ON);
+}
+
+static void wcd9xxx_disable_clock_block(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+
+ /* Notify */
+ if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_OFF);
+ else
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_PRE_MCLK_OFF);
+
+ switch (resmgr->codec_type) {
+ case WCD9XXX_CDC_TYPE_TOMTOM:
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
+ usleep_range(50, 55);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x40, 0x40);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x40, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x00);
+ break;
+ default:
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
+ usleep_range(50, 55);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00);
+ break;
+ }
+ usleep_range(50, 55);
+ /* Notify */
+ if (resmgr->clk_type == WCD9XXX_CLK_RCO) {
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_RCO_OFF);
+ } else {
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_MCLK_OFF);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_resmgr_cdc_specific_get_clk(struct wcd9xxx_resmgr *resmgr,
+ int clk_users)
+{
+ /* Caller of this funcion should have acquired
+ * BG_CLK lock
+ */
+ WCD9XXX_BG_CLK_UNLOCK(resmgr);
+ if (clk_users) {
+ if (resmgr->resmgr_cb &&
+ resmgr->resmgr_cb->cdc_rco_ctrl) {
+ while (clk_users--)
+ resmgr->resmgr_cb->cdc_rco_ctrl(resmgr->codec,
+ true);
+ }
+ }
+ /* Acquire BG_CLK lock before return */
+ WCD9XXX_BG_CLK_LOCK(resmgr);
+}
+
+void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr)
+{
+ int old_bg_audio_users, old_bg_mbhc_users;
+ int old_clk_rco_users, old_clk_mclk_users;
+
+ pr_debug("%s: enter\n", __func__);
+
+ WCD9XXX_BG_CLK_LOCK(resmgr);
+ old_bg_audio_users = resmgr->bg_audio_users;
+ old_bg_mbhc_users = resmgr->bg_mbhc_users;
+ old_clk_rco_users = resmgr->clk_rco_users;
+ old_clk_mclk_users = resmgr->clk_mclk_users;
+ resmgr->bg_audio_users = 0;
+ resmgr->bg_mbhc_users = 0;
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ resmgr->clk_rco_users = 0;
+ resmgr->clk_mclk_users = 0;
+ resmgr->clk_type = WCD9XXX_CLK_OFF;
+
+ if (old_bg_audio_users) {
+ while (old_bg_audio_users--)
+ wcd9xxx_resmgr_get_bandgap(resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ }
+
+ if (old_bg_mbhc_users) {
+ while (old_bg_mbhc_users--)
+ wcd9xxx_resmgr_get_bandgap(resmgr,
+ WCD9XXX_BANDGAP_MBHC_MODE);
+ }
+
+ if (old_clk_mclk_users) {
+ while (old_clk_mclk_users--)
+ wcd9xxx_resmgr_get_clk_block(resmgr, WCD9XXX_CLK_MCLK);
+ }
+
+ if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
+ wcd9xxx_resmgr_cdc_specific_get_clk(resmgr, old_clk_rco_users);
+ } else if (old_clk_rco_users) {
+ while (old_clk_rco_users--)
+ wcd9xxx_resmgr_get_clk_block(resmgr,
+ WCD9XXX_CLK_RCO);
+ }
+ WCD9XXX_BG_CLK_UNLOCK(resmgr);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/*
+ * wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_bandgap_type choice)
+{
+ enum wcd9xxx_clock_type clock_save = WCD9XXX_CLK_OFF;
+
+ pr_debug("%s: enter, wants %d\n", __func__, choice);
+
+ WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+ switch (choice) {
+ case WCD9XXX_BANDGAP_AUDIO_MODE:
+ resmgr->bg_audio_users++;
+ if (resmgr->bg_audio_users == 1 && resmgr->bg_mbhc_users) {
+ /*
+ * Current bg is MBHC mode, about to switch to
+ * audio mode.
+ */
+ WARN_ON(resmgr->bandgap_type !=
+ WCD9XXX_BANDGAP_MBHC_MODE);
+
+ /* BG mode can be changed only with clock off */
+ if (resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM)
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* Swtich BG mode */
+ wcd9xxx_disable_bg(resmgr);
+ wcd9xxx_enable_bg_audio(resmgr);
+ /* restore clock */
+ if (resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM)
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ } else if (resmgr->bg_audio_users == 1) {
+ /* currently off, just enable it */
+ WARN_ON(resmgr->bandgap_type != WCD9XXX_BANDGAP_OFF);
+ wcd9xxx_enable_bg_audio(resmgr);
+ }
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE;
+ break;
+ case WCD9XXX_BANDGAP_MBHC_MODE:
+ resmgr->bg_mbhc_users++;
+ if (resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE ||
+ resmgr->bandgap_type == WCD9XXX_BANDGAP_AUDIO_MODE)
+ /* do nothing */
+ break;
+
+ /* bg mode can be changed only with clock off */
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* enable bg with MBHC mode */
+ wcd9xxx_enable_bg_mbhc(resmgr);
+ /* restore clock */
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ /* save current mode */
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_MBHC_MODE;
+ break;
+ default:
+ pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+ break;
+ }
+
+ pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+ resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+/*
+ * wcd9xxx_resmgr_put_bandgap : Unvote bandgap ref that has been voted
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_bandgap_type choice)
+{
+ enum wcd9xxx_clock_type clock_save;
+
+ pr_debug("%s: enter choice %d\n", __func__, choice);
+
+ WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+ switch (choice) {
+ case WCD9XXX_BANDGAP_AUDIO_MODE:
+ if (--resmgr->bg_audio_users == 0) {
+ if (resmgr->bg_mbhc_users) {
+ /* bg mode can be changed only with clock off */
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* switch to MBHC mode */
+ wcd9xxx_enable_bg_mbhc(resmgr);
+ /* restore clock */
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ resmgr->bandgap_type =
+ WCD9XXX_BANDGAP_MBHC_MODE;
+ } else {
+ /* turn off */
+ wcd9xxx_disable_bg(resmgr);
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ }
+ }
+ break;
+ case WCD9XXX_BANDGAP_MBHC_MODE:
+ WARN(resmgr->bandgap_type == WCD9XXX_BANDGAP_OFF,
+ "Unexpected bandgap type %d\n", resmgr->bandgap_type);
+ if (--resmgr->bg_mbhc_users == 0 &&
+ resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE) {
+ wcd9xxx_disable_bg(resmgr);
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+ break;
+ }
+
+ pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+ resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ if (enable) {
+ resmgr->rx_bias_count++;
+ if (resmgr->rx_bias_count == 1)
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+ 0x80, 0x80);
+ } else {
+ resmgr->rx_bias_count--;
+ if (!resmgr->rx_bias_count)
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+ 0x80, 0x00);
+ }
+}
+
+int wcd9xxx_resmgr_enable_config_mode(struct wcd9xxx_resmgr *resmgr, int enable)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ pr_debug("%s: enable = %d\n", __func__, enable);
+ if (enable) {
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
+ /* bandgap mode to fast */
+ if (resmgr->pdata->mclk_rate == WCD9XXX_MCLK_CLK_12P288MHZ)
+ /* Set current value to 200nA for 12.288MHz clock */
+ snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x37);
+ else
+ snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
+
+ usleep_range(5, 10);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
+ usleep_range(10, 20);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0);
+ usleep_range(10000, 10100);
+
+ if (resmgr->pdata->mclk_rate != WCD9XXX_MCLK_CLK_12P288MHZ)
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+ 0x08, 0x08);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x1, 0);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0);
+ }
+
+ return 0;
+}
+
+static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_config_mode config_mode)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+ unsigned long delay = WCD9XXX_RCO_CALIBRATION_DELAY_US;
+ int num_retry = 0;
+ unsigned int valr;
+ unsigned int valr1;
+ unsigned int valw[] = {0x01, 0x01, 0x10, 0x00};
+
+ pr_debug("%s: config_mode = %d\n", __func__, config_mode);
+
+ /* transit to RCO requires mclk off */
+ if (resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM)
+ WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+
+ if (config_mode == WCD9XXX_CFG_RCO) {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON);
+ /* enable RCO and switch to it */
+ wcd9xxx_resmgr_enable_config_mode(resmgr, 1);
+ snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+ usleep_range(1000, 1100);
+ } else if (config_mode == WCD9XXX_CFG_CAL_RCO) {
+ snd_soc_update_bits(codec, TOMTOM_A_BIAS_OSC_BG_CTL,
+ 0x01, 0x01);
+ /* 1ms sleep required after BG enabled */
+ usleep_range(1000, 1100);
+
+ if (resmgr->pdata->mclk_rate == WCD9XXX_MCLK_CLK_12P288MHZ) {
+ /*
+ * Set RCO clock rate as 12.288MHz rate explicitly
+ * as the Qfuse values are incorrect for this rate
+ */
+ snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL,
+ 0x50, 0x50);
+ } else {
+ snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL,
+ 0x18, 0x10);
+ valr = snd_soc_read(codec,
+ TOMTOM_A_QFUSE_DATA_OUT0) & (0x04);
+ valr1 = snd_soc_read(codec,
+ TOMTOM_A_QFUSE_DATA_OUT1) & (0x08);
+ valr = (valr >> 1) | (valr1 >> 3);
+ snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL, 0x60,
+ valw[valr] << 5);
+ }
+ snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL, 0x80, 0x80);
+
+ do {
+ snd_soc_update_bits(codec,
+ TOMTOM_A_RCO_CALIBRATION_CTRL1,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec,
+ TOMTOM_A_RCO_CALIBRATION_CTRL1,
+ 0x80, 0x00);
+ /* RCO calibration takes approx. 5ms */
+ usleep_range(delay, delay +
+ WCD9XXX_USLEEP_RANGE_MARGIN_US);
+ if (!(snd_soc_read(codec,
+ TOMTOM_A_RCO_CALIBRATION_RESULT1) & 0x10))
+ break;
+ if (num_retry >= 3) {
+ delay = delay +
+ WCD9XXX_RCO_CALIBRATION_DELAY_INC_US;
+ }
+ } while (num_retry++ < WCD9XXX_RCO_CALIBRATION_RETRY_COUNT);
+ } else {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON);
+ /* switch to MCLK */
+
+ switch (resmgr->codec_type) {
+ case WCD9XXX_CDC_TYPE_TOMTOM:
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+ 0x08, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+ 0x40, 0x40);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+ 0x40, 0x00);
+ /* clk source to ext clk and clk buff ref to VBG */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+ 0x0C, 0x04);
+ break;
+ default:
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+ 0x08, 0x00);
+ /* if RCO is enabled, switch from it */
+ if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) {
+ snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2,
+ 0x02);
+ wcd9xxx_resmgr_enable_config_mode(resmgr, 0);
+ }
+ /* clk source to ext clk and clk buff ref to VBG */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+ 0x0C, 0x04);
+ break;
+ }
+ }
+
+ if (config_mode != WCD9XXX_CFG_CAL_RCO) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+ 0x01, 0x01);
+ /*
+ * sleep required by codec hardware to
+ * enable clock buffer
+ */
+ usleep_range(1000, 1200);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2,
+ 0x02, 0x00);
+ /* on MCLK */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL,
+ 0x01, 0x01);
+ }
+ usleep_range(50, 55);
+
+ /* Notify */
+ if (config_mode == WCD9XXX_CFG_RCO)
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_RCO_ON);
+ else if (config_mode == WCD9XXX_CFG_MCLK)
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_MCLK_ON);
+}
+
+/*
+ * disable clock and return previous clock state
+ */
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr *resmgr)
+{
+ WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+ if (resmgr->clk_type != WCD9XXX_CLK_OFF)
+ wcd9xxx_disable_clock_block(resmgr);
+ return resmgr->clk_type != WCD9XXX_CLK_OFF;
+}
+
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ if (type != WCD9XXX_CLK_OFF)
+ wcd9xxx_enable_clock_block(resmgr, type == WCD9XXX_CLK_RCO);
+}
+
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n",
+ __func__, resmgr->clk_type, type,
+ resmgr->clk_rco_users, resmgr->clk_mclk_users);
+ WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+ switch (type) {
+ case WCD9XXX_CLK_RCO:
+ if (++resmgr->clk_rco_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_OFF) {
+ /* enable RCO and switch to it */
+ wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_RCO);
+ resmgr->clk_type = WCD9XXX_CLK_RCO;
+ } else if (resmgr->clk_rco_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_MCLK &&
+ resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
+ /*
+ * Enable RCO but do not switch CLK MUX to RCO
+ * unless ext_clk_users is 1, which indicates
+ * EXT CLK is enabled for RCO calibration
+ */
+ wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_CAL_RCO);
+ if (resmgr->ext_clk_users == 1) {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_PRE_RCO_ON);
+ /* CLK MUX to RCO */
+ if (resmgr->pdata->mclk_rate !=
+ WCD9XXX_MCLK_CLK_12P288MHZ)
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CLK_BUFF_EN1,
+ 0x08, 0x08);
+ resmgr->clk_type = WCD9XXX_CLK_RCO;
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_RCO_ON);
+ }
+ }
+ break;
+ case WCD9XXX_CLK_MCLK:
+ if (++resmgr->clk_mclk_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_OFF) {
+ /* switch to MCLK */
+ wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_MCLK);
+ resmgr->clk_type = WCD9XXX_CLK_MCLK;
+ } else if (resmgr->clk_mclk_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_RCO) {
+ /* RCO to MCLK switch, with RCO still powered on */
+ if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_PRE_MCLK_ON);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
+ 0x40, 0x00);
+ /* Enable clock buffer */
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CLK_BUFF_EN1,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CLK_BUFF_EN1,
+ 0x08, 0x00);
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_MCLK_ON);
+ } else {
+ /* if RCO is enabled, switch from it */
+ WARN_ON(!(snd_soc_read(resmgr->codec,
+ WCD9XXX_A_RC_OSC_FREQ) & 0x80));
+ /* disable clock block */
+ wcd9xxx_disable_clock_block(resmgr);
+ /* switch to MCLK */
+ wcd9xxx_enable_clock_block(resmgr,
+ WCD9XXX_CFG_MCLK);
+ }
+ resmgr->clk_type = WCD9XXX_CLK_MCLK;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+ type);
+ break;
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type);
+
+ WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+ switch (type) {
+ case WCD9XXX_CLK_RCO:
+ if (--resmgr->clk_rco_users == 0 &&
+ resmgr->clk_type == WCD9XXX_CLK_RCO) {
+ wcd9xxx_disable_clock_block(resmgr);
+ if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
+ /* Powerdown RCO */
+ snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL,
+ 0x80, 0x00);
+ snd_soc_update_bits(codec,
+ TOMTOM_A_BIAS_OSC_BG_CTL,
+ 0x01, 0x00);
+ } else {
+ /* if RCO is enabled, switch from it */
+ if (snd_soc_read(resmgr->codec,
+ WCD9XXX_A_RC_OSC_FREQ)
+ & 0x80) {
+ snd_soc_write(resmgr->codec,
+ WCD9XXX_A_CLK_BUFF_EN2,
+ 0x02);
+ wcd9xxx_resmgr_enable_config_mode(
+ resmgr, 0);
+ }
+ }
+ resmgr->clk_type = WCD9XXX_CLK_OFF;
+ }
+ break;
+ case WCD9XXX_CLK_MCLK:
+ if (--resmgr->clk_mclk_users == 0 &&
+ resmgr->clk_rco_users == 0) {
+ wcd9xxx_disable_clock_block(resmgr);
+
+ if ((resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) &&
+ (snd_soc_read(codec, TOMTOM_A_RCO_CTRL) & 0x80)) {
+ /* powerdown RCO*/
+ snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL,
+ 0x80, 0x00);
+ snd_soc_update_bits(codec,
+ TOMTOM_A_BIAS_OSC_BG_CTL,
+ 0x01, 0x00);
+ }
+ resmgr->clk_type = WCD9XXX_CLK_OFF;
+ } else if (resmgr->clk_mclk_users == 0 &&
+ resmgr->clk_rco_users) {
+ if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
+ if (!(snd_soc_read(codec, TOMTOM_A_RCO_CTRL) &
+ 0x80)) {
+ dev_dbg(codec->dev, "%s: Enabling RCO\n",
+ __func__);
+ wcd9xxx_enable_clock_block(resmgr,
+ WCD9XXX_CFG_CAL_RCO);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CLK_BUFF_EN1,
+ 0x01, 0x00);
+ } else {
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_PRE_MCLK_OFF);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CLK_BUFF_EN1,
+ 0x08, 0x08);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_CLK_BUFF_EN1,
+ 0x01, 0x00);
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_MCLK_OFF);
+ /* CLK Mux changed to RCO, notify that
+ * RCO is ON
+ */
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_RCO_ON);
+ }
+ } else {
+ /* disable clock */
+ wcd9xxx_disable_clock_block(resmgr);
+ /* switch to RCO */
+ wcd9xxx_enable_clock_block(resmgr,
+ WCD9XXX_CFG_RCO);
+ }
+ resmgr->clk_type = WCD9XXX_CLK_RCO;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+ type);
+ break;
+ }
+ WARN_ON(resmgr->clk_rco_users < 0);
+ WARN_ON(resmgr->clk_mclk_users < 0);
+
+ pr_debug("%s: new rco_users %d, mclk_users %d\n", __func__,
+ resmgr->clk_rco_users, resmgr->clk_mclk_users);
+}
+
+/*
+ * wcd9xxx_resmgr_get_clk_type()
+ * Returns clk type that is currently enabled
+ */
+int wcd9xxx_resmgr_get_clk_type(struct wcd9xxx_resmgr *resmgr)
+{
+ return resmgr->clk_type;
+}
+
+static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel,
+ bool inc)
+{
+ u16 micb_cfilt_reg;
+ enum wcd9xxx_notify_event e_pre_on, e_post_off;
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ switch (cfilt_sel) {
+ case WCD9XXX_CFILT1_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_1_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_1_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_1_OFF;
+ break;
+ case WCD9XXX_CFILT2_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_2_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_2_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_2_OFF;
+ break;
+ case WCD9XXX_CFILT3_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_3_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_3_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_3_OFF;
+ break;
+ default:
+ WARN(1, "Invalid CFILT selection %d\n", cfilt_sel);
+ return; /* should not happen */
+ }
+
+ if (inc) {
+ if ((resmgr->cfilt_users[cfilt_sel]++) == 0) {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, e_pre_on);
+ /* Enable CFILT */
+ snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
+ }
+ } else {
+ /*
+ * Check if count not zero, decrease
+ * then check if zero, go ahead disable cfilter
+ */
+ WARN(resmgr->cfilt_users[cfilt_sel] == 0,
+ "Invalid CFILT use count 0\n");
+ if ((--resmgr->cfilt_users[cfilt_sel]) == 0) {
+ /* Disable CFILT */
+ snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
+ /* Notify MBHC so MBHC can switch CFILT to fast mode */
+ wcd9xxx_resmgr_notifier_call(resmgr, e_post_off);
+ }
+ }
+}
+
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+ return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, true);
+}
+
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+ return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, false);
+}
+
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+ unsigned int cfilt_mv)
+{
+ int rc = -EINVAL;
+ unsigned int ldoh_v = resmgr->micbias_pdata->ldoh_v;
+ unsigned min_mv, max_mv;
+
+ switch (ldoh_v) {
+ case WCD9XXX_LDOH_1P95_V:
+ min_mv = 160;
+ max_mv = 1800;
+ break;
+ case WCD9XXX_LDOH_2P35_V:
+ min_mv = 200;
+ max_mv = 2200;
+ break;
+ case WCD9XXX_LDOH_2P75_V:
+ min_mv = 240;
+ max_mv = 2600;
+ break;
+ case WCD9XXX_LDOH_3P0_V:
+ min_mv = 260;
+ max_mv = 2875;
+ break;
+ default:
+ goto done;
+ }
+
+ if (cfilt_mv < min_mv || cfilt_mv > max_mv)
+ goto done;
+
+ for (rc = 4; rc <= 44; rc++) {
+ min_mv = max_mv * (rc) / 44;
+ if (min_mv >= cfilt_mv) {
+ rc -= 4;
+ break;
+ }
+ }
+done:
+ return rc;
+}
+
+static void wcd9xxx_resmgr_cond_trigger_cond(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_resmgr_cond cond)
+{
+ struct list_head *l;
+ struct wcd9xxx_resmgr_cond_entry *e;
+ bool set;
+
+ pr_debug("%s: enter\n", __func__);
+ /* update bit if cond isn't available or cond is set */
+ set = !test_bit(cond, &resmgr->cond_avail_flags) ||
+ !!test_bit(cond, &resmgr->cond_flags);
+ list_for_each(l, &resmgr->update_bit_cond_h) {
+ e = list_entry(l, struct wcd9xxx_resmgr_cond_entry, list);
+ if (e->cond == cond)
+ snd_soc_update_bits(resmgr->codec, e->reg,
+ 1 << e->shift,
+ (set ? !e->invert : e->invert)
+ << e->shift);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+/*
+ * wcd9xxx_regmgr_cond_register : notify resmgr conditions in the condbits are
+ * avaliable and notified.
+ * condbits : contains bitmask of enum wcd9xxx_resmgr_cond
+ */
+void wcd9xxx_regmgr_cond_register(struct wcd9xxx_resmgr *resmgr,
+ unsigned long condbits)
+{
+ unsigned int cond;
+
+ for_each_set_bit(cond, &condbits, BITS_PER_BYTE * sizeof(condbits)) {
+ mutex_lock(&resmgr->update_bit_cond_lock);
+ WARN(test_bit(cond, &resmgr->cond_avail_flags),
+ "Condition 0x%0x is already registered\n", cond);
+ set_bit(cond, &resmgr->cond_avail_flags);
+ wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond);
+ mutex_unlock(&resmgr->update_bit_cond_lock);
+ pr_debug("%s: Condition 0x%x is registered\n", __func__, cond);
+ }
+}
+
+void wcd9xxx_regmgr_cond_deregister(struct wcd9xxx_resmgr *resmgr,
+ unsigned long condbits)
+{
+ unsigned int cond;
+
+ for_each_set_bit(cond, &condbits, BITS_PER_BYTE * sizeof(condbits)) {
+ mutex_lock(&resmgr->update_bit_cond_lock);
+ WARN(!test_bit(cond, &resmgr->cond_avail_flags),
+ "Condition 0x%0x isn't registered\n", cond);
+ clear_bit(cond, &resmgr->cond_avail_flags);
+ wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond);
+ mutex_unlock(&resmgr->update_bit_cond_lock);
+ pr_debug("%s: Condition 0x%x is deregistered\n", __func__,
+ cond);
+ }
+}
+
+void wcd9xxx_resmgr_cond_update_cond(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_resmgr_cond cond, bool set)
+{
+ mutex_lock(&resmgr->update_bit_cond_lock);
+ if ((set && !test_and_set_bit(cond, &resmgr->cond_flags)) ||
+ (!set && test_and_clear_bit(cond, &resmgr->cond_flags))) {
+ pr_debug("%s: Resource %d condition changed to %s\n", __func__,
+ cond, set ? "set" : "clear");
+ wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond);
+ }
+ mutex_unlock(&resmgr->update_bit_cond_lock);
+}
+
+int wcd9xxx_resmgr_add_cond_update_bits(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_resmgr_cond cond,
+ unsigned short reg, int shift,
+ bool invert)
+{
+ struct wcd9xxx_resmgr_cond_entry *entry;
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->cond = cond;
+ entry->reg = reg;
+ entry->shift = shift;
+ entry->invert = invert;
+
+ mutex_lock(&resmgr->update_bit_cond_lock);
+ list_add_tail(&entry->list, &resmgr->update_bit_cond_h);
+
+ wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond);
+ mutex_unlock(&resmgr->update_bit_cond_lock);
+
+ return 0;
+}
+
+/*
+ * wcd9xxx_resmgr_rm_cond_update_bits :
+ * Clear bit and remove from the conditional bit update list
+ */
+int wcd9xxx_resmgr_rm_cond_update_bits(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_resmgr_cond cond,
+ unsigned short reg, int shift,
+ bool invert)
+{
+ struct list_head *l, *next;
+ struct wcd9xxx_resmgr_cond_entry *e = NULL;
+
+ pr_debug("%s: enter\n", __func__);
+ mutex_lock(&resmgr->update_bit_cond_lock);
+ list_for_each_safe(l, next, &resmgr->update_bit_cond_h) {
+ e = list_entry(l, struct wcd9xxx_resmgr_cond_entry, list);
+ if (e->reg == reg && e->shift == shift && e->invert == invert) {
+ snd_soc_update_bits(resmgr->codec, e->reg,
+ 1 << e->shift,
+ e->invert << e->shift);
+ list_del(&e->list);
+ mutex_unlock(&resmgr->update_bit_cond_lock);
+ kfree(e);
+ return 0;
+ }
+ }
+ mutex_unlock(&resmgr->update_bit_cond_lock);
+ pr_err("%s: Cannot find update bit entry reg 0x%x, shift %d\n",
+ __func__, e ? e->reg : 0, e ? e->shift : 0);
+
+ return -EINVAL;
+}
+
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock)
+{
+ return blocking_notifier_chain_register(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock)
+{
+ return blocking_notifier_chain_unregister(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec,
+ struct wcd9xxx_core_resource *core_res,
+ struct wcd9xxx_pdata *pdata,
+ struct wcd9xxx_micbias_setting *micbias_pdata,
+ struct wcd9xxx_reg_address *reg_addr,
+ const struct wcd9xxx_resmgr_cb *resmgr_cb,
+ enum wcd9xxx_cdc_type cdc_type)
+{
+ WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1,
+ "Event string table isn't up to date!, %zd != %d\n",
+ ARRAY_SIZE(wcd9xxx_event_string), WCD9XXX_EVENT_LAST + 1);
+
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ resmgr->codec = codec;
+ resmgr->codec_type = cdc_type;
+ /* This gives access of core handle to lock/unlock suspend */
+ resmgr->core_res = core_res;
+ resmgr->pdata = pdata;
+ resmgr->micbias_pdata = micbias_pdata;
+ resmgr->reg_addr = reg_addr;
+ resmgr->resmgr_cb = resmgr_cb;
+
+ INIT_LIST_HEAD(&resmgr->update_bit_cond_h);
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier);
+
+ mutex_init(&resmgr->codec_resource_lock);
+ mutex_init(&resmgr->codec_bg_clk_lock);
+ mutex_init(&resmgr->update_bit_cond_lock);
+
+ return 0;
+}
+
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_destroy(&resmgr->update_bit_cond_lock);
+ mutex_destroy(&resmgr->codec_bg_clk_lock);
+ mutex_destroy(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_lock(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_unlock(&resmgr->codec_resource_lock);
+}
+
+MODULE_DESCRIPTION("wcd9xxx resmgr module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
new file mode 100644
index 000000000000..e35d6161d488
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.h
@@ -0,0 +1,280 @@
+/* Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_COMMON_H__
+#define __WCD9XXX_COMMON_H__
+
+#include <linux/notifier.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+enum wcd9xxx_bandgap_type {
+ WCD9XXX_BANDGAP_OFF,
+ WCD9XXX_BANDGAP_AUDIO_MODE,
+ WCD9XXX_BANDGAP_MBHC_MODE,
+};
+
+enum wcd9xxx_cdc_type {
+ WCD9XXX_CDC_TYPE_INVALID = 0,
+ WCD9XXX_CDC_TYPE_TAIKO,
+ WCD9XXX_CDC_TYPE_TAPAN,
+ WCD9XXX_CDC_TYPE_HELICON,
+ WCD9XXX_CDC_TYPE_TOMTOM,
+};
+
+enum wcd9xxx_clock_type {
+ WCD9XXX_CLK_OFF,
+ WCD9XXX_CLK_RCO,
+ WCD9XXX_CLK_MCLK,
+};
+
+enum wcd9xxx_clock_config_mode {
+ WCD9XXX_CFG_MCLK = 0,
+ WCD9XXX_CFG_RCO,
+ WCD9XXX_CFG_CAL_RCO,
+};
+
+enum wcd9xxx_cfilt_sel {
+ WCD9XXX_CFILT1_SEL,
+ WCD9XXX_CFILT2_SEL,
+ WCD9XXX_CFILT3_SEL,
+ WCD9XXX_NUM_OF_CFILT,
+};
+
+struct wcd9xxx_reg_address {
+ u16 micb_4_ctl;
+ u16 micb_4_int_rbias;
+ u16 micb_4_mbhc;
+};
+
+enum wcd9xxx_notify_event {
+ WCD9XXX_EVENT_INVALID,
+
+ WCD9XXX_EVENT_PRE_RCO_ON,
+ WCD9XXX_EVENT_POST_RCO_ON,
+ WCD9XXX_EVENT_PRE_RCO_OFF,
+ WCD9XXX_EVENT_POST_RCO_OFF,
+
+ WCD9XXX_EVENT_PRE_MCLK_ON,
+ WCD9XXX_EVENT_POST_MCLK_ON,
+ WCD9XXX_EVENT_PRE_MCLK_OFF,
+ WCD9XXX_EVENT_POST_MCLK_OFF,
+
+ WCD9XXX_EVENT_PRE_BG_OFF,
+ WCD9XXX_EVENT_POST_BG_OFF,
+ WCD9XXX_EVENT_PRE_BG_AUDIO_ON,
+ WCD9XXX_EVENT_POST_BG_AUDIO_ON,
+ WCD9XXX_EVENT_PRE_BG_MBHC_ON,
+ WCD9XXX_EVENT_POST_BG_MBHC_ON,
+
+ WCD9XXX_EVENT_PRE_MICBIAS_1_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_1_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_2_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_2_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_3_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_3_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_4_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_4_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_1_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_1_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_2_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_2_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_3_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_3_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_4_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_4_ON,
+
+ WCD9XXX_EVENT_PRE_CFILT_1_OFF,
+ WCD9XXX_EVENT_POST_CFILT_1_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_2_OFF,
+ WCD9XXX_EVENT_POST_CFILT_2_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_3_OFF,
+ WCD9XXX_EVENT_POST_CFILT_3_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_1_ON,
+ WCD9XXX_EVENT_POST_CFILT_1_ON,
+ WCD9XXX_EVENT_PRE_CFILT_2_ON,
+ WCD9XXX_EVENT_POST_CFILT_2_ON,
+ WCD9XXX_EVENT_PRE_CFILT_3_ON,
+ WCD9XXX_EVENT_POST_CFILT_3_ON,
+
+ WCD9XXX_EVENT_PRE_HPHL_PA_ON,
+ WCD9XXX_EVENT_POST_HPHL_PA_OFF,
+ WCD9XXX_EVENT_PRE_HPHR_PA_ON,
+ WCD9XXX_EVENT_POST_HPHR_PA_OFF,
+
+ WCD9XXX_EVENT_POST_RESUME,
+
+ WCD9XXX_EVENT_PRE_TX_3_ON,
+ WCD9XXX_EVENT_POST_TX_3_OFF,
+
+ WCD9XXX_EVENT_LAST,
+};
+
+struct wcd9xxx_resmgr_cb {
+ int (*cdc_rco_ctrl)(struct snd_soc_codec *, bool);
+};
+
+struct wcd9xxx_resmgr {
+ struct snd_soc_codec *codec;
+ struct wcd9xxx_core_resource *core_res;
+
+ u32 rx_bias_count;
+
+ /*
+ * bandgap_type, bg_audio_users and bg_mbhc_users have to be
+ * referred/manipulated after acquiring codec_bg_clk_lock mutex
+ */
+ enum wcd9xxx_bandgap_type bandgap_type;
+ u16 bg_audio_users;
+ u16 bg_mbhc_users;
+
+ /*
+ * clk_type, clk_rco_users and clk_mclk_users have to be
+ * referred/manipulated after acquiring codec_bg_clk_lock mutex
+ */
+ enum wcd9xxx_clock_type clk_type;
+ u16 clk_rco_users;
+ u16 clk_mclk_users;
+ u16 ext_clk_users;
+
+ /* cfilt users per cfilts */
+ u16 cfilt_users[WCD9XXX_NUM_OF_CFILT];
+
+ struct wcd9xxx_reg_address *reg_addr;
+
+ struct wcd9xxx_pdata *pdata;
+
+ struct wcd9xxx_micbias_setting *micbias_pdata;
+
+ struct blocking_notifier_head notifier;
+ /* Notifier needs mbhc pointer with resmgr */
+ struct wcd9xxx_mbhc *mbhc;
+
+ unsigned long cond_flags;
+ unsigned long cond_avail_flags;
+ struct list_head update_bit_cond_h;
+ struct mutex update_bit_cond_lock;
+
+ /*
+ * Currently, only used for mbhc purpose, to protect
+ * concurrent execution of mbhc threaded irq handlers and
+ * kill race between DAPM and MBHC. But can serve as a
+ * general lock to protect codec resource
+ */
+ struct mutex codec_resource_lock;
+ struct mutex codec_bg_clk_lock;
+
+ enum wcd9xxx_cdc_type codec_type;
+
+ const struct wcd9xxx_resmgr_cb *resmgr_cb;
+};
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec,
+ struct wcd9xxx_core_resource *core_res,
+ struct wcd9xxx_pdata *pdata,
+ struct wcd9xxx_micbias_setting *micbias_pdata,
+ struct wcd9xxx_reg_address *reg_addr,
+ const struct wcd9xxx_resmgr_cb *resmgr_cb,
+ enum wcd9xxx_cdc_type cdc_type);
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr);
+
+int wcd9xxx_resmgr_enable_config_mode(struct wcd9xxx_resmgr *resmgr,
+ int enable);
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable);
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel);
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel);
+int wcd9xxx_resmgr_get_clk_type(struct wcd9xxx_resmgr *resmgr);
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr);
+void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_LOCK(resmgr) \
+{ \
+ pr_debug("%s: Acquiring BCL\n", __func__); \
+ wcd9xxx_resmgr_bcl_lock(resmgr); \
+ pr_debug("%s: Acquiring BCL done\n", __func__); \
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_UNLOCK(resmgr) \
+{ \
+ pr_debug("%s: Release BCL\n", __func__); \
+ wcd9xxx_resmgr_bcl_unlock(resmgr); \
+}
+
+#define WCD9XXX_BCL_ASSERT_LOCKED(resmgr) \
+{ \
+ WARN_ONCE(!mutex_is_locked(&resmgr->codec_resource_lock), \
+ "%s: BCL should have acquired\n", __func__); \
+}
+
+#define WCD9XXX_BG_CLK_LOCK(resmgr) \
+{ \
+ struct wcd9xxx_resmgr *__resmgr = resmgr; \
+ pr_debug("%s: Acquiring BG_CLK\n", __func__); \
+ mutex_lock(&__resmgr->codec_bg_clk_lock); \
+ pr_debug("%s: Acquiring BG_CLK done\n", __func__); \
+}
+
+#define WCD9XXX_BG_CLK_UNLOCK(resmgr) \
+{ \
+ struct wcd9xxx_resmgr *__resmgr = resmgr; \
+ pr_debug("%s: Releasing BG_CLK\n", __func__); \
+ mutex_unlock(&__resmgr->codec_bg_clk_lock); \
+}
+
+#define WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr) \
+{ \
+ WARN_ONCE(!mutex_is_locked(&resmgr->codec_bg_clk_lock), \
+ "%s: BG_CLK lock should have acquired\n", __func__); \
+}
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type);
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+ unsigned int cfilt_mv);
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock);
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock);
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_notify_event e);
+
+enum wcd9xxx_resmgr_cond {
+ WCD9XXX_COND_HPH = 0x01, /* Headphone */
+ WCD9XXX_COND_HPH_MIC = 0x02, /* Microphone on the headset */
+};
+void wcd9xxx_regmgr_cond_register(struct wcd9xxx_resmgr *resmgr,
+ unsigned long condbits);
+void wcd9xxx_regmgr_cond_deregister(struct wcd9xxx_resmgr *resmgr,
+ unsigned long condbits);
+int wcd9xxx_resmgr_rm_cond_update_bits(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_resmgr_cond cond,
+ unsigned short reg, int shift,
+ bool invert);
+int wcd9xxx_resmgr_add_cond_update_bits(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_resmgr_cond cond,
+ unsigned short reg, int shift,
+ bool invert);
+void wcd9xxx_resmgr_cond_update_cond(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_resmgr_cond cond, bool set);
+
+#endif /* __WCD9XXX_COMMON_H__ */
diff --git a/sound/soc/codecs/wcd_cmi_api.h b/sound/soc/codecs/wcd_cmi_api.h
new file mode 100644
index 000000000000..39be6417e327
--- /dev/null
+++ b/sound/soc/codecs/wcd_cmi_api.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CMI_API__
+#define __CMI_API__
+
+enum cmi_api_result {
+ CMI_API_FAILED = 1,
+ CMI_API_BUSY,
+ CMI_API_NO_MEMORY,
+ CMI_API_NOT_READY,
+};
+
+enum cmi_api_event {
+ CMI_API_MSG = 1,
+ CMI_API_OFFLINE,
+ CMI_API_ONLINE,
+ CMI_API_DEINITIALIZED,
+};
+
+struct cmi_api_notification {
+ enum cmi_api_event event;
+ enum cmi_api_result result;
+ void *message;
+};
+
+void *cmi_register(
+ void notification_callback
+ (const struct cmi_api_notification *parameter),
+ u32 service);
+enum cmi_api_result cmi_deregister(void *reg_handle);
+enum cmi_api_result cmi_send_msg(void *message);
+
+#endif /*__CMI_API__*/
diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c
new file mode 100644
index 000000000000..a4ef63bcd32e
--- /dev/null
+++ b/sound/soc/codecs/wcd_cpe_core.c
@@ -0,0 +1,4614 @@
+/* Copyright (c) 2014-2018,2020 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/elf.h>
+#include <linux/wait.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/pm_qos.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+#include <sound/info.h>
+#include <sound/lsm_params.h>
+#include <sound/cpe_core.h>
+#include <sound/cpe_cmi.h>
+#include <sound/cpe_err.h>
+#include <soc/qcom/pm.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <sound/audio_cal_utils.h>
+#include "wcd_cpe_core.h"
+#include "wcd_cpe_services.h"
+#include "wcd_cmi_api.h"
+
+#define CMI_CMD_TIMEOUT (10 * HZ)
+#define WCD_CPE_LSM_MAX_SESSIONS 2
+#define WCD_CPE_AFE_MAX_PORTS 4
+#define AFE_SVC_EXPLICIT_PORT_START 1
+#define WCD_CPE_EC_PP_BUF_SIZE 480 /* 5 msec buffer */
+
+#define ELF_FLAG_EXECUTE (1 << 0)
+#define ELF_FLAG_WRITE (1 << 1)
+#define ELF_FLAG_READ (1 << 2)
+
+#define ELF_FLAG_RW (ELF_FLAG_READ | ELF_FLAG_WRITE)
+
+#define WCD_CPE_GRAB_LOCK(lock, name) \
+{ \
+ pr_debug("%s: %s lock acquire\n", \
+ __func__, name); \
+ mutex_lock(lock); \
+}
+
+#define WCD_CPE_REL_LOCK(lock, name) \
+{ \
+ pr_debug("%s: %s lock release\n", \
+ __func__, name); \
+ mutex_unlock(lock); \
+}
+
+#define WCD_CPE_STATE_MAX_LEN 11
+#define CPE_OFFLINE_WAIT_TIMEOUT (2 * HZ)
+#define CPE_READY_WAIT_TIMEOUT (3 * HZ)
+#define WCD_CPE_SYSFS_DIR_MAX_LENGTH 32
+
+#define CPE_ERR_IRQ_CB(core) \
+ (core->cpe_cdc_cb->cpe_err_irq_control)
+
+/*
+ * AFE output buffer size is always
+ * (sample_rate * number of bytes per sample/2*1000)
+ */
+#define AFE_OUT_BUF_SIZE(bit_width, sample_rate) \
+ (((sample_rate) * (bit_width / BITS_PER_BYTE))/(2*1000))
+
+enum afe_port_state {
+ AFE_PORT_STATE_DEINIT = 0,
+ AFE_PORT_STATE_INIT,
+ AFE_PORT_STATE_CONFIG,
+ AFE_PORT_STATE_STARTED,
+ AFE_PORT_STATE_SUSPENDED,
+};
+
+struct wcd_cmi_afe_port_data {
+ u8 port_id;
+ struct mutex afe_lock;
+ struct completion afe_cmd_complete;
+ enum afe_port_state port_state;
+ u8 cmd_result;
+ u32 mem_handle;
+};
+
+struct cpe_lsm_ids {
+ u32 module_id;
+ u32 param_id;
+};
+
+static struct wcd_cpe_core *core_d;
+static struct cpe_lsm_session
+ *lsm_sessions[WCD_CPE_LSM_MAX_SESSIONS + 1];
+struct wcd_cpe_core * (*wcd_get_cpe_core) (struct snd_soc_codec *);
+static struct wcd_cmi_afe_port_data afe_ports[WCD_CPE_AFE_MAX_PORTS + 1];
+static void wcd_cpe_svc_event_cb(const struct cpe_svc_notification *param);
+static int wcd_cpe_setup_irqs(struct wcd_cpe_core *core);
+static void wcd_cpe_cleanup_irqs(struct wcd_cpe_core *core);
+static ssize_t cpe_ftm_test_trigger(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos);
+static u32 ramdump_enable;
+static u32 cpe_ftm_test_status;
+static const struct file_operations cpe_ftm_test_trigger_fops = {
+ .open = simple_open,
+ .write = cpe_ftm_test_trigger,
+};
+
+static int wcd_cpe_afe_svc_cmd_mode(void *core_handle,
+ u8 mode);
+struct wcd_cpe_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct wcd_cpe_core *core, char *buf);
+ ssize_t (*store)(struct wcd_cpe_core *core, const char *buf,
+ ssize_t count);
+};
+
+#define WCD_CPE_ATTR(_name, _mode, _show, _store) \
+static struct wcd_cpe_attribute cpe_attr_##_name = { \
+ .attr = {.name = __stringify(_name), .mode = _mode}, \
+ .show = _show, \
+ .store = _store, \
+}
+
+#define to_wcd_cpe_attr(a) \
+ container_of((a), struct wcd_cpe_attribute, attr)
+
+#define kobj_to_cpe_core(kobj) \
+ container_of((kobj), struct wcd_cpe_core, cpe_kobj)
+
+/* wcd_cpe_lsm_session_active: check if any session is active
+ * return true if any session is active.
+ */
+static bool wcd_cpe_lsm_session_active(void)
+{
+ int index = 1;
+ bool lsm_active = false;
+
+ /* session starts from index 1 */
+ for (; index <= WCD_CPE_LSM_MAX_SESSIONS; index++) {
+ if (lsm_sessions[index] != NULL) {
+ lsm_active = true;
+ break;
+ } else {
+ lsm_active = false;
+ }
+ }
+ return lsm_active;
+}
+
+static int wcd_cpe_get_sfr_dump(struct wcd_cpe_core *core)
+{
+ struct cpe_svc_mem_segment dump_seg;
+ int rc;
+ u8 *sfr_dump;
+
+ sfr_dump = kzalloc(core->sfr_buf_size, GFP_KERNEL);
+ if (!sfr_dump) {
+ dev_err(core->dev,
+ "%s: No memory for sfr dump\n",
+ __func__);
+ goto done;
+ }
+
+ dump_seg.type = CPE_SVC_DATA_MEM;
+ dump_seg.cpe_addr = core->sfr_buf_addr;
+ dump_seg.size = core->sfr_buf_size;
+ dump_seg.data = sfr_dump;
+ dev_dbg(core->dev,
+ "%s: reading SFR from CPE, size = %zu\n",
+ __func__, core->sfr_buf_size);
+
+ rc = cpe_svc_ramdump(core->cpe_handle, &dump_seg);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(core->dev,
+ "%s: Failed to read cpe sfr_dump, err = %d\n",
+ __func__, rc);
+ goto free_sfr_dump;
+ }
+
+ dev_info(core->dev,
+ "%s: cpe_sfr = %s\n", __func__, sfr_dump);
+
+free_sfr_dump:
+ kfree(sfr_dump);
+done:
+ /* Even if SFR dump failed, do not return error */
+ return 0;
+}
+
+static int wcd_cpe_collect_ramdump(struct wcd_cpe_core *core)
+{
+ struct cpe_svc_mem_segment dump_seg;
+ int rc;
+
+ if (!core->cpe_ramdump_dev || !core->cpe_dump_v_addr ||
+ core->hw_info.dram_size == 0) {
+ dev_err(core->dev,
+ "%s: Ramdump devices not set up, size = %zu\n",
+ __func__, core->hw_info.dram_size);
+ return -EINVAL;
+ }
+
+ dump_seg.type = CPE_SVC_DATA_MEM;
+ dump_seg.cpe_addr = core->hw_info.dram_offset;
+ dump_seg.size = core->hw_info.dram_size;
+ dump_seg.data = core->cpe_dump_v_addr;
+
+ dev_dbg(core->dev,
+ "%s: Reading ramdump from CPE\n",
+ __func__);
+
+ rc = cpe_svc_ramdump(core->cpe_handle, &dump_seg);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(core->dev,
+ "%s: Failed to read CPE ramdump, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ dev_dbg(core->dev,
+ "%s: completed reading ramdump from CPE\n",
+ __func__);
+
+ core->cpe_ramdump_seg.address = (unsigned long) core->cpe_dump_addr;
+ core->cpe_ramdump_seg.size = core->hw_info.dram_size;
+ core->cpe_ramdump_seg.v_address = core->cpe_dump_v_addr;
+
+ rc = do_ramdump(core->cpe_ramdump_dev,
+ &core->cpe_ramdump_seg, 1);
+ if (rc)
+ dev_err(core->dev,
+ "%s: fail to dump cpe ram to device, err = %d\n",
+ __func__, rc);
+ return rc;
+}
+
+/* wcd_cpe_is_valid_elf_hdr: check if the ELF header is valid
+ * @core: handle to wcd_cpe_core
+ * @fw_size: size of firmware from request_firmware
+ * @ehdr: the elf header to be checked for
+ * return true if all checks pass, true if any elf check fails
+ */
+static bool wcd_cpe_is_valid_elf_hdr(struct wcd_cpe_core *core, size_t fw_size,
+ const struct elf32_hdr *ehdr)
+{
+ if (fw_size < sizeof(*ehdr)) {
+ dev_err(core->dev, "%s:Firmware too small\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+ dev_err(core->dev, "%s: Not an ELF file\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
+ dev_err(core->dev, "%s: Not a executable image\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (ehdr->e_phnum == 0) {
+ dev_err(core->dev, "%s: no segments to load\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
+ sizeof(struct elf32_hdr) > fw_size) {
+ dev_err(core->dev, "%s: Too small MDT file\n", __func__);
+ goto elf_check_fail;
+ }
+
+ return true;
+
+elf_check_fail:
+ return false;
+}
+
+/*
+ * wcd_cpe_load_each_segment: download segment to CPE
+ * @core: handle to struct wcd_cpe_core
+ * @file_idx: index of split firmware image file name
+ * @phdr: program header from metadata
+ */
+static int wcd_cpe_load_each_segment(struct wcd_cpe_core *core,
+ int file_idx, const struct elf32_phdr *phdr)
+{
+ const struct firmware *split_fw;
+ char split_fname[32];
+ int ret = 0;
+ struct cpe_svc_mem_segment *segment;
+
+ if (!core || !phdr) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ /* file size can be 0 for bss segments */
+ if (phdr->p_filesz == 0 || phdr->p_memsz == 0)
+ return 0;
+
+ segment = kzalloc(sizeof(struct cpe_svc_mem_segment), GFP_KERNEL);
+ if (!segment) {
+ dev_err(core->dev,
+ "%s: no memory for segment info, file_idx = %d\n"
+ , __func__, file_idx);
+ return -ENOMEM;
+ }
+
+ snprintf(split_fname, sizeof(split_fname), "%s.b%02d",
+ core->fname, file_idx);
+
+ ret = request_firmware(&split_fw, split_fname, core->dev);
+ if (ret) {
+ dev_err(core->dev, "firmware %s not found\n",
+ split_fname);
+ ret = -EIO;
+ goto fw_req_fail;
+ }
+
+ if (phdr->p_flags & ELF_FLAG_EXECUTE)
+ segment->type = CPE_SVC_INSTRUCTION_MEM;
+ else if (phdr->p_flags & ELF_FLAG_RW)
+ segment->type = CPE_SVC_DATA_MEM;
+ else {
+ dev_err(core->dev, "%s invalid flags 0x%x\n",
+ __func__, phdr->p_flags);
+ goto done;
+ }
+
+ if (phdr->p_filesz != split_fw->size) {
+ dev_err(core->dev,
+ "%s: %s size mismatch, phdr_size: 0x%x fw_size: 0x%zx",
+ __func__, split_fname, phdr->p_filesz, split_fw->size);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ segment->cpe_addr = phdr->p_paddr;
+ segment->size = phdr->p_filesz;
+ segment->data = (u8 *) split_fw->data;
+
+ dev_dbg(core->dev,
+ "%s: cpe segment type %s read from firmware\n", __func__,
+ (segment->type == CPE_SVC_INSTRUCTION_MEM) ?
+ "INSTRUCTION" : "DATA");
+
+ ret = cpe_svc_download_segment(core->cpe_handle, segment);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: Failed to download %s, error = %d\n",
+ __func__, split_fname, ret);
+ goto done;
+ }
+
+done:
+ release_firmware(split_fw);
+
+fw_req_fail:
+ kfree(segment);
+ return ret;
+}
+
+/*
+ * wcd_cpe_enable_cpe_clks: enable the clocks for CPE
+ * @core: handle to wcd_cpe_core
+ * @enable: flag indicating whether to enable/disable cpe clocks
+ */
+static int wcd_cpe_enable_cpe_clks(struct wcd_cpe_core *core, bool enable)
+{
+ int ret, ret1;
+
+ if (!core || !core->cpe_cdc_cb ||
+ !core->cpe_cdc_cb->cpe_clk_en) {
+ pr_err("%s: invalid handle\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret = core->cpe_cdc_cb->cdc_clk_en(core->codec, enable);
+ if (ret) {
+ dev_err(core->dev, "%s: Failed to enable RCO\n",
+ __func__);
+ return ret;
+ }
+
+ if (!enable && core->cpe_clk_ref > 0)
+ core->cpe_clk_ref--;
+
+ /*
+ * CPE clk will be enabled at the first time
+ * and be disabled at the last time.
+ */
+ if (core->cpe_clk_ref == 0) {
+ ret = core->cpe_cdc_cb->cpe_clk_en(core->codec, enable);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: cpe_clk_en() failed, err = %d\n",
+ __func__, ret);
+ goto cpe_clk_fail;
+ }
+ }
+
+ if (enable)
+ core->cpe_clk_ref++;
+
+ return 0;
+
+cpe_clk_fail:
+ /* Release the codec clk if CPE clk enable failed */
+ if (enable) {
+ ret1 = core->cpe_cdc_cb->cdc_clk_en(core->codec, !enable);
+ if (ret1)
+ dev_err(core->dev,
+ "%s: Fail to release codec clk, err = %d\n",
+ __func__, ret1);
+ }
+
+ return ret;
+}
+
+/*
+ * wcd_cpe_bus_vote_max_bw: Function to vote for max bandwidth on codec bus
+ * @core: handle to core for cpe
+ * @vote: flag to indicate enable/disable of vote
+ *
+ * This function will try to use the codec provided callback to
+ * vote/unvote for the max bandwidth of the bus that is used by
+ * the codec for register reads/writes.
+ */
+static int wcd_cpe_bus_vote_max_bw(struct wcd_cpe_core *core,
+ bool vote)
+{
+ if (!core || !core->cpe_cdc_cb) {
+ pr_err("%s: Invalid handle to %s\n",
+ __func__,
+ (!core) ? "core" : "codec callbacks");
+ return -EINVAL;
+ }
+
+ if (core->cpe_cdc_cb->bus_vote_bw) {
+ dev_dbg(core->dev, "%s: %s cdc bus max bandwidth\n",
+ __func__, vote ? "Vote" : "Unvote");
+ core->cpe_cdc_cb->bus_vote_bw(core->codec, vote);
+ }
+
+ return 0;
+}
+
+/*
+ * wcd_cpe_load_fw: Function to load the fw image
+ * @core: cpe core pointer
+ * @load_type: indicates whether to load to data section
+ * or the instruction section
+ *
+ * Parse the mdt file to look for program headers, load each
+ * split file corresponding to the program headers.
+ */
+static int wcd_cpe_load_fw(struct wcd_cpe_core *core,
+ unsigned int load_type)
+{
+
+ int ret, phdr_idx;
+ struct snd_soc_codec *codec = NULL;
+ struct wcd9xxx *wcd9xxx = NULL;
+ const struct elf32_hdr *ehdr;
+ const struct elf32_phdr *phdr;
+ const struct firmware *fw;
+ const u8 *elf_ptr;
+ char mdt_name[64];
+ bool img_dload_fail = false;
+ bool load_segment;
+
+ if (!core || !core->cpe_handle) {
+ pr_err("%s: Error CPE core %pK\n", __func__,
+ core);
+ return -EINVAL;
+ }
+ codec = core->codec;
+ wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", core->fname);
+ ret = request_firmware(&fw, mdt_name, core->dev);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev, "firmware %s not found\n", mdt_name);
+ return ret;
+ }
+
+ ehdr = (struct elf32_hdr *) fw->data;
+ if (!wcd_cpe_is_valid_elf_hdr(core, fw->size, ehdr)) {
+ dev_err(core->dev, "%s: fw mdt %s is invalid\n",
+ __func__, mdt_name);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ elf_ptr = fw->data + sizeof(*ehdr);
+
+ if (load_type == ELF_FLAG_EXECUTE) {
+ /* Reset CPE first */
+ ret = cpe_svc_reset(core->cpe_handle);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev,
+ "%s: Failed to reset CPE with error %d\n",
+ __func__, ret);
+ goto done;
+ }
+ }
+
+ dev_dbg(core->dev, "%s: start image dload, name = %s, load_type = 0x%x\n",
+ __func__, core->fname, load_type);
+
+ wcd_cpe_bus_vote_max_bw(core, true);
+
+ /* parse every program header and request corresponding firmware */
+ for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) {
+ phdr = (struct elf32_phdr *)elf_ptr;
+ load_segment = false;
+
+ dev_dbg(core->dev,
+ "index = %d, vaddr = 0x%x, paddr = 0x%x, "
+ "filesz = 0x%x, memsz = 0x%x, flags = 0x%x\n"
+ , phdr_idx, phdr->p_vaddr, phdr->p_paddr,
+ phdr->p_filesz, phdr->p_memsz, phdr->p_flags);
+
+ switch (load_type) {
+ case ELF_FLAG_EXECUTE:
+ if (phdr->p_flags & load_type)
+ load_segment = true;
+ break;
+ case ELF_FLAG_RW:
+ if (!(phdr->p_flags & ELF_FLAG_EXECUTE) &&
+ (phdr->p_flags & load_type))
+ load_segment = true;
+ break;
+ default:
+ pr_err("%s: Invalid load_type 0x%x\n",
+ __func__, load_type);
+ ret = -EINVAL;
+ goto rel_bus_vote;
+ }
+
+ if (load_segment) {
+ ret = wcd_cpe_load_each_segment(core,
+ phdr_idx, phdr);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev,
+ "Failed to load segment %d, aborting img dload\n",
+ phdr_idx);
+ img_dload_fail = true;
+ goto rel_bus_vote;
+ }
+ } else {
+ dev_dbg(core->dev,
+ "%s: skipped segment with index %d\n",
+ __func__, phdr_idx);
+ }
+
+ elf_ptr = elf_ptr + sizeof(*phdr);
+ }
+ if (load_type == ELF_FLAG_EXECUTE)
+ core->ssr_type = WCD_CPE_IMEM_DOWNLOADED;
+
+rel_bus_vote:
+ wcd_cpe_bus_vote_max_bw(core, false);
+
+done:
+ release_firmware(fw);
+ return ret;
+}
+
+/*
+ * wcd_cpe_change_online_state - mark cpe online/offline state
+ * @core: core session to mark
+ * @online: whether online of offline
+ *
+ */
+static void wcd_cpe_change_online_state(struct wcd_cpe_core *core,
+ int online)
+{
+ struct wcd_cpe_ssr_entry *ssr_entry = NULL;
+ unsigned long ret;
+
+ if (!core) {
+ pr_err("%s: Invalid core handle\n",
+ __func__);
+ return;
+ }
+
+ ssr_entry = &core->ssr_entry;
+ WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR");
+ ssr_entry->offline = !online;
+ wmb();
+ ret = xchg(&ssr_entry->offline_change, 1);
+ wake_up_interruptible(&ssr_entry->offline_poll_wait);
+ WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR");
+ pr_debug("%s: change state 0x%x offline_change 0x%x\n"
+ " core->offline 0x%x, ret = %ld\n",
+ __func__, online,
+ ssr_entry->offline_change,
+ core->ssr_entry.offline, ret);
+}
+
+/*
+ * wcd_cpe_load_fw_image: work function to load the fw image
+ * @work: work that is scheduled to perform the image loading
+ *
+ * Parse the mdt file to look for program headers, load each
+ * split file corresponding to the program headers.
+ */
+static void wcd_cpe_load_fw_image(struct work_struct *work)
+{
+ struct wcd_cpe_core *core;
+ int ret = 0;
+ core = container_of(work, struct wcd_cpe_core, load_fw_work);
+ ret = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE);
+ if (!ret)
+ wcd_cpe_change_online_state(core, 1);
+ else
+ pr_err("%s: failed to load instruction section, err = %d\n",
+ __func__, ret);
+ return;
+}
+
+/*
+ * wcd_cpe_get_core_handle: get the handle to wcd_cpe_core
+ * @codec: codec from which this handle is to be obtained
+ * Codec driver should provide a callback function to obtain
+ * handle to wcd_cpe_core during initialization of wcd_cpe_core
+ */
+void *wcd_cpe_get_core_handle(
+ struct snd_soc_codec *codec)
+{
+ struct wcd_cpe_core *core = NULL;
+
+ if (!codec) {
+ pr_err("%s: Invalid codec handle\n",
+ __func__);
+ goto done;
+ }
+
+ if (!wcd_get_cpe_core) {
+ dev_err(codec->dev,
+ "%s: codec callback not available\n",
+ __func__);
+ goto done;
+ }
+
+ core = wcd_get_cpe_core(codec);
+
+ if (!core)
+ dev_err(codec->dev,
+ "%s: handle to core not available\n",
+ __func__);
+done:
+ return core;
+}
+
+/*
+ * svass_engine_irq: threaded interrupt handler for svass engine irq
+ * @irq: interrupt number
+ * @data: data pointer passed during irq registration
+ */
+static irqreturn_t svass_engine_irq(int irq, void *data)
+{
+ struct wcd_cpe_core *core = data;
+ int ret = 0;
+
+ if (!core) {
+ pr_err("%s: Invalid data for interrupt handler\n",
+ __func__);
+ goto done;
+ }
+
+ ret = cpe_svc_process_irq(core->cpe_handle, CPE_IRQ_OUTBOX_IRQ);
+ if (IS_ERR_VALUE(ret))
+ dev_err(core->dev,
+ "%s: Error processing irq from cpe_Services\n",
+ __func__);
+done:
+ return IRQ_HANDLED;
+}
+
+/*
+ * wcd_cpe_state_read - update read status in procfs
+ * @entry: snd_info_entry
+ * @buf: buffer where the read status is updated.
+ *
+ */
+static ssize_t wcd_cpe_state_read(struct snd_info_entry *entry,
+ void *file_private_data, struct file *file,
+ char __user *buf, size_t count, loff_t pos)
+{
+ int len = 0;
+ char buffer[WCD_CPE_STATE_MAX_LEN];
+ struct wcd_cpe_core *core = NULL;
+ struct wcd_cpe_ssr_entry *ssr_entry = NULL;
+
+ core = (struct wcd_cpe_core *) entry->private_data;
+ if (!core) {
+ pr_err("%s: CPE core NULL\n", __func__);
+ return -EINVAL;
+ }
+ ssr_entry = &core->ssr_entry;
+ rmb();
+ dev_dbg(core->dev,
+ "%s: Offline 0x%x\n", __func__,
+ ssr_entry->offline);
+
+ WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR");
+ len = snprintf(buffer, sizeof(buffer), "%s\n",
+ ssr_entry->offline ? "OFFLINE" : "ONLINE");
+ WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR");
+
+ return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+/*
+ * wcd_cpe_state_poll - polls for change state
+ * @entry: snd_info_entry
+ * @wait: wait for duration for poll wait
+ *
+ */
+static unsigned int wcd_cpe_state_poll(struct snd_info_entry *entry,
+ void *private_data, struct file *file,
+ poll_table *wait)
+{
+ struct wcd_cpe_core *core = NULL;
+ struct wcd_cpe_ssr_entry *ssr_entry = NULL;
+ int ret = 0;
+
+ core = (struct wcd_cpe_core *) entry->private_data;
+ if (!core) {
+ pr_err("%s: CPE core NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ssr_entry = &core->ssr_entry;
+
+ dev_dbg(core->dev, "%s: CPE Poll wait\n",
+ __func__);
+ poll_wait(file, &ssr_entry->offline_poll_wait, wait);
+ dev_dbg(core->dev, "%s: Wake-up Poll wait\n",
+ __func__);
+ WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR");
+
+ if (xchg(&ssr_entry->offline_change, 0))
+ ret = POLLIN | POLLPRI | POLLRDNORM;
+
+ WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR");
+
+ dev_dbg(core->dev, "%s: ret (%d) from poll_wait\n",
+ __func__, ret);
+ return ret;
+}
+
+/*
+ * wcd_cpe_is_online_state - return true if card is online state
+ * @core: core offline to query
+ */
+static bool wcd_cpe_is_online_state(void *core_handle)
+{
+ struct wcd_cpe_core *core = core_handle;
+ if (core_handle) {
+ return !core->ssr_entry.offline;
+ } else {
+ pr_err("%s: Core handle NULL\n", __func__);
+ /* still return 1- offline if core ptr null */
+ return false;
+ }
+}
+
+static struct snd_info_entry_ops wcd_cpe_state_proc_ops = {
+ .read = wcd_cpe_state_read,
+ .poll = wcd_cpe_state_poll,
+};
+
+static int wcd_cpe_check_new_image(struct wcd_cpe_core *core)
+{
+ int rc = 0;
+ char temp_img_name[WCD_CPE_IMAGE_FNAME_MAX];
+
+ if (!strcmp(core->fname, core->dyn_fname) &&
+ core->ssr_type != WCD_CPE_INITIALIZED) {
+ dev_dbg(core->dev,
+ "%s: Firmware unchanged, fname = %s, ssr_type 0x%x\n",
+ __func__, core->fname, core->ssr_type);
+ goto done;
+ }
+
+ /*
+ * Different firmware name requested,
+ * Re-load the instruction section
+ */
+ strlcpy(temp_img_name, core->fname,
+ WCD_CPE_IMAGE_FNAME_MAX);
+ strlcpy(core->fname, core->dyn_fname,
+ WCD_CPE_IMAGE_FNAME_MAX);
+
+ rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE);
+ if (rc) {
+ dev_err(core->dev,
+ "%s: Failed to dload new image %s, err = %d\n",
+ __func__, core->fname, rc);
+ /* If new image download failed, revert back to old image */
+ strlcpy(core->fname, temp_img_name,
+ WCD_CPE_IMAGE_FNAME_MAX);
+ rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE);
+ if (rc)
+ dev_err(core->dev,
+ "%s: Failed to re-dload image %s, err = %d\n",
+ __func__, core->fname, rc);
+ } else {
+ dev_info(core->dev, "%s: fw changed to %s\n",
+ __func__, core->fname);
+ }
+done:
+ return rc;
+}
+
+static int wcd_cpe_enable(struct wcd_cpe_core *core,
+ bool enable)
+{
+ int ret = 0;
+
+ if (enable) {
+ /* Reset CPE first */
+ ret = cpe_svc_reset(core->cpe_handle);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev,
+ "%s: CPE Reset failed, error = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = wcd_cpe_setup_irqs(core);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: CPE IRQs setup failed, error = %d\n",
+ __func__, ret);
+ goto done;
+ }
+ ret = wcd_cpe_check_new_image(core);
+ if (ret)
+ goto fail_boot;
+
+ /* Dload data section */
+ ret = wcd_cpe_load_fw(core, ELF_FLAG_RW);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: Failed to dload data section, err = %d\n",
+ __func__, ret);
+ goto fail_boot;
+ }
+
+ ret = wcd_cpe_enable_cpe_clks(core, true);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev,
+ "%s: CPE clk enable failed, err = %d\n",
+ __func__, ret);
+ goto fail_boot;
+ }
+
+ ret = cpe_svc_boot(core->cpe_handle,
+ core->cpe_debug_mode);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev,
+ "%s: Failed to boot CPE\n",
+ __func__);
+ goto fail_boot;
+ }
+
+ /* wait for CPE to be online */
+ dev_dbg(core->dev,
+ "%s: waiting for CPE bootup\n",
+ __func__);
+
+ wait_for_completion(&core->online_compl);
+
+ dev_dbg(core->dev,
+ "%s: CPE bootup done\n",
+ __func__);
+
+ core->ssr_type = WCD_CPE_ENABLED;
+ } else {
+ if (core->ssr_type == WCD_CPE_BUS_DOWN_EVENT ||
+ core->ssr_type == WCD_CPE_SSR_EVENT) {
+ /*
+ * If this disable vote is when
+ * SSR is in progress, do not disable CPE here,
+ * instead SSR handler will control CPE.
+ */
+ wcd_cpe_enable_cpe_clks(core, false);
+ wcd_cpe_cleanup_irqs(core);
+ goto done;
+ }
+
+ ret = cpe_svc_shutdown(core->cpe_handle);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev,
+ "%s: CPE shutdown failed, error %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ wcd_cpe_enable_cpe_clks(core, false);
+ wcd_cpe_cleanup_irqs(core);
+ core->ssr_type = WCD_CPE_IMEM_DOWNLOADED;
+ }
+
+ return ret;
+
+fail_boot:
+ wcd_cpe_cleanup_irqs(core);
+
+done:
+ return ret;
+}
+
+/*
+ * wcd_cpe_boot_ssr: Load the images to CPE after ssr and bootup cpe
+ * @core: handle to the core
+ */
+static int wcd_cpe_boot_ssr(struct wcd_cpe_core *core)
+{
+ int rc = 0;
+
+ if (!core || !core->cpe_handle) {
+ pr_err("%s: Invalid handle\n", __func__);
+ rc = -EINVAL;
+ goto fail;
+ }
+ /* Load the instruction section and mark CPE as online */
+ rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE);
+ if (rc) {
+ dev_err(core->dev,
+ "%s: Failed to load instruction, err = %d\n",
+ __func__, rc);
+ goto fail;
+ } else {
+ wcd_cpe_change_online_state(core, 1);
+ }
+
+fail:
+ return rc;
+}
+
+/*
+ * wcd_cpe_clr_ready_status:
+ * Clear the value from the ready status for CPE
+ * @core: handle to the core
+ * @value: flag/bitmask that is to be cleared
+ *
+ * This function should not be invoked with ssr_lock acquired
+ */
+static void wcd_cpe_clr_ready_status(struct wcd_cpe_core *core,
+ u8 value)
+{
+ WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR");
+ core->ready_status &= ~(value);
+ dev_dbg(core->dev,
+ "%s: ready_status = 0x%x\n",
+ __func__, core->ready_status);
+ WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR");
+}
+
+/*
+ * wcd_cpe_set_and_complete:
+ * Set the ready status with the provided value and
+ * flag the completion object if ready status moves
+ * to ready to download
+ * @core: handle to the core
+ * @value: flag/bitmask that is to be set
+ */
+static void wcd_cpe_set_and_complete(struct wcd_cpe_core *core,
+ u8 value)
+{
+ WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR");
+ core->ready_status |= value;
+ if ((core->ready_status & WCD_CPE_READY_TO_DLOAD) ==
+ WCD_CPE_READY_TO_DLOAD) {
+ dev_dbg(core->dev,
+ "%s: marking ready, status = 0x%x\n",
+ __func__, core->ready_status);
+ complete(&core->ready_compl);
+ }
+ WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR");
+}
+
+
+/*
+ * wcd_cpe_ssr_work: work function to handle CPE SSR
+ * @work: work that is scheduled to perform CPE shutdown
+ * and restart
+ */
+static void wcd_cpe_ssr_work(struct work_struct *work)
+{
+
+ int rc = 0;
+ u32 irq = 0;
+ struct wcd_cpe_core *core = NULL;
+ u8 status = 0;
+
+ core = container_of(work, struct wcd_cpe_core, ssr_work);
+ if (!core) {
+ pr_err("%s: Core handle NULL\n", __func__);
+ return;
+ }
+
+ /* Obtain pm request up in case of suspend mode */
+ pm_qos_add_request(&core->pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+ pm_qos_update_request(&core->pm_qos_req,
+ msm_cpuidle_get_deep_idle_latency());
+
+ dev_dbg(core->dev,
+ "%s: CPE SSR with event %d\n",
+ __func__, core->ssr_type);
+
+ if (core->ssr_type == WCD_CPE_SSR_EVENT) {
+ if (CPE_ERR_IRQ_CB(core))
+ core->cpe_cdc_cb->cpe_err_irq_control(
+ core->codec,
+ CPE_ERR_IRQ_STATUS,
+ &status);
+ if (status & core->irq_info.cpe_fatal_irqs)
+ irq = CPE_IRQ_WDOG_BITE;
+ } else {
+ /* If bus is down, cdc reg cannot be read */
+ irq = CPE_IRQ_WDOG_BITE;
+ }
+
+ if (core->cpe_users > 0) {
+ rc = cpe_svc_process_irq(core->cpe_handle, irq);
+ if (IS_ERR_VALUE(rc))
+ /*
+ * Even if process_irq fails,
+ * wait for cpe to move to offline state
+ */
+ dev_err(core->dev,
+ "%s: irq processing failed, error = %d\n",
+ __func__, rc);
+
+ rc = wait_for_completion_timeout(&core->offline_compl,
+ CPE_OFFLINE_WAIT_TIMEOUT);
+ if (!rc) {
+ dev_err(core->dev,
+ "%s: wait for cpe offline timed out\n",
+ __func__);
+ goto err_ret;
+ }
+ if (core->ssr_type != WCD_CPE_BUS_DOWN_EVENT) {
+ wcd_cpe_get_sfr_dump(core);
+
+ /*
+ * Ramdump has to be explicitly enabled
+ * through debugfs and cannot be collected
+ * when bus is down.
+ */
+ if (ramdump_enable)
+ wcd_cpe_collect_ramdump(core);
+ }
+ } else {
+ pr_err("%s: no cpe users, mark as offline\n", __func__);
+ wcd_cpe_change_online_state(core, 0);
+ wcd_cpe_set_and_complete(core,
+ WCD_CPE_BLK_READY);
+ }
+
+ rc = wait_for_completion_timeout(&core->ready_compl,
+ CPE_READY_WAIT_TIMEOUT);
+ if (!rc) {
+ dev_err(core->dev,
+ "%s: ready to online timed out, status = %u\n",
+ __func__, core->ready_status);
+ goto err_ret;
+ }
+
+ rc = wcd_cpe_boot_ssr(core);
+
+ /* Once image are downloaded make sure all
+ * error interrupts are cleared
+ */
+ if (CPE_ERR_IRQ_CB(core))
+ core->cpe_cdc_cb->cpe_err_irq_control(core->codec,
+ CPE_ERR_IRQ_CLEAR, NULL);
+
+err_ret:
+ /* remove after default pm qos */
+ pm_qos_update_request(&core->pm_qos_req,
+ PM_QOS_DEFAULT_VALUE);
+ pm_qos_remove_request(&core->pm_qos_req);
+}
+
+/*
+ * wcd_cpe_ssr_handle: handle SSR events here.
+ * @core_handle: handle to the cpe core
+ * @event: indicates ADSP or CDSP SSR.
+ */
+int wcd_cpe_ssr_event(void *core_handle,
+ enum wcd_cpe_ssr_state_event event)
+{
+ struct wcd_cpe_core *core = core_handle;
+
+ if (!core) {
+ pr_err("%s: Invalid handle to core\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * If CPE is not even enabled, the SSR event for
+ * CPE needs to be ignored
+ */
+ if (core->ssr_type == WCD_CPE_INITIALIZED) {
+ dev_info(core->dev,
+ "%s: CPE initialized but not enabled, skip CPE ssr\n",
+ __func__);
+ return 0;
+ }
+
+ dev_dbg(core->dev,
+ "%s: Schedule ssr work, event = %d\n",
+ __func__, core->ssr_type);
+
+ switch (event) {
+ case WCD_CPE_BUS_DOWN_EVENT:
+ /*
+ * If bus down, then CPE block is also
+ * treated to be down
+ */
+ wcd_cpe_clr_ready_status(core, WCD_CPE_READY_TO_DLOAD);
+ core->ssr_type = event;
+ schedule_work(&core->ssr_work);
+ break;
+
+ case WCD_CPE_SSR_EVENT:
+ wcd_cpe_clr_ready_status(core, WCD_CPE_BLK_READY);
+ core->ssr_type = event;
+ schedule_work(&core->ssr_work);
+ break;
+
+ case WCD_CPE_BUS_UP_EVENT:
+ wcd_cpe_set_and_complete(core, WCD_CPE_BUS_READY);
+ /*
+ * In case of bus up event ssr_type will be changed
+ * to WCD_CPE_ACTIVE once CPE is online
+ */
+ break;
+
+ default:
+ dev_err(core->dev,
+ "%s: unhandled SSR event %d\n",
+ __func__, event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(wcd_cpe_ssr_event);
+
+/*
+ * svass_exception_irq: threaded irq handler for sva error interrupts
+ * @irq: interrupt number
+ * @data: data pointer passed during irq registration
+ *
+ * Once a error interrupt is received, it is not cleared, since
+ * clearing this interrupt will raise spurious interrupts unless
+ * CPE is reset.
+ */
+static irqreturn_t svass_exception_irq(int irq, void *data)
+{
+ struct wcd_cpe_core *core = data;
+ u8 status = 0;
+
+ if (!core || !CPE_ERR_IRQ_CB(core)) {
+ pr_err("%s: Invalid %s\n",
+ __func__,
+ (!core) ? "core" : "cdc control");
+ return IRQ_HANDLED;
+ }
+
+ core->cpe_cdc_cb->cpe_err_irq_control(core->codec,
+ CPE_ERR_IRQ_STATUS, &status);
+
+ while (status != 0) {
+ if (status & core->irq_info.cpe_fatal_irqs) {
+ dev_err(core->dev,
+ "%s: CPE SSR event,err_status = 0x%02x\n",
+ __func__, status);
+ wcd_cpe_ssr_event(core, WCD_CPE_SSR_EVENT);
+ /*
+ * If fatal interrupt is received,
+ * trigger SSR and stop processing
+ * further interrupts
+ */
+ break;
+ }
+ /*
+ * Mask the interrupt that was raised to
+ * avoid spurious interrupts
+ */
+ core->cpe_cdc_cb->cpe_err_irq_control(core->codec,
+ CPE_ERR_IRQ_MASK, &status);
+
+ /* Clear only the interrupt that was raised */
+ core->cpe_cdc_cb->cpe_err_irq_control(core->codec,
+ CPE_ERR_IRQ_CLEAR, &status);
+ dev_err(core->dev,
+ "%s: err_interrupt status = 0x%x\n",
+ __func__, status);
+
+ /* Read status for pending interrupts */
+ core->cpe_cdc_cb->cpe_err_irq_control(core->codec,
+ CPE_ERR_IRQ_STATUS, &status);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * wcd_cpe_cmi_afe_cb: callback called on response to afe commands
+ * @param: parameter containing the response code, etc
+ *
+ * Process the request to the command sent to CPE and wakeup the
+ * command send wait.
+ */
+static void wcd_cpe_cmi_afe_cb(const struct cmi_api_notification *param)
+{
+ struct cmi_hdr *hdr;
+ struct wcd_cmi_afe_port_data *afe_port_d;
+ u8 port_id;
+
+ if (!param) {
+ pr_err("%s: param is null\n", __func__);
+ return;
+ }
+
+ if (param->event != CMI_API_MSG) {
+ pr_err("%s: unhandled event 0x%x\n",
+ __func__, param->event);
+ return;
+ }
+
+ pr_debug("%s: param->result = %d\n",
+ __func__, param->result);
+
+ hdr = (struct cmi_hdr *) param->message;
+
+ /*
+ * for AFE cmd response, port id is
+ * stored at session id field of header
+ */
+ port_id = CMI_HDR_GET_SESSION_ID(hdr);
+ if (port_id > WCD_CPE_AFE_MAX_PORTS) {
+ pr_err("%s: invalid port_id %d\n",
+ __func__, port_id);
+ return;
+ }
+
+ afe_port_d = &(afe_ports[port_id]);
+
+ if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) {
+
+ u8 *payload = ((u8 *)param->message) + (sizeof(struct cmi_hdr));
+ u8 result = payload[0];
+ afe_port_d->cmd_result = result;
+ complete(&afe_port_d->afe_cmd_complete);
+
+ } else if (hdr->opcode == CPE_AFE_PORT_CMDRSP_SHARED_MEM_ALLOC) {
+
+ struct cpe_cmdrsp_shmem_alloc *cmdrsp_shmem_alloc =
+ (struct cpe_cmdrsp_shmem_alloc *) param->message;
+
+ if (cmdrsp_shmem_alloc->addr == 0) {
+ pr_err("%s: Failed AFE shared mem alloc\n", __func__);
+ afe_port_d->cmd_result = CMI_SHMEM_ALLOC_FAILED;
+ } else {
+ pr_debug("%s AFE shared mem addr = 0x%x\n",
+ __func__, cmdrsp_shmem_alloc->addr);
+ afe_port_d->mem_handle = cmdrsp_shmem_alloc->addr;
+ afe_port_d->cmd_result = 0;
+ }
+ complete(&afe_port_d->afe_cmd_complete);
+ }
+
+ return;
+}
+
+/*
+ * wcd_cpe_initialize_afe_port_data: Initialize all AFE ports
+ *
+ * Initialize the data for all the afe ports. Assign the
+ * afe port state to INIT state.
+ */
+static void wcd_cpe_initialize_afe_port_data(void)
+{
+ struct wcd_cmi_afe_port_data *afe_port_d;
+ int i;
+
+ for (i = 0; i <= WCD_CPE_AFE_MAX_PORTS; i++) {
+ afe_port_d = &afe_ports[i];
+ afe_port_d->port_id = i;
+ init_completion(&afe_port_d->afe_cmd_complete);
+ afe_port_d->port_state = AFE_PORT_STATE_INIT;
+ mutex_init(&afe_port_d->afe_lock);
+ }
+}
+
+/*
+ * wcd_cpe_deinitialize_afe_port_data: De-initialize all AFE ports
+ *
+ * De-Initialize the data for all the afe ports. Assign the
+ * afe port state to DEINIT state.
+ */
+static void wcd_cpe_deinitialize_afe_port_data(void)
+{
+ struct wcd_cmi_afe_port_data *afe_port_d;
+ int i;
+
+ for (i = 0; i <= WCD_CPE_AFE_MAX_PORTS; i++) {
+ afe_port_d = &afe_ports[i];
+ afe_port_d->port_state = AFE_PORT_STATE_DEINIT;
+ mutex_destroy(&afe_port_d->afe_lock);
+ }
+}
+
+/*
+ * wcd_cpe_svc_event_cb: callback from cpe services, indicating
+ * CPE is online or offline.
+ * @param: parameter / payload for event to be notified
+ */
+static void wcd_cpe_svc_event_cb(const struct cpe_svc_notification *param)
+{
+ struct snd_soc_codec *codec;
+ struct wcd_cpe_core *core;
+ struct cpe_svc_boot_event *boot_data;
+ bool active_sessions;
+
+ if (!param) {
+ pr_err("%s: Invalid event\n", __func__);
+ return;
+ }
+
+ codec = param->private_data;
+ if (!codec) {
+ pr_err("%s: Invalid handle to codec\n",
+ __func__);
+ return;
+ }
+
+ core = wcd_cpe_get_core_handle(codec);
+ if (!core) {
+ pr_err("%s: Invalid handle to core\n",
+ __func__);
+ return;
+ }
+
+ dev_dbg(core->dev,
+ "%s: event = 0x%x, ssr_type = 0x%x\n",
+ __func__, param->event, core->ssr_type);
+
+ switch (param->event) {
+ case CPE_SVC_BOOT:
+ boot_data = (struct cpe_svc_boot_event *)
+ param->payload;
+ core->sfr_buf_addr = boot_data->debug_address;
+ core->sfr_buf_size = boot_data->debug_buffer_size;
+ dev_dbg(core->dev,
+ "%s: CPE booted, sfr_addr = %d, sfr_size = %zu\n",
+ __func__, core->sfr_buf_addr,
+ core->sfr_buf_size);
+ break;
+ case CPE_SVC_ONLINE:
+ core->ssr_type = WCD_CPE_ACTIVE;
+ dev_dbg(core->dev, "%s CPE is now online\n",
+ __func__);
+ complete(&core->online_compl);
+ break;
+ case CPE_SVC_OFFLINE:
+ /*
+ * offline can happen during normal shutdown,
+ * but we are interested in offline only during
+ * SSR.
+ */
+ if (core->ssr_type != WCD_CPE_SSR_EVENT &&
+ core->ssr_type != WCD_CPE_BUS_DOWN_EVENT)
+ break;
+
+ active_sessions = wcd_cpe_lsm_session_active();
+ wcd_cpe_change_online_state(core, 0);
+ complete(&core->offline_compl);
+ dev_err(core->dev, "%s: CPE is now offline\n",
+ __func__);
+ break;
+ case CPE_SVC_CMI_CLIENTS_DEREG:
+
+ /*
+ * Only when either CPE SSR is in progress,
+ * or the bus is down, we need to mark the CPE
+ * as ready. In all other cases, this event is
+ * ignored
+ */
+ if (core->ssr_type == WCD_CPE_SSR_EVENT ||
+ core->ssr_type == WCD_CPE_BUS_DOWN_EVENT)
+ wcd_cpe_set_and_complete(core,
+ WCD_CPE_BLK_READY);
+ break;
+ default:
+ dev_err(core->dev,
+ "%s: unhandled notification\n",
+ __func__);
+ break;
+ }
+
+ return;
+}
+
+/*
+ * wcd_cpe_cleanup_irqs: free the irq resources required by cpe
+ * @core: handle the cpe core
+ *
+ * This API will free the IRQs for CPE but does not mask the
+ * CPE interrupts. If masking is needed, it has to be done
+ * explicity by caller.
+ */
+static void wcd_cpe_cleanup_irqs(struct wcd_cpe_core *core)
+{
+
+ struct snd_soc_codec *codec = core->codec;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
+
+ wcd9xxx_free_irq(core_res,
+ core->irq_info.cpe_engine_irq,
+ core);
+ wcd9xxx_free_irq(core_res,
+ core->irq_info.cpe_err_irq,
+ core);
+
+}
+
+/*
+ * wcd_cpe_setup_sva_err_intr: setup the irqs for CPE
+ * @core: handle to wcd_cpe_core
+ * All interrupts needed for CPE are acquired. If any
+ * request_irq fails, then all irqs are free'd
+ */
+static int wcd_cpe_setup_irqs(struct wcd_cpe_core *core)
+{
+ int ret;
+ struct snd_soc_codec *codec = core->codec;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
+
+ ret = wcd9xxx_request_irq(core_res,
+ core->irq_info.cpe_engine_irq,
+ svass_engine_irq, "SVASS_Engine", core);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: Failed to request svass engine irq\n",
+ __func__);
+ goto fail_engine_irq;
+ }
+
+ /* Make sure all error interrupts are cleared */
+ if (CPE_ERR_IRQ_CB(core))
+ core->cpe_cdc_cb->cpe_err_irq_control(
+ core->codec,
+ CPE_ERR_IRQ_CLEAR,
+ NULL);
+
+ /* Enable required error interrupts */
+ if (CPE_ERR_IRQ_CB(core))
+ core->cpe_cdc_cb->cpe_err_irq_control(
+ core->codec,
+ CPE_ERR_IRQ_UNMASK,
+ NULL);
+
+ ret = wcd9xxx_request_irq(core_res,
+ core->irq_info.cpe_err_irq,
+ svass_exception_irq, "SVASS_Exception", core);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: Failed to request svass err irq\n",
+ __func__);
+ goto fail_exception_irq;
+ }
+
+ return 0;
+
+fail_exception_irq:
+ wcd9xxx_free_irq(core_res,
+ core->irq_info.cpe_engine_irq, core);
+
+fail_engine_irq:
+ return ret;
+}
+
+static int wcd_cpe_get_cal_index(int32_t cal_type)
+{
+ int cal_index = -EINVAL;
+
+ if (cal_type == ULP_AFE_CAL_TYPE)
+ cal_index = WCD_CPE_LSM_CAL_AFE;
+ else if (cal_type == ULP_LSM_CAL_TYPE)
+ cal_index = WCD_CPE_LSM_CAL_LSM;
+ else if (cal_type == ULP_LSM_TOPOLOGY_ID_CAL_TYPE)
+ cal_index = WCD_CPE_LSM_CAL_TOPOLOGY_ID;
+ else
+ pr_err("%s: invalid cal_type %d\n",
+ __func__, cal_type);
+
+ return cal_index;
+}
+
+static int wcd_cpe_alloc_cal(int32_t cal_type, size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+
+ cal_index = wcd_cpe_get_cal_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: invalid caltype %d\n",
+ __func__, cal_type);
+ return -EINVAL;
+ }
+
+ ret = cal_utils_alloc_cal(data_size, data,
+ core_d->cal_data[cal_index],
+ 0, NULL);
+ if (ret < 0)
+ pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ return ret;
+}
+
+static int wcd_cpe_dealloc_cal(int32_t cal_type, size_t data_size,
+ void *data)
+{
+ int ret = 0;
+ int cal_index;
+
+ cal_index = wcd_cpe_get_cal_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: invalid caltype %d\n",
+ __func__, cal_type);
+ return -EINVAL;
+ }
+
+ ret = cal_utils_dealloc_cal(data_size, data,
+ core_d->cal_data[cal_index]);
+ if (ret < 0)
+ pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ return ret;
+}
+
+static int wcd_cpe_set_cal(int32_t cal_type, size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+
+ cal_index = wcd_cpe_get_cal_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: invalid caltype %d\n",
+ __func__, cal_type);
+ return -EINVAL;
+ }
+
+ ret = cal_utils_set_cal(data_size, data,
+ core_d->cal_data[cal_index],
+ 0, NULL);
+ if (ret < 0)
+ pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ return ret;
+}
+
+static int wcd_cpe_cal_init(struct wcd_cpe_core *core)
+{
+ int ret = 0;
+
+ struct cal_type_info cal_type_info[] = {
+ {{ULP_AFE_CAL_TYPE,
+ {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL,
+ wcd_cpe_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{ULP_LSM_CAL_TYPE,
+ {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL,
+ wcd_cpe_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{ULP_LSM_TOPOLOGY_ID_CAL_TYPE,
+ {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL,
+ wcd_cpe_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+ };
+
+ ret = cal_utils_create_cal_types(WCD_CPE_LSM_CAL_MAX,
+ core->cal_data,
+ cal_type_info);
+ if (ret < 0)
+ pr_err("%s: could not create cal type!\n",
+ __func__);
+ return ret;
+}
+
+/*
+ * wcd_cpe_enable: setup the cpe interrupts and schedule
+ * the work to download image and bootup the CPE.
+ * core: handle to cpe core structure
+ */
+static int wcd_cpe_vote(struct wcd_cpe_core *core,
+ bool enable)
+{
+ int ret = 0;
+
+ if (!core) {
+ pr_err("%s: Invalid handle to core\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ dev_dbg(core->dev,
+ "%s: enter, enable = %s, cpe_users = %u\n",
+ __func__, (enable ? "true" : "false"),
+ core->cpe_users);
+
+ if (enable) {
+ core->cpe_users++;
+ if (core->cpe_users == 1) {
+ ret = wcd_cpe_enable(core, enable);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: CPE enable failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+ } else {
+ dev_dbg(core->dev,
+ "%s: cpe already enabled, users = %u\n",
+ __func__, core->cpe_users);
+ goto done;
+ }
+ } else {
+ core->cpe_users--;
+ if (core->cpe_users == 0) {
+ ret = wcd_cpe_enable(core, enable);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: CPE disable failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+ } else {
+ dev_dbg(core->dev,
+ "%s: %u valid users on cpe\n",
+ __func__, core->cpe_users);
+ goto done;
+ }
+ }
+
+ dev_dbg(core->dev,
+ "%s: leave, enable = %s, cpe_users = %u\n",
+ __func__, (enable ? "true" : "false"),
+ core->cpe_users);
+
+done:
+ return ret;
+}
+
+static int wcd_cpe_debugfs_init(struct wcd_cpe_core *core)
+{
+ int rc = 0;
+
+ struct dentry *dir = debugfs_create_dir("wcd_cpe", NULL);
+ if (IS_ERR_OR_NULL(dir)) {
+ dir = NULL;
+ rc = -ENODEV;
+ goto err_create_dir;
+ }
+
+ if (!debugfs_create_u32("ramdump_enable", S_IRUGO | S_IWUSR,
+ dir, &ramdump_enable)) {
+ dev_err(core->dev, "%s: Failed to create debugfs node %s\n",
+ __func__, "ramdump_enable");
+ rc = -ENODEV;
+ goto err_create_entry;
+ }
+
+ if (!debugfs_create_file("cpe_ftm_test_trigger", S_IWUSR,
+ dir, core, &cpe_ftm_test_trigger_fops)) {
+ dev_err(core->dev, "%s: Failed to create debugfs node %s\n",
+ __func__, "cpe_ftm_test_trigger");
+ rc = -ENODEV;
+ goto err_create_entry;
+ }
+
+ if (!debugfs_create_u32("cpe_ftm_test_status", S_IRUGO,
+ dir, &cpe_ftm_test_status)) {
+ dev_err(core->dev, "%s: Failed to create debugfs node %s\n",
+ __func__, "cpe_ftm_test_status");
+ rc = -ENODEV;
+ goto err_create_entry;
+ }
+
+err_create_entry:
+ debugfs_remove(dir);
+
+err_create_dir:
+ return rc;
+}
+
+static ssize_t fw_name_show(struct wcd_cpe_core *core, char *buf)
+{
+ return snprintf(buf, WCD_CPE_IMAGE_FNAME_MAX, "%s",
+ core->dyn_fname);
+}
+
+static ssize_t fw_name_store(struct wcd_cpe_core *core,
+ const char *buf, ssize_t count)
+{
+ int copy_count = count;
+ const char *pos;
+
+ pos = memchr(buf, '\n', count);
+ if (pos)
+ copy_count = pos - buf;
+
+ if (copy_count > (WCD_CPE_IMAGE_FNAME_MAX - 1)) {
+ dev_err(core->dev,
+ "%s: Invalid length %d, max allowed %d\n",
+ __func__, copy_count, WCD_CPE_IMAGE_FNAME_MAX - 1);
+ return -EINVAL;
+ }
+
+ strlcpy(core->dyn_fname, buf, copy_count + 1);
+
+ return count;
+}
+
+WCD_CPE_ATTR(fw_name, 0660, fw_name_show, fw_name_store);
+
+static ssize_t wcd_cpe_sysfs_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct wcd_cpe_attribute *cpe_attr = to_wcd_cpe_attr(attr);
+ struct wcd_cpe_core *core = kobj_to_cpe_core(kobj);
+ ssize_t ret = -EINVAL;
+
+ if (core && cpe_attr->show)
+ ret = cpe_attr->show(core, buf);
+
+ return ret;
+}
+
+static ssize_t wcd_cpe_sysfs_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf,
+ size_t count)
+{
+ struct wcd_cpe_attribute *cpe_attr = to_wcd_cpe_attr(attr);
+ struct wcd_cpe_core *core = kobj_to_cpe_core(kobj);
+ ssize_t ret = -EINVAL;
+
+ if (core && cpe_attr->store)
+ ret = cpe_attr->store(core, buf, count);
+
+ return ret;
+}
+
+static const struct sysfs_ops wcd_cpe_sysfs_ops = {
+ .show = wcd_cpe_sysfs_show,
+ .store = wcd_cpe_sysfs_store,
+};
+
+static struct kobj_type wcd_cpe_ktype = {
+ .sysfs_ops = &wcd_cpe_sysfs_ops,
+};
+
+static int wcd_cpe_sysfs_init(struct wcd_cpe_core *core, int id)
+{
+ char sysfs_dir_name[WCD_CPE_SYSFS_DIR_MAX_LENGTH];
+ int rc = 0;
+
+ snprintf(sysfs_dir_name, WCD_CPE_SYSFS_DIR_MAX_LENGTH,
+ "%s%d", "wcd_cpe", id);
+
+ rc = kobject_init_and_add(&core->cpe_kobj, &wcd_cpe_ktype,
+ kernel_kobj,
+ sysfs_dir_name);
+ if (unlikely(rc)) {
+ dev_err(core->dev,
+ "%s: Failed to add kobject %s, err = %d\n",
+ __func__, sysfs_dir_name, rc);
+ goto done;
+ }
+
+ rc = sysfs_create_file(&core->cpe_kobj, &cpe_attr_fw_name.attr);
+ if (rc) {
+ dev_err(core->dev,
+ "%s: Failed to fw_name sysfs entry to %s\n",
+ __func__, sysfs_dir_name);
+ goto fail_create_file;
+ }
+
+ return 0;
+
+fail_create_file:
+ kobject_put(&core->cpe_kobj);
+done:
+ return rc;
+}
+
+static ssize_t cpe_ftm_test_trigger(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wcd_cpe_core *core = file->private_data;
+ int ret = 0;
+
+ /* Enable the clks for cpe */
+ ret = wcd_cpe_enable_cpe_clks(core, true);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev,
+ "%s: CPE clk enable failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ /* Get the CPE_STATUS */
+ ret = cpe_svc_ftm_test(core->cpe_handle, &cpe_ftm_test_status);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev,
+ "%s: CPE FTM test failed, err = %d\n",
+ __func__, ret);
+ if (ret == CPE_SVC_BUSY) {
+ cpe_ftm_test_status = 1;
+ ret = 0;
+ }
+ }
+
+ /* Disable the clks for cpe */
+ ret = wcd_cpe_enable_cpe_clks(core, false);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev,
+ "%s: CPE clk disable failed, err = %d\n",
+ __func__, ret);
+ }
+
+done:
+ if (ret < 0)
+ return ret;
+ else
+ return count;
+}
+
+static int wcd_cpe_validate_params(
+ struct snd_soc_codec *codec,
+ struct wcd_cpe_params *params)
+{
+
+ if (!codec) {
+ pr_err("%s: Invalid codec\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!params) {
+ dev_err(codec->dev,
+ "%s: No params supplied for codec %s\n",
+ __func__, codec->component.name);
+ return -EINVAL;
+ }
+
+ if (!params->codec || !params->get_cpe_core ||
+ !params->cdc_cb) {
+ dev_err(codec->dev,
+ "%s: Invalid params for codec %s\n",
+ __func__, codec->component.name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * wcd_cpe_init: Initialize CPE related structures
+ * @img_fname: filename for firmware image
+ * @codec: handle to codec requesting for image download
+ * @params: parameter structure passed from caller
+ *
+ * This API will initialize the cpe core but will not
+ * download the image or boot the cpe core.
+ */
+struct wcd_cpe_core *wcd_cpe_init(const char *img_fname,
+ struct snd_soc_codec *codec,
+ struct wcd_cpe_params *params)
+{
+ struct wcd_cpe_core *core;
+ int ret = 0;
+ struct snd_card *card = NULL;
+ struct snd_info_entry *entry = NULL;
+ char proc_name[WCD_CPE_STATE_MAX_LEN];
+ const char *cpe_name = "cpe";
+ const char *state_name = "_state";
+ const struct cpe_svc_hw_cfg *hw_info;
+ int id = 0;
+
+ if (wcd_cpe_validate_params(codec, params))
+ return NULL;
+
+ core = kzalloc(sizeof(struct wcd_cpe_core), GFP_KERNEL);
+ if (!core) {
+ dev_err(codec->dev,
+ "%s: Failed to allocate cpe core data\n",
+ __func__);
+ return NULL;
+ }
+
+ snprintf(core->fname, sizeof(core->fname), "%s", img_fname);
+ strlcpy(core->dyn_fname, core->fname, WCD_CPE_IMAGE_FNAME_MAX);
+
+ wcd_get_cpe_core = params->get_cpe_core;
+
+ core->codec = params->codec;
+ core->dev = params->codec->dev;
+ core->cpe_debug_mode = params->dbg_mode;
+
+ core->cdc_info.major_version = params->cdc_major_ver;
+ core->cdc_info.minor_version = params->cdc_minor_ver;
+ core->cdc_info.id = params->cdc_id;
+
+ core->cpe_cdc_cb = params->cdc_cb;
+
+ memcpy(&core->irq_info, &params->cdc_irq_info,
+ sizeof(core->irq_info));
+
+ INIT_WORK(&core->load_fw_work, wcd_cpe_load_fw_image);
+ INIT_WORK(&core->ssr_work, wcd_cpe_ssr_work);
+ init_completion(&core->offline_compl);
+ init_completion(&core->ready_compl);
+ init_completion(&core->online_compl);
+ init_waitqueue_head(&core->ssr_entry.offline_poll_wait);
+ mutex_init(&core->ssr_lock);
+ mutex_init(&core->session_lock);
+ core->cpe_users = 0;
+ core->cpe_clk_ref = 0;
+
+ /*
+ * By default, during probe, it is assumed that
+ * both CPE hardware block and underlying bus to codec
+ * are ready
+ */
+ core->ready_status = WCD_CPE_READY_TO_DLOAD;
+
+ core->cpe_handle = cpe_svc_initialize(NULL, &core->cdc_info,
+ params->cpe_svc_params);
+ if (!core->cpe_handle) {
+ dev_err(core->dev,
+ "%s: failed to initialize cpe services\n",
+ __func__);
+ goto fail_cpe_initialize;
+ }
+
+ core->cpe_reg_handle = cpe_svc_register(core->cpe_handle,
+ wcd_cpe_svc_event_cb,
+ CPE_SVC_ONLINE | CPE_SVC_OFFLINE |
+ CPE_SVC_BOOT |
+ CPE_SVC_CMI_CLIENTS_DEREG,
+ "codec cpe handler");
+ if (!core->cpe_reg_handle) {
+ dev_err(core->dev,
+ "%s: failed to register cpe service\n",
+ __func__);
+ goto fail_cpe_register;
+ }
+
+ card = codec->component.card->snd_card;
+ snprintf(proc_name, sizeof(proc_name), "%s%d%s", cpe_name, id,
+ state_name);
+ entry = snd_info_create_card_entry(card, proc_name,
+ card->proc_root);
+ if (entry) {
+ core->ssr_entry.entry = entry;
+ core->ssr_entry.offline = 1;
+ entry->size = WCD_CPE_STATE_MAX_LEN;
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->c.ops = &wcd_cpe_state_proc_ops;
+ entry->private_data = core;
+ ret = snd_info_register(entry);
+ if (ret < 0) {
+ dev_err(core->dev,
+ "%s: snd_info_register failed (%d)\n",
+ __func__, ret);
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ } else {
+ dev_err(core->dev,
+ "%s: Failed to create CPE SSR status entry\n",
+ __func__);
+ /*
+ * Even if SSR entry creation fails, continue
+ * with image download
+ */
+ }
+
+ core_d = core;
+ ret = wcd_cpe_cal_init(core);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev,
+ "%s: CPE calibration init failed, err = %d\n",
+ __func__, ret);
+ goto fail_cpe_reset;
+ }
+
+ wcd_cpe_debugfs_init(core);
+
+ wcd_cpe_sysfs_init(core, id);
+
+ hw_info = cpe_svc_get_hw_cfg(core->cpe_handle);
+ if (!hw_info) {
+ dev_err(core->dev,
+ "%s: hw info not available\n",
+ __func__);
+ goto schedule_dload_work;
+ } else {
+ core->hw_info.dram_offset = hw_info->DRAM_offset;
+ core->hw_info.dram_size = hw_info->DRAM_size;
+ core->hw_info.iram_offset = hw_info->IRAM_offset;
+ core->hw_info.iram_size = hw_info->IRAM_size;
+ }
+
+ /* Setup the ramdump device and buffer */
+ core->cpe_ramdump_dev = create_ramdump_device("cpe",
+ core->dev);
+ if (!core->cpe_ramdump_dev) {
+ dev_err(core->dev,
+ "%s: Failed to create ramdump device\n",
+ __func__);
+ goto schedule_dload_work;
+ }
+
+ arch_setup_dma_ops(core->dev, 0, 0, NULL, 0);
+ core->cpe_dump_v_addr = dma_alloc_coherent(core->dev,
+ core->hw_info.dram_size,
+ &core->cpe_dump_addr,
+ GFP_KERNEL);
+ if (!core->cpe_dump_v_addr) {
+ dev_err(core->dev,
+ "%s: Failed to alloc memory for cpe dump, size = %zd\n",
+ __func__, core->hw_info.dram_size);
+ goto schedule_dload_work;
+ } else {
+ memset(core->cpe_dump_v_addr, 0, core->hw_info.dram_size);
+ }
+
+schedule_dload_work:
+ core->ssr_type = WCD_CPE_INITIALIZED;
+ schedule_work(&core->load_fw_work);
+ return core;
+
+fail_cpe_reset:
+ cpe_svc_deregister(core->cpe_handle, core->cpe_reg_handle);
+
+fail_cpe_register:
+ cpe_svc_deinitialize(core->cpe_handle);
+
+fail_cpe_initialize:
+ kfree(core);
+ return NULL;
+}
+EXPORT_SYMBOL(wcd_cpe_init);
+
+/*
+ * wcd_cpe_cmi_lsm_callback: callback called from cpe services
+ * to notify command response for lsm
+ * service
+ * @param: param containing the response code and status
+ *
+ * This callback is registered with cpe services while registering
+ * the LSM service
+ */
+static void wcd_cpe_cmi_lsm_callback(const struct cmi_api_notification *param)
+{
+ struct cmi_hdr *hdr;
+ struct cpe_lsm_session *lsm_session;
+ u8 session_id;
+
+ if (!param) {
+ pr_err("%s: param is null\n", __func__);
+ return;
+ }
+
+ if (param->event != CMI_API_MSG) {
+ pr_err("%s: unhandled event 0x%x\n", __func__, param->event);
+ return;
+ }
+
+ hdr = (struct cmi_hdr *) param->message;
+ session_id = CMI_HDR_GET_SESSION_ID(hdr);
+
+ if (session_id > WCD_CPE_LSM_MAX_SESSIONS) {
+ pr_err("%s: invalid lsm session id = %d\n",
+ __func__, session_id);
+ return;
+ }
+
+ lsm_session = lsm_sessions[session_id];
+
+ if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) {
+
+ u8 *payload = ((u8 *)param->message) + (sizeof(struct cmi_hdr));
+ u8 result = payload[0];
+ lsm_session->cmd_err_code = result;
+ complete(&lsm_session->cmd_comp);
+
+ } else if (hdr->opcode == CPE_LSM_SESSION_CMDRSP_SHARED_MEM_ALLOC) {
+
+ struct cpe_cmdrsp_shmem_alloc *cmdrsp_shmem_alloc =
+ (struct cpe_cmdrsp_shmem_alloc *) param->message;
+
+ if (cmdrsp_shmem_alloc->addr == 0) {
+ pr_err("%s: Failed LSM shared mem alloc\n", __func__);
+ lsm_session->cmd_err_code = CMI_SHMEM_ALLOC_FAILED;
+
+ } else {
+
+ pr_debug("%s LSM shared mem addr = 0x%x\n",
+ __func__, cmdrsp_shmem_alloc->addr);
+ lsm_session->lsm_mem_handle = cmdrsp_shmem_alloc->addr;
+ lsm_session->cmd_err_code = 0;
+ }
+
+ complete(&lsm_session->cmd_comp);
+
+ } else if (hdr->opcode == CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2) {
+
+ struct cpe_lsm_event_detect_v2 *event_detect_v2 =
+ (struct cpe_lsm_event_detect_v2 *) param->message;
+
+ if (!lsm_session->priv_d) {
+ pr_err("%s: private data is not present\n",
+ __func__);
+ return;
+ }
+
+ pr_debug("%s: event payload, status = %u, size = %u\n",
+ __func__, event_detect_v2->detection_status,
+ event_detect_v2->size);
+
+ if (lsm_session->event_cb)
+ lsm_session->event_cb(
+ lsm_session->priv_d,
+ event_detect_v2->detection_status,
+ event_detect_v2->size,
+ event_detect_v2->payload);
+ }
+
+ return;
+}
+
+/*
+ * wcd_cpe_cmi_send_lsm_msg: send a message to lsm service
+ * @core: handle to cpe core
+ * @session: session on which to send the message
+ * @message: actual message containing header and payload
+ *
+ * Sends message to lsm service for specified session and wait
+ * for response back on the message.
+ * should be called after acquiring session specific mutex
+ */
+static int wcd_cpe_cmi_send_lsm_msg(
+ struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session,
+ void *message)
+{
+ int ret = 0;
+ struct cmi_hdr *hdr = message;
+
+ pr_debug("%s: sending message with opcode 0x%x\n",
+ __func__, hdr->opcode);
+
+ if (unlikely(!wcd_cpe_is_online_state(core))) {
+ dev_err(core->dev,
+ "%s: MSG not sent, CPE offline\n",
+ __func__);
+ goto done;
+ }
+
+ if (CMI_HDR_GET_OBM_FLAG(hdr))
+ wcd_cpe_bus_vote_max_bw(core, true);
+
+ reinit_completion(&session->cmd_comp);
+ ret = cmi_send_msg(message);
+ if (ret) {
+ pr_err("%s: msg opcode (0x%x) send failed (%d)\n",
+ __func__, hdr->opcode, ret);
+ goto rel_bus_vote;
+ }
+
+ ret = wait_for_completion_timeout(&session->cmd_comp,
+ CMI_CMD_TIMEOUT);
+ if (ret > 0) {
+ pr_debug("%s: command 0x%x, received response 0x%x\n",
+ __func__, hdr->opcode, session->cmd_err_code);
+ if (session->cmd_err_code == CMI_SHMEM_ALLOC_FAILED)
+ session->cmd_err_code = CPE_ENOMEMORY;
+ if (session->cmd_err_code > 0)
+ pr_err("%s: CPE returned error[%s]\n",
+ __func__, cpe_err_get_err_str(
+ session->cmd_err_code));
+ ret = cpe_err_get_lnx_err_code(session->cmd_err_code);
+ goto rel_bus_vote;
+ } else {
+ pr_err("%s: command (0x%x) send timed out\n",
+ __func__, hdr->opcode);
+ ret = -ETIMEDOUT;
+ goto rel_bus_vote;
+ }
+
+
+rel_bus_vote:
+
+ if (CMI_HDR_GET_OBM_FLAG(hdr))
+ wcd_cpe_bus_vote_max_bw(core, false);
+
+done:
+ return ret;
+}
+
+
+/*
+ * fill_cmi_header: fill the cmi header with specified values
+ *
+ * @hdr: header to be updated with values
+ * @session_id: session id of the header,
+ * in case of AFE service it is port_id
+ * @service_id: afe/lsm, etc
+ * @version: update the version field in header
+ * @payload_size: size of the payload following after header
+ * @opcode: opcode of the message
+ * @obm_flag: indicates if this header is for obm message
+ *
+ */
+static int fill_cmi_header(struct cmi_hdr *hdr,
+ u8 session_id, u8 service_id,
+ bool version, u8 payload_size,
+ u16 opcode, bool obm_flag)
+{
+ /* sanitize the data */
+ if (!IS_VALID_SESSION_ID(session_id) ||
+ !IS_VALID_SERVICE_ID(service_id) ||
+ !IS_VALID_PLD_SIZE(payload_size)) {
+ pr_err("Invalid header creation request\n");
+ return -EINVAL;
+ }
+
+ CMI_HDR_SET_SESSION(hdr, session_id);
+ CMI_HDR_SET_SERVICE(hdr, service_id);
+ if (version)
+ CMI_HDR_SET_VERSION(hdr, 1);
+ else
+ CMI_HDR_SET_VERSION(hdr, 0);
+
+ CMI_HDR_SET_PAYLOAD_SIZE(hdr, payload_size);
+
+ hdr->opcode = opcode;
+
+ if (obm_flag)
+ CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_OUT_BAND);
+ else
+ CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND);
+
+ return 0;
+}
+
+/*
+ * fill_lsm_cmd_header_v0_inband:
+ * Given the header, fill the header with information
+ * for lsm service, version 0 and inband message
+ * @hdr: the cmi header to be filled.
+ * @session_id: ID for the lsm session
+ * @payload_size: size for cmi message payload
+ * @opcode: opcode for cmi message
+ */
+static int fill_lsm_cmd_header_v0_inband(struct cmi_hdr *hdr,
+ u8 session_id, u8 payload_size, u16 opcode)
+{
+ return fill_cmi_header(hdr, session_id,
+ CMI_CPE_LSM_SERVICE_ID, false,
+ payload_size, opcode, false);
+}
+
+/*
+ * wcd_cpe_is_valid_lsm_session:
+ * Check session paramters to identify validity for the sesion
+ * @core: handle to cpe core
+ * @session: handle to the lsm session
+ * @func: invoking function to be printed in error logs
+ */
+static int wcd_cpe_is_valid_lsm_session(struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session,
+ const char *func)
+{
+ if (unlikely(IS_ERR_OR_NULL(core))) {
+ pr_err("%s: invalid handle to core\n",
+ func);
+ return -EINVAL;
+ }
+
+ if (unlikely(IS_ERR_OR_NULL(session))) {
+ dev_err(core->dev, "%s: invalid session\n",
+ func);
+ return -EINVAL;
+ }
+
+ if (session->id > WCD_CPE_LSM_MAX_SESSIONS) {
+ dev_err(core->dev, "%s: invalid session id (%u)\n",
+ func, session->id);
+ return -EINVAL;
+ }
+
+ dev_dbg(core->dev, "%s: session_id = %u\n",
+ func, session->id);
+ return 0;
+}
+
+static int wcd_cpe_cmd_lsm_open_tx_v2(
+ struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session)
+{
+ struct cpe_lsm_cmd_open_tx_v2 cmd_open_tx_v2;
+ struct cal_block_data *top_cal = NULL;
+ struct audio_cal_info_lsm_top *lsm_top;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session,
+ __func__);
+ if (ret)
+ return ret;
+
+ if (core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID] == NULL) {
+ dev_err(core->dev,
+ "%s: LSM_TOPOLOGY cal not allocated!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]->lock);
+ top_cal = cal_utils_get_only_cal_block(
+ core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]);
+ if (!top_cal) {
+ dev_err(core->dev,
+ "%s: Failed to get LSM TOPOLOGY cal block\n",
+ __func__);
+ ret = -EINVAL;
+ goto unlock_cal_mutex;
+ }
+
+ lsm_top = (struct audio_cal_info_lsm_top *)
+ top_cal->cal_info;
+
+ if (!lsm_top) {
+ dev_err(core->dev,
+ "%s: cal_info for LSM_TOPOLOGY not found\n",
+ __func__);
+ ret = -EINVAL;
+ goto unlock_cal_mutex;
+ }
+
+ dev_dbg(core->dev,
+ "%s: topology_id = 0x%x, acdb_id = 0x%x, app_type = 0x%x\n",
+ __func__, lsm_top->topology, lsm_top->acdb_id,
+ lsm_top->app_type);
+
+ if (lsm_top->topology == 0) {
+ dev_err(core->dev,
+ "%s: topology id not sent for app_type 0x%x\n",
+ __func__, lsm_top->app_type);
+ ret = -EINVAL;
+ goto unlock_cal_mutex;
+ }
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+ memset(&cmd_open_tx_v2, 0, sizeof(struct cpe_lsm_cmd_open_tx_v2));
+ if (fill_lsm_cmd_header_v0_inband(&cmd_open_tx_v2.hdr,
+ session->id, OPEN_V2_CMD_PAYLOAD_SIZE,
+ CPE_LSM_SESSION_CMD_OPEN_TX_V2)) {
+ ret = -EINVAL;
+ goto end_ret;
+ }
+
+ cmd_open_tx_v2.topology_id = lsm_top->topology;
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_open_tx_v2);
+ if (ret)
+ dev_err(core->dev,
+ "%s: failed to send open_tx_v2 cmd, err = %d\n",
+ __func__, ret);
+ else
+ session->is_topology_used = true;
+end_ret:
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+
+unlock_cal_mutex:
+ mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]->lock);
+ return ret;
+}
+
+/*
+ * wcd_cpe_cmd_lsm_open_tx: compose and send lsm open command
+ * @core_handle: handle to cpe core
+ * @session: session for which the command needs to be sent
+ * @app_id: application id part of the command
+ * @sample_rate: sample rate for this session
+ */
+static int wcd_cpe_cmd_lsm_open_tx(void *core_handle,
+ struct cpe_lsm_session *session,
+ u16 app_id, u16 sample_rate)
+{
+ struct cpe_lsm_cmd_open_tx cmd_open_tx;
+ struct wcd_cpe_core *core = core_handle;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session,
+ __func__);
+ if (ret)
+ return ret;
+
+ /* Try to open with topology first */
+ ret = wcd_cpe_cmd_lsm_open_tx_v2(core, session);
+ if (!ret)
+ goto done;
+
+ dev_dbg(core->dev, "%s: Try open_tx without topology\n",
+ __func__);
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+ memset(&cmd_open_tx, 0, sizeof(struct cpe_lsm_cmd_open_tx));
+ if (fill_lsm_cmd_header_v0_inband(&cmd_open_tx.hdr,
+ session->id, OPEN_CMD_PAYLOAD_SIZE,
+ CPE_LSM_SESSION_CMD_OPEN_TX)) {
+ ret = -EINVAL;
+ goto end_ret;
+ }
+
+ cmd_open_tx.app_id = app_id;
+ cmd_open_tx.sampling_rate = sample_rate;
+
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_open_tx);
+ if (ret)
+ dev_err(core->dev,
+ "%s: failed to send open_tx cmd, err = %d\n",
+ __func__, ret);
+end_ret:
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+done:
+ return ret;
+}
+
+/*
+ * wcd_cpe_cmd_close_tx: compose and send lsm close command
+ * @core_handle: handle to cpe core
+ * @session: session for which the command needs to be sent
+ */
+static int wcd_cpe_cmd_lsm_close_tx(void *core_handle,
+ struct cpe_lsm_session *session)
+{
+ struct cmi_hdr cmd_close_tx;
+ struct wcd_cpe_core *core = core_handle;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session,
+ __func__);
+ if (ret)
+ return ret;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+ memset(&cmd_close_tx, 0, sizeof(cmd_close_tx));
+ if (fill_lsm_cmd_header_v0_inband(&cmd_close_tx, session->id,
+ 0, CPE_LSM_SESSION_CMD_CLOSE_TX)) {
+ ret = -EINVAL;
+ goto end_ret;
+ }
+
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_close_tx);
+ if (ret)
+ dev_err(core->dev,
+ "%s: lsm close_tx cmd failed, err = %d\n",
+ __func__, ret);
+ else
+ session->is_topology_used = false;
+end_ret:
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+ return ret;
+}
+
+/*
+ * wcd_cpe_cmd_shmem_alloc: compose and send lsm shared
+ * memory allocation command
+ * @core_handle: handle to cpe core
+ * @session: session for which the command needs to be sent
+ * @size: size of memory to be allocated
+ */
+static int wcd_cpe_cmd_lsm_shmem_alloc(void *core_handle,
+ struct cpe_lsm_session *session,
+ u32 size)
+{
+ struct cpe_cmd_shmem_alloc cmd_shmem_alloc;
+ struct wcd_cpe_core *core = core_handle;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session,
+ __func__);
+ if (ret)
+ return ret;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+ memset(&cmd_shmem_alloc, 0, sizeof(cmd_shmem_alloc));
+ if (fill_lsm_cmd_header_v0_inband(&cmd_shmem_alloc.hdr, session->id,
+ SHMEM_ALLOC_CMD_PLD_SIZE,
+ CPE_LSM_SESSION_CMD_SHARED_MEM_ALLOC)) {
+ ret = -EINVAL;
+ goto end_ret;
+ }
+
+ cmd_shmem_alloc.size = size;
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_shmem_alloc);
+ if (ret)
+ dev_err(core->dev,
+ "%s: lsm_shmem_alloc cmd send fail, %d\n",
+ __func__, ret);
+end_ret:
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+ return ret;
+}
+
+/*
+ * wcd_cpe_cmd_lsm_shmem_dealloc: deallocate the shared memory
+ * for the specified session
+ * @core_handle: handle to cpe core
+ * @session: session for which memory needs to be deallocated.
+ */
+static int wcd_cpe_cmd_lsm_shmem_dealloc(void *core_handle,
+ struct cpe_lsm_session *session)
+{
+ struct cpe_cmd_shmem_dealloc cmd_dealloc;
+ struct wcd_cpe_core *core = core_handle;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session,
+ __func__);
+ if (ret)
+ return ret;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+ memset(&cmd_dealloc, 0, sizeof(cmd_dealloc));
+ if (fill_lsm_cmd_header_v0_inband(&cmd_dealloc.hdr, session->id,
+ SHMEM_DEALLOC_CMD_PLD_SIZE,
+ CPE_LSM_SESSION_CMD_SHARED_MEM_DEALLOC)) {
+ ret = -EINVAL;
+ goto end_ret;
+ }
+
+ cmd_dealloc.addr = session->lsm_mem_handle;
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_dealloc);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: lsm_shmem_dealloc cmd failed, rc %d\n",
+ __func__, ret);
+ goto end_ret;
+ }
+
+ memset(&session->lsm_mem_handle, 0,
+ sizeof(session->lsm_mem_handle));
+
+end_ret:
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+ return ret;
+}
+
+/*
+ * wcd_cpe_send_lsm_cal: send the calibration for lsm service
+ * from acdb to the cpe
+ * @core: handle to cpe core
+ * @session: session for which the calibration needs to be set.
+ */
+static int wcd_cpe_send_lsm_cal(
+ struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session)
+{
+
+ u8 *msg_pld;
+ struct cmi_hdr *hdr;
+ struct cal_block_data *lsm_cal = NULL;
+ void *inb_msg;
+ int rc = 0;
+
+ if (core->cal_data[WCD_CPE_LSM_CAL_LSM] == NULL) {
+ pr_err("%s: LSM cal not allocated!\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_LSM]->lock);
+ lsm_cal = cal_utils_get_only_cal_block(
+ core->cal_data[WCD_CPE_LSM_CAL_LSM]);
+ if (!lsm_cal) {
+ pr_err("%s: failed to get lsm cal block\n", __func__);
+ rc = -EINVAL;
+ goto unlock_cal_mutex;
+ }
+
+ if (lsm_cal->cal_data.size == 0) {
+ dev_dbg(core->dev, "%s: No LSM cal to send\n",
+ __func__);
+ rc = 0;
+ goto unlock_cal_mutex;
+ }
+
+ inb_msg = kzalloc(sizeof(struct cmi_hdr) + lsm_cal->cal_data.size,
+ GFP_KERNEL);
+ if (!inb_msg) {
+ pr_err("%s: no memory for lsm acdb cal\n",
+ __func__);
+ rc = -ENOMEM;
+ goto unlock_cal_mutex;
+ }
+
+ hdr = (struct cmi_hdr *) inb_msg;
+
+ rc = fill_lsm_cmd_header_v0_inband(hdr, session->id,
+ lsm_cal->cal_data.size,
+ CPE_LSM_SESSION_CMD_SET_PARAMS);
+ if (rc) {
+ pr_err("%s: invalid params for header, err = %d\n",
+ __func__, rc);
+ goto free_msg;
+ }
+
+ msg_pld = ((u8 *) inb_msg) + sizeof(struct cmi_hdr);
+ memcpy(msg_pld, lsm_cal->cal_data.kvaddr,
+ lsm_cal->cal_data.size);
+
+ rc = wcd_cpe_cmi_send_lsm_msg(core, session, inb_msg);
+ if (rc)
+ pr_err("%s: acdb lsm_params send failed, err = %d\n",
+ __func__, rc);
+
+free_msg:
+ kfree(inb_msg);
+
+unlock_cal_mutex:
+ mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_LSM]->lock);
+ return rc;
+
+}
+
+static void wcd_cpe_set_param_data(struct cpe_param_data *param_d,
+ struct cpe_lsm_ids *ids, u32 p_size,
+ u32 set_param_cmd)
+{
+ param_d->module_id = ids->module_id;
+ param_d->param_id = ids->param_id;
+
+ switch (set_param_cmd) {
+ case CPE_LSM_SESSION_CMD_SET_PARAMS_V2:
+ param_d->p_size.param_size = p_size;
+ break;
+ case CPE_LSM_SESSION_CMD_SET_PARAMS:
+ default:
+ param_d->p_size.sr.param_size =
+ (u16) p_size;
+ param_d->p_size.sr.reserved = 0;
+ break;
+ }
+}
+
+static int wcd_cpe_send_param_epd_thres(struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session,
+ void *data, struct cpe_lsm_ids *ids)
+{
+ struct snd_lsm_ep_det_thres *ep_det_data;
+ struct cpe_lsm_param_epd_thres epd_cmd;
+ struct cmi_hdr *msg_hdr = &epd_cmd.hdr;
+ struct cpe_param_data *param_d =
+ &epd_cmd.param;
+ int rc;
+
+ memset(&epd_cmd, 0, sizeof(epd_cmd));
+ ep_det_data = (struct snd_lsm_ep_det_thres *) data;
+ if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+ session->id,
+ CPE_CMD_EPD_THRES_PLD_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+ rc = -EINVAL;
+ goto err_ret;
+ }
+
+ wcd_cpe_set_param_data(param_d, ids,
+ CPE_EPD_THRES_PARAM_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+ epd_cmd.minor_version = 1;
+ epd_cmd.epd_begin = ep_det_data->epd_begin;
+ epd_cmd.epd_end = ep_det_data->epd_end;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ rc = wcd_cpe_cmi_send_lsm_msg(core, session, &epd_cmd);
+ if (unlikely(rc))
+ dev_err(core->dev,
+ "%s: set_param(EPD Threshold) failed, rc %dn",
+ __func__, rc);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+ return rc;
+}
+
+static int wcd_cpe_send_param_opmode(struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session,
+ void *data, struct cpe_lsm_ids *ids)
+{
+ struct snd_lsm_detect_mode *opmode_d;
+ struct cpe_lsm_param_opmode opmode_cmd;
+ struct cmi_hdr *msg_hdr = &opmode_cmd.hdr;
+ struct cpe_param_data *param_d =
+ &opmode_cmd.param;
+ int rc;
+
+ memset(&opmode_cmd, 0, sizeof(opmode_cmd));
+ opmode_d = (struct snd_lsm_detect_mode *) data;
+ if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+ session->id,
+ CPE_CMD_OPMODE_PLD_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+ rc = -EINVAL;
+ goto err_ret;
+ }
+
+ wcd_cpe_set_param_data(param_d, ids,
+ CPE_OPMODE_PARAM_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+ opmode_cmd.minor_version = 1;
+ if (opmode_d->mode == LSM_MODE_KEYWORD_ONLY_DETECTION)
+ opmode_cmd.mode = 1;
+ else
+ opmode_cmd.mode = 3;
+
+ if (opmode_d->detect_failure)
+ opmode_cmd.mode |= 0x04;
+
+ opmode_cmd.reserved = 0;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ rc = wcd_cpe_cmi_send_lsm_msg(core, session, &opmode_cmd);
+ if (unlikely(rc))
+ dev_err(core->dev,
+ "%s: set_param(operation_mode) failed, rc %dn",
+ __func__, rc);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+ return rc;
+}
+
+static int wcd_cpe_send_param_gain(struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session,
+ void *data, struct cpe_lsm_ids *ids)
+{
+ struct snd_lsm_gain *gain_d;
+ struct cpe_lsm_param_gain gain_cmd;
+ struct cmi_hdr *msg_hdr = &gain_cmd.hdr;
+ struct cpe_param_data *param_d =
+ &gain_cmd.param;
+ int rc;
+
+ memset(&gain_cmd, 0, sizeof(gain_cmd));
+ gain_d = (struct snd_lsm_gain *) data;
+ if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+ session->id,
+ CPE_CMD_GAIN_PLD_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+ rc = -EINVAL;
+ goto err_ret;
+ }
+
+ wcd_cpe_set_param_data(param_d, ids,
+ CPE_GAIN_PARAM_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+ gain_cmd.minor_version = 1;
+ gain_cmd.gain = gain_d->gain;
+ gain_cmd.reserved = 0;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ rc = wcd_cpe_cmi_send_lsm_msg(core, session, &gain_cmd);
+ if (unlikely(rc))
+ dev_err(core->dev,
+ "%s: set_param(lsm_gain) failed, rc %dn",
+ __func__, rc);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+ return rc;
+}
+
+static int wcd_cpe_send_param_connectport(struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session,
+ void *data, struct cpe_lsm_ids *ids, u16 port_id)
+{
+ struct cpe_lsm_param_connectport con_port_cmd;
+ struct cmi_hdr *msg_hdr = &con_port_cmd.hdr;
+ struct cpe_param_data *param_d =
+ &con_port_cmd.param;
+ int rc;
+
+ memset(&con_port_cmd, 0, sizeof(con_port_cmd));
+ if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+ session->id,
+ CPE_CMD_CONNECTPORT_PLD_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+ rc = -EINVAL;
+ goto err_ret;
+ }
+
+ wcd_cpe_set_param_data(param_d, ids,
+ CPE_CONNECTPORT_PARAM_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+ con_port_cmd.minor_version = 1;
+ con_port_cmd.afe_port_id = port_id;
+ con_port_cmd.reserved = 0;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ rc = wcd_cpe_cmi_send_lsm_msg(core, session, &con_port_cmd);
+ if (unlikely(rc))
+ dev_err(core->dev,
+ "%s: set_param(connect_port) failed, rc %dn",
+ __func__, rc);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+ return rc;
+}
+
+static int wcd_cpe_send_param_conf_levels(
+ struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session,
+ struct cpe_lsm_ids *ids)
+{
+ struct cpe_lsm_conf_level conf_level_data;
+ struct cmi_hdr *hdr = &(conf_level_data.hdr);
+ struct cpe_param_data *param_d = &(conf_level_data.param);
+ u8 pld_size = 0;
+ u8 pad_bytes = 0;
+ void *message;
+ int ret = 0;
+
+ memset(&conf_level_data, 0, sizeof(conf_level_data));
+
+ pld_size = (sizeof(struct cpe_lsm_conf_level) - sizeof(struct cmi_hdr));
+ pld_size += session->num_confidence_levels;
+ pad_bytes = ((4 - (pld_size % 4)) % 4);
+ pld_size += pad_bytes;
+
+ fill_cmi_header(hdr, session->id, CMI_CPE_LSM_SERVICE_ID,
+ false, pld_size,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2, false);
+
+ wcd_cpe_set_param_data(param_d, ids,
+ pld_size - sizeof(struct cpe_param_data),
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+ conf_level_data.num_active_models = session->num_confidence_levels;
+
+ message = kzalloc(sizeof(struct cpe_lsm_conf_level) +
+ conf_level_data.num_active_models + pad_bytes,
+ GFP_KERNEL);
+ if (!message) {
+ pr_err("%s: no memory for conf_level\n", __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(message, &conf_level_data,
+ sizeof(struct cpe_lsm_conf_level));
+ memcpy(((u8 *) message) + sizeof(struct cpe_lsm_conf_level),
+ session->conf_levels, conf_level_data.num_active_models);
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, message);
+ if (ret)
+ pr_err("%s: lsm_set_conf_levels failed, err = %d\n",
+ __func__, ret);
+ kfree(message);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+ return ret;
+}
+
+static int wcd_cpe_send_param_snd_model(struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session, struct cpe_lsm_ids *ids)
+{
+ int ret = 0;
+ struct cmi_obm_msg obm_msg;
+ struct cpe_param_data *param_d;
+
+
+ ret = fill_cmi_header(&obm_msg.hdr, session->id,
+ CMI_CPE_LSM_SERVICE_ID, 0, 20,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2, true);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: Invalid parameters, rc = %d\n",
+ __func__, ret);
+ goto err_ret;
+ }
+
+ obm_msg.pld.version = 0;
+ obm_msg.pld.size = session->snd_model_size;
+ obm_msg.pld.data_ptr.kvaddr = session->snd_model_data;
+ obm_msg.pld.mem_handle = session->lsm_mem_handle;
+
+ param_d = (struct cpe_param_data *) session->snd_model_data;
+ wcd_cpe_set_param_data(param_d, ids,
+ (session->snd_model_size - sizeof(*param_d)),
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &obm_msg);
+ if (ret)
+ dev_err(core->dev,
+ "%s: snd_model_register failed, %d\n",
+ __func__, ret);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+
+err_ret:
+ return ret;
+}
+
+static int wcd_cpe_send_param_dereg_model(
+ struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session,
+ struct cpe_lsm_ids *ids)
+{
+ struct cmi_hdr *hdr;
+ struct cpe_param_data *param_d;
+ u8 *message;
+ u32 pld_size;
+ int rc = 0;
+
+ pld_size = sizeof(*hdr) + sizeof(*param_d);
+
+ message = kzalloc(pld_size, GFP_KERNEL);
+ if (!message)
+ return -ENOMEM;
+
+ hdr = (struct cmi_hdr *) message;
+ param_d = (struct cpe_param_data *)
+ (((u8 *) message) + sizeof(*hdr));
+
+ if (fill_lsm_cmd_header_v0_inband(hdr,
+ session->id,
+ sizeof(*param_d),
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+ rc = -EINVAL;
+ goto err_ret;
+ }
+ wcd_cpe_set_param_data(param_d, ids, 0,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ rc = wcd_cpe_cmi_send_lsm_msg(core, session, message);
+ if (rc)
+ dev_err(core->dev,
+ "%s: snd_model_deregister failed, %d\n",
+ __func__, rc);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+ kfree(message);
+ return rc;
+}
+
+static int wcd_cpe_send_custom_param(
+ struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session,
+ void *data, u32 msg_size)
+{
+ u8 *msg;
+ struct cmi_hdr *hdr;
+ u8 *msg_pld;
+ int rc;
+
+ if (msg_size > CMI_INBAND_MESSAGE_SIZE) {
+ dev_err(core->dev,
+ "%s: out of band custom params not supported\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ msg = kzalloc(sizeof(*hdr) + msg_size, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = (struct cmi_hdr *) msg;
+ msg_pld = msg + sizeof(struct cmi_hdr);
+
+ if (fill_lsm_cmd_header_v0_inband(hdr,
+ session->id,
+ msg_size,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+ rc = -EINVAL;
+ goto err_ret;
+ }
+
+ memcpy(msg_pld, data, msg_size);
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ rc = wcd_cpe_cmi_send_lsm_msg(core, session, msg);
+ if (rc)
+ dev_err(core->dev,
+ "%s: custom params send failed, err = %d\n",
+ __func__, rc);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+ kfree(msg);
+ return rc;
+}
+
+static int wcd_cpe_set_one_param(void *core_handle,
+ struct cpe_lsm_session *session, struct lsm_params_info *p_info,
+ void *data, uint32_t param_type)
+{
+ struct wcd_cpe_core *core = core_handle;
+ int rc = 0;
+ struct cpe_lsm_ids ids;
+
+ memset(&ids, 0, sizeof(ids));
+ ids.module_id = p_info->module_id;
+ ids.param_id = p_info->param_id;
+
+ switch (param_type) {
+ case LSM_ENDPOINT_DETECT_THRESHOLD:
+ rc = wcd_cpe_send_param_epd_thres(core, session,
+ data, &ids);
+ break;
+ case LSM_OPERATION_MODE:
+ rc = wcd_cpe_send_param_opmode(core, session, data, &ids);
+ break;
+ case LSM_GAIN:
+ rc = wcd_cpe_send_param_gain(core, session, data, &ids);
+ break;
+ case LSM_MIN_CONFIDENCE_LEVELS:
+ rc = wcd_cpe_send_param_conf_levels(core, session, &ids);
+ break;
+ case LSM_REG_SND_MODEL:
+ rc = wcd_cpe_send_param_snd_model(core, session, &ids);
+ break;
+ case LSM_DEREG_SND_MODEL:
+ rc = wcd_cpe_send_param_dereg_model(core, session, &ids);
+ break;
+ case LSM_CUSTOM_PARAMS:
+ rc = wcd_cpe_send_custom_param(core, session,
+ data, p_info->param_size);
+ break;
+ default:
+ pr_err("%s: wrong param_type 0x%x\n",
+ __func__, param_type);
+ }
+
+ if (rc)
+ dev_err(core->dev,
+ "%s: send_param(%d) failed, err %d\n",
+ __func__, param_type, rc);
+ return rc;
+}
+
+/*
+ * wcd_cpe_lsm_set_params: set the parameters for lsm service
+ * @core: handle to cpe core
+ * @session: session for which the parameters are to be set
+ * @detect_mode: mode for detection
+ * @detect_failure: flag indicating failure detection enabled/disabled
+ *
+ */
+static int wcd_cpe_lsm_set_params(struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session,
+ enum lsm_detection_mode detect_mode, bool detect_failure)
+{
+ struct cpe_lsm_ids ids;
+ struct snd_lsm_detect_mode det_mode;
+
+ int ret = 0;
+
+ /* Send lsm calibration */
+ ret = wcd_cpe_send_lsm_cal(core, session);
+ if (ret) {
+ pr_err("%s: fail to sent acdb cal, err = %d",
+ __func__, ret);
+ goto err_ret;
+ }
+
+ /* Send operation mode */
+ ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP;
+ ids.param_id = CPE_LSM_PARAM_ID_OPERATION_MODE;
+ det_mode.mode = detect_mode;
+ det_mode.detect_failure = detect_failure;
+ ret = wcd_cpe_send_param_opmode(core, session,
+ &det_mode, &ids);
+ if (ret)
+ dev_err(core->dev,
+ "%s: Failed to set opmode, err=%d\n",
+ __func__, ret);
+
+err_ret:
+ return ret;
+}
+
+static int wcd_cpe_lsm_set_data(void *core_handle,
+ struct cpe_lsm_session *session,
+ enum lsm_detection_mode detect_mode,
+ bool detect_failure)
+{
+ struct wcd_cpe_core *core = core_handle;
+ struct cpe_lsm_ids ids;
+ int ret = 0;
+
+ if (session->num_confidence_levels > 0) {
+ ret = wcd_cpe_lsm_set_params(core, session, detect_mode,
+ detect_failure);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: lsm set params failed, rc = %d\n",
+ __func__, ret);
+ goto err_ret;
+ }
+
+ ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP;
+ ids.param_id = CPE_LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS;
+ ret = wcd_cpe_send_param_conf_levels(core, session, &ids);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: lsm confidence levels failed, rc = %d\n",
+ __func__, ret);
+ goto err_ret;
+ }
+ } else {
+ dev_dbg(core->dev,
+ "%s: no conf levels to set\n",
+ __func__);
+ }
+
+err_ret:
+ return ret;
+}
+
+/*
+ * wcd_cpe_lsm_reg_snd_model: register the sound model for listen
+ * @session: session for which to register the sound model
+ * @detect_mode: detection mode, user dependent/independent
+ * @detect_failure: flag to indicate if failure detection is enabled
+ *
+ * The memory required for sound model should be pre-allocated on CPE
+ * before this function is invoked.
+ */
+static int wcd_cpe_lsm_reg_snd_model(void *core_handle,
+ struct cpe_lsm_session *session,
+ enum lsm_detection_mode detect_mode,
+ bool detect_failure)
+{
+ int ret = 0;
+ struct cmi_obm_msg obm_msg;
+ struct wcd_cpe_core *core = core_handle;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session,
+ __func__);
+ if (ret)
+ return ret;
+
+ ret = wcd_cpe_lsm_set_data(core_handle, session,
+ detect_mode, detect_failure);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: fail to set lsm data, err = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+ ret = fill_cmi_header(&obm_msg.hdr, session->id,
+ CMI_CPE_LSM_SERVICE_ID, 0, 20,
+ CPE_LSM_SESSION_CMD_REGISTER_SOUND_MODEL, true);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: Invalid parameters, rc = %d\n",
+ __func__, ret);
+ goto err_ret;
+ }
+
+ obm_msg.pld.version = 0;
+ obm_msg.pld.size = session->snd_model_size;
+ obm_msg.pld.data_ptr.kvaddr = session->snd_model_data;
+ obm_msg.pld.mem_handle = session->lsm_mem_handle;
+
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &obm_msg);
+ if (ret)
+ dev_err(core->dev,
+ "%s: snd_model_register failed, %d\n",
+ __func__, ret);
+err_ret:
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+ return ret;
+}
+
+/*
+ * wcd_cpe_lsm_dereg_snd_model: deregister the sound model for listen
+ * @core_handle: handle to cpe core
+ * @session: session for which to deregister the sound model
+ *
+ */
+static int wcd_cpe_lsm_dereg_snd_model(void *core_handle,
+ struct cpe_lsm_session *session)
+{
+ struct cmi_hdr cmd_dereg_snd_model;
+ struct wcd_cpe_core *core = core_handle;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session,
+ __func__);
+ if (ret)
+ return ret;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+ memset(&cmd_dereg_snd_model, 0, sizeof(cmd_dereg_snd_model));
+ if (fill_lsm_cmd_header_v0_inband(&cmd_dereg_snd_model, session->id,
+ 0, CPE_LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL)) {
+ ret = -EINVAL;
+ goto end_ret;
+ }
+
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_dereg_snd_model);
+ if (ret)
+ dev_err(core->dev,
+ "%s: failed to send dereg_snd_model cmd\n",
+ __func__);
+end_ret:
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+ return ret;
+}
+
+/*
+ * wcd_cpe_lsm_get_afe_out_port_id: get afe output port id
+ * @core_handle: handle to the CPE core
+ * @session: session for which port id needs to get
+ */
+static int wcd_cpe_lsm_get_afe_out_port_id(void *core_handle,
+ struct cpe_lsm_session *session)
+{
+ struct wcd_cpe_core *core = core_handle;
+ struct snd_soc_codec *codec;
+ int rc = 0;
+
+ if (!core || !core->codec) {
+ pr_err("%s: Invalid handle to %s\n",
+ __func__,
+ (!core) ? "core" : "codec");
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (!session) {
+ dev_err(core->dev, "%s: Invalid session\n",
+ __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (!core->cpe_cdc_cb ||
+ !core->cpe_cdc_cb->get_afe_out_port_id) {
+ session->afe_out_port_id = WCD_CPE_AFE_OUT_PORT_2;
+ dev_dbg(core->dev,
+ "%s: callback not defined, default port_id = %d\n",
+ __func__, session->afe_out_port_id);
+ goto done;
+ }
+
+ codec = core->codec;
+ rc = core->cpe_cdc_cb->get_afe_out_port_id(codec,
+ &session->afe_out_port_id);
+ if (rc) {
+ dev_err(core->dev,
+ "%s: failed to get port id, err = %d\n",
+ __func__, rc);
+ goto done;
+ }
+ dev_dbg(core->dev, "%s: port_id: %d\n", __func__,
+ session->afe_out_port_id);
+
+done:
+ return rc;
+}
+
+/*
+ * wcd_cpe_cmd_lsm_start: send the start command to lsm
+ * @core_handle: handle to the CPE core
+ * @session: session for which start command to be sent
+ *
+ */
+static int wcd_cpe_cmd_lsm_start(void *core_handle,
+ struct cpe_lsm_session *session)
+{
+ struct cmi_hdr cmd_lsm_start;
+ struct wcd_cpe_core *core = core_handle;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session,
+ __func__);
+ if (ret)
+ return ret;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+ memset(&cmd_lsm_start, 0, sizeof(struct cmi_hdr));
+ if (fill_lsm_cmd_header_v0_inband(&cmd_lsm_start, session->id, 0,
+ CPE_LSM_SESSION_CMD_START)) {
+ ret = -EINVAL;
+ goto end_ret;
+ }
+
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_lsm_start);
+ if (ret)
+ dev_err(core->dev, "failed to send lsm_start cmd\n");
+end_ret:
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+ return ret;
+}
+
+/*
+ * wcd_cpe_cmd_lsm_stop: send the stop command for LSM service
+ * @core_handle: handle to the cpe core
+ * @session: session for which stop command to be sent
+ *
+ */
+static int wcd_cpe_cmd_lsm_stop(void *core_handle,
+ struct cpe_lsm_session *session)
+{
+ struct cmi_hdr cmd_lsm_stop;
+ struct wcd_cpe_core *core = core_handle;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session,
+ __func__);
+ if (ret)
+ return ret;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+ memset(&cmd_lsm_stop, 0, sizeof(struct cmi_hdr));
+ if (fill_lsm_cmd_header_v0_inband(&cmd_lsm_stop, session->id, 0,
+ CPE_LSM_SESSION_CMD_STOP)) {
+ ret = -EINVAL;
+ goto end_ret;
+ }
+
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_lsm_stop);
+ if (ret)
+ dev_err(core->dev,
+ "%s: failed to send lsm_stop cmd\n",
+ __func__);
+end_ret:
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+ return ret;
+
+}
+
+/*
+ * wcd_cpe_alloc_lsm_session: allocate a lsm session
+ * @core: handle to wcd_cpe_core
+ * @lsm_priv_d: lsm private data
+ */
+static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session(
+ void *core_handle, void *client_data,
+ void (*event_cb) (void *, u8, u8, u8 *))
+{
+ struct cpe_lsm_session *session;
+ int i, session_id = -1;
+ struct wcd_cpe_core *core = core_handle;
+ bool afe_register_service = false;
+ int ret = 0;
+
+ /*
+ * Even if multiple listen sessions can be
+ * allocated, the AFE service registration
+ * should be done only once as CPE can only
+ * have one instance of AFE service.
+ *
+ * If this is the first session to be allocated,
+ * only then register the afe service.
+ */
+ WCD_CPE_GRAB_LOCK(&core->session_lock, "session_lock");
+ if (!wcd_cpe_lsm_session_active())
+ afe_register_service = true;
+
+ for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++) {
+ if (!lsm_sessions[i]) {
+ session_id = i;
+ break;
+ }
+ }
+
+ if (session_id < 0) {
+ dev_err(core->dev,
+ "%s: max allowed sessions already allocated\n",
+ __func__);
+ WCD_CPE_REL_LOCK(&core->session_lock, "session_lock");
+ return NULL;
+ }
+
+ ret = wcd_cpe_vote(core, true);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: Failed to enable cpe, err = %d\n",
+ __func__, ret);
+ WCD_CPE_REL_LOCK(&core->session_lock, "session_lock");
+ return NULL;
+ }
+
+ session = kzalloc(sizeof(struct cpe_lsm_session), GFP_KERNEL);
+ if (!session) {
+ dev_err(core->dev,
+ "%s: failed to allocate session, no memory\n",
+ __func__);
+ goto err_session_alloc;
+ }
+
+ session->id = session_id;
+ session->event_cb = event_cb;
+ session->cmi_reg_handle = cmi_register(wcd_cpe_cmi_lsm_callback,
+ CMI_CPE_LSM_SERVICE_ID);
+ if (!session->cmi_reg_handle) {
+ dev_err(core->dev,
+ "%s: Failed to register LSM service with CMI\n",
+ __func__);
+ goto err_ret;
+ }
+ session->priv_d = client_data;
+ mutex_init(&session->lsm_lock);
+ if (afe_register_service) {
+ /* Register for AFE Service */
+ core->cmi_afe_handle = cmi_register(wcd_cpe_cmi_afe_cb,
+ CMI_CPE_AFE_SERVICE_ID);
+ wcd_cpe_initialize_afe_port_data();
+ if (!core->cmi_afe_handle) {
+ dev_err(core->dev,
+ "%s: Failed to register AFE service with CMI\n",
+ __func__);
+ goto err_afe_svc_reg;
+ }
+
+ /* Once AFE service is registered, send the mode command */
+ ret = wcd_cpe_afe_svc_cmd_mode(core,
+ AFE_SVC_EXPLICIT_PORT_START);
+ if (ret)
+ goto err_afe_mode_cmd;
+ }
+
+ session->lsm_mem_handle = 0;
+ init_completion(&session->cmd_comp);
+
+ lsm_sessions[session_id] = session;
+
+ WCD_CPE_REL_LOCK(&core->session_lock, "session_lock");
+ return session;
+
+err_afe_mode_cmd:
+ cmi_deregister(core->cmi_afe_handle);
+
+err_afe_svc_reg:
+ cmi_deregister(session->cmi_reg_handle);
+ mutex_destroy(&session->lsm_lock);
+
+err_ret:
+ kfree(session);
+
+err_session_alloc:
+ wcd_cpe_vote(core, false);
+ WCD_CPE_REL_LOCK(&core->session_lock, "session_lock");
+ return NULL;
+}
+
+/*
+ * wcd_cpe_lsm_config_lab_latency: send lab latency value
+ * @core: handle to wcd_cpe_core
+ * @session: lsm session
+ * @latency: the value of latency for lab setup in msec
+ */
+static int wcd_cpe_lsm_config_lab_latency(
+ struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session,
+ u32 latency)
+{
+ int ret = 0, pld_size = CPE_PARAM_LSM_LAB_LATENCY_SIZE;
+ struct cpe_lsm_lab_latency_config cpe_lab_latency;
+ struct cpe_lsm_lab_config *lab_lat = &cpe_lab_latency.latency_cfg;
+ struct cpe_param_data *param_d = &lab_lat->param;
+ struct cpe_lsm_ids ids;
+
+ if (fill_lsm_cmd_header_v0_inband(&cpe_lab_latency.hdr, session->id,
+ (u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+ pr_err("%s: Failed to create header\n", __func__);
+ return -EINVAL;
+ }
+ if (latency == 0x00 || latency > WCD_CPE_LAB_MAX_LATENCY) {
+ pr_err("%s: Invalid latency %u\n",
+ __func__, latency);
+ return -EINVAL;
+ } else {
+ lab_lat->latency = latency;
+ }
+
+ lab_lat->minor_ver = 1;
+ ids.module_id = CPE_LSM_MODULE_ID_LAB;
+ ids.param_id = CPE_LSM_PARAM_ID_LAB_CONFIG;
+ wcd_cpe_set_param_data(param_d, &ids,
+ PARAM_SIZE_LSM_LATENCY_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+ pr_debug("%s: Module 0x%x Param 0x%x size %zu pld_size 0x%x\n",
+ __func__, lab_lat->param.module_id,
+ lab_lat->param.param_id, PARAM_SIZE_LSM_LATENCY_SIZE,
+ pld_size);
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cpe_lab_latency);
+ if (ret != 0)
+ pr_err("%s: lsm_set_params failed, error = %d\n",
+ __func__, ret);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+ return ret;
+}
+
+/*
+ * wcd_cpe_lsm_lab_control: enable/disable lab
+ * @core: handle to wcd_cpe_core
+ * @session: lsm session
+ * @enable: Indicates whether to enable / disable lab
+ */
+static int wcd_cpe_lsm_lab_control(
+ void *core_handle,
+ struct cpe_lsm_session *session,
+ bool enable)
+{
+ struct wcd_cpe_core *core = core_handle;
+ int ret = 0, pld_size = CPE_PARAM_SIZE_LSM_LAB_CONTROL;
+ struct cpe_lsm_control_lab cpe_lab_enable;
+ struct cpe_lsm_lab_enable *lab_enable = &cpe_lab_enable.lab_enable;
+ struct cpe_param_data *param_d = &lab_enable->param;
+ struct cpe_lsm_ids ids;
+
+ pr_debug("%s: enter payload_size = %d Enable %d\n",
+ __func__, pld_size, enable);
+
+ memset(&cpe_lab_enable, 0, sizeof(cpe_lab_enable));
+
+ if (fill_lsm_cmd_header_v0_inband(&cpe_lab_enable.hdr, session->id,
+ (u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+ return -EINVAL;
+ }
+ if (enable == true)
+ lab_enable->enable = 1;
+ else
+ lab_enable->enable = 0;
+
+ ids.module_id = CPE_LSM_MODULE_ID_LAB;
+ ids.param_id = CPE_LSM_PARAM_ID_LAB_ENABLE;
+ wcd_cpe_set_param_data(param_d, &ids,
+ PARAM_SIZE_LSM_CONTROL_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+ pr_debug("%s: Module 0x%x, Param 0x%x size %zu pld_size 0x%x\n",
+ __func__, lab_enable->param.module_id,
+ lab_enable->param.param_id, PARAM_SIZE_LSM_CONTROL_SIZE,
+ pld_size);
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cpe_lab_enable);
+ if (ret != 0) {
+ pr_err("%s: lsm_set_params failed, error = %d\n",
+ __func__, ret);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+ goto done;
+ }
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+
+ if (lab_enable->enable)
+ ret = wcd_cpe_lsm_config_lab_latency(core, session,
+ WCD_CPE_LAB_MAX_LATENCY);
+done:
+ return ret;
+}
+
+/*
+ * wcd_cpe_lsm_eob: stop lab
+ * @core: handle to wcd_cpe_core
+ * @session: lsm session to be deallocated
+ */
+static int wcd_cpe_lsm_eob(
+ struct wcd_cpe_core *core,
+ struct cpe_lsm_session *session)
+{
+ int ret = 0;
+ struct cmi_hdr lab_eob;
+
+ if (fill_lsm_cmd_header_v0_inband(&lab_eob, session->id,
+ 0, CPE_LSM_SESSION_CMD_EOB)) {
+ return -EINVAL;
+ }
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &lab_eob);
+ if (ret != 0)
+ pr_err("%s: lsm_set_params failed\n", __func__);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+
+ return ret;
+}
+
+/*
+ * wcd_cpe_dealloc_lsm_session: deallocate lsm session
+ * @core: handle to wcd_cpe_core
+ * @session: lsm session to be deallocated
+ */
+static int wcd_cpe_dealloc_lsm_session(void *core_handle,
+ struct cpe_lsm_session *session)
+{
+ struct wcd_cpe_core *core = core_handle;
+ int ret = 0;
+
+ WCD_CPE_GRAB_LOCK(&core->session_lock, "session_lock");
+ if (!session) {
+ dev_err(core->dev,
+ "%s: Invalid lsm session\n", __func__);
+ WCD_CPE_REL_LOCK(&core->session_lock, "session_lock");
+ return -EINVAL;
+ }
+
+ dev_dbg(core->dev, "%s: session %d being deallocated\n",
+ __func__, session->id);
+ if (session->id > WCD_CPE_LSM_MAX_SESSIONS) {
+ dev_err(core->dev,
+ "%s: Wrong session id %d max allowed = %d\n",
+ __func__, session->id,
+ WCD_CPE_LSM_MAX_SESSIONS);
+ WCD_CPE_REL_LOCK(&core->session_lock, "session_lock");
+ return -EINVAL;
+ }
+
+ cmi_deregister(session->cmi_reg_handle);
+ mutex_destroy(&session->lsm_lock);
+ lsm_sessions[session->id] = NULL;
+ kfree(session);
+
+ if (!wcd_cpe_lsm_session_active()) {
+ cmi_deregister(core->cmi_afe_handle);
+ core->cmi_afe_handle = NULL;
+ wcd_cpe_deinitialize_afe_port_data();
+ }
+
+ ret = wcd_cpe_vote(core, false);
+ if (ret)
+ dev_dbg(core->dev,
+ "%s: Failed to un-vote cpe, err = %d\n",
+ __func__, ret);
+
+ WCD_CPE_REL_LOCK(&core->session_lock, "session_lock");
+ return ret;
+}
+
+static int wcd_cpe_lab_ch_setup(void *core_handle,
+ struct cpe_lsm_session *session,
+ enum wcd_cpe_event event)
+{
+ struct wcd_cpe_core *core = core_handle;
+ struct snd_soc_codec *codec;
+ int rc = 0;
+ u8 cpe_intr_bits;
+
+ if (!core || !core->codec) {
+ pr_err("%s: Invalid handle to %s\n",
+ __func__,
+ (!core) ? "core" : "codec");
+ rc = EINVAL;
+ goto done;
+ }
+
+ if (!core->cpe_cdc_cb ||
+ !core->cpe_cdc_cb->cdc_ext_clk ||
+ !core->cpe_cdc_cb->lab_cdc_ch_ctl) {
+ dev_err(core->dev,
+ "%s: Invalid codec callbacks\n",
+ __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ codec = core->codec;
+ dev_dbg(core->dev,
+ "%s: event = 0x%x\n",
+ __func__, event);
+
+ switch (event) {
+ case WCD_CPE_PRE_ENABLE:
+ rc = core->cpe_cdc_cb->cdc_ext_clk(codec, true, false);
+ if (rc) {
+ dev_err(core->dev,
+ "%s: failed to enable cdc clk, err = %d\n",
+ __func__, rc);
+ goto done;
+ }
+
+ rc = core->cpe_cdc_cb->lab_cdc_ch_ctl(codec,
+ true);
+ if (rc) {
+ dev_err(core->dev,
+ "%s: failed to enable cdc port, err = %d\n",
+ __func__, rc);
+ rc = core->cpe_cdc_cb->cdc_ext_clk(codec, false, false);
+ goto done;
+ }
+
+ break;
+
+ case WCD_CPE_POST_ENABLE:
+ rc = cpe_svc_toggle_lab(core->cpe_handle, true);
+ if (rc)
+ dev_err(core->dev,
+ "%s: Failed to enable lab\n", __func__);
+ break;
+
+ case WCD_CPE_PRE_DISABLE:
+ /*
+ * Mask the non-fatal interrupts in CPE as they will
+ * be generated during lab teardown and may flood.
+ */
+ cpe_intr_bits = ~(core->irq_info.cpe_fatal_irqs & 0xFF);
+ if (CPE_ERR_IRQ_CB(core))
+ core->cpe_cdc_cb->cpe_err_irq_control(
+ core->codec,
+ CPE_ERR_IRQ_MASK,
+ &cpe_intr_bits);
+
+ rc = core->cpe_cdc_cb->lab_cdc_ch_ctl(codec,
+ false);
+ if (rc)
+ dev_err(core->dev,
+ "%s: failed to disable cdc port, err = %d\n",
+ __func__, rc);
+ break;
+
+ case WCD_CPE_POST_DISABLE:
+ rc = wcd_cpe_lsm_eob(core, session);
+ if (rc)
+ dev_err(core->dev,
+ "%s: eob send failed, err = %d\n",
+ __func__, rc);
+
+ /* Continue teardown even if eob failed */
+ rc = cpe_svc_toggle_lab(core->cpe_handle, false);
+ if (rc)
+ dev_err(core->dev,
+ "%s: Failed to disable lab\n", __func__);
+
+ /* Continue with disabling even if toggle lab fails */
+ rc = core->cpe_cdc_cb->cdc_ext_clk(codec, false, false);
+ if (rc)
+ dev_err(core->dev,
+ "%s: failed to disable cdc clk, err = %d\n",
+ __func__, rc);
+
+ /* Unmask non-fatal CPE interrupts */
+ cpe_intr_bits = ~(core->irq_info.cpe_fatal_irqs & 0xFF);
+ if (CPE_ERR_IRQ_CB(core))
+ core->cpe_cdc_cb->cpe_err_irq_control(
+ core->codec,
+ CPE_ERR_IRQ_UNMASK,
+ &cpe_intr_bits);
+ break;
+
+ default:
+ dev_err(core->dev,
+ "%s: Invalid event 0x%x\n",
+ __func__, event);
+ rc = -EINVAL;
+ break;
+ }
+
+done:
+ return rc;
+}
+
+static int wcd_cpe_lsm_set_fmt_cfg(void *core_handle,
+ struct cpe_lsm_session *session)
+{
+ int ret;
+ struct cpe_lsm_output_format_cfg out_fmt_cfg;
+ struct wcd_cpe_core *core = core_handle;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session, __func__);
+ if (ret)
+ goto done;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+ memset(&out_fmt_cfg, 0, sizeof(out_fmt_cfg));
+ if (fill_lsm_cmd_header_v0_inband(&out_fmt_cfg.hdr,
+ session->id, OUT_FMT_CFG_CMD_PAYLOAD_SIZE,
+ CPE_LSM_SESSION_CMD_TX_BUFF_OUTPUT_CONFIG)) {
+ ret = -EINVAL;
+ goto err_ret;
+ }
+
+ out_fmt_cfg.format = session->out_fmt_cfg.format;
+ out_fmt_cfg.packing = session->out_fmt_cfg.pack_mode;
+ out_fmt_cfg.data_path_events = session->out_fmt_cfg.data_path_events;
+
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &out_fmt_cfg);
+ if (ret)
+ dev_err(core->dev,
+ "%s: lsm_set_output_format_cfg failed, err = %d\n",
+ __func__, ret);
+
+err_ret:
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+done:
+ return ret;
+}
+
+static void wcd_cpe_snd_model_offset(void *core_handle,
+ struct cpe_lsm_session *session, size_t *offset)
+{
+ *offset = sizeof(struct cpe_param_data);
+}
+
+static int wcd_cpe_lsm_set_media_fmt_params(void *core_handle,
+ struct cpe_lsm_session *session,
+ struct lsm_hw_params *param)
+{
+ struct cpe_lsm_media_fmt_param media_fmt;
+ struct cmi_hdr *msg_hdr = &media_fmt.hdr;
+ struct wcd_cpe_core *core = core_handle;
+ struct cpe_param_data *param_d = &media_fmt.param;
+ struct cpe_lsm_ids ids;
+ int ret;
+
+ memset(&media_fmt, 0, sizeof(media_fmt));
+ if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+ session->id,
+ CPE_MEDIA_FMT_PLD_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memset(&ids, 0, sizeof(ids));
+ ids.module_id = CPE_LSM_MODULE_FRAMEWORK;
+ ids.param_id = CPE_LSM_PARAM_ID_MEDIA_FMT;
+
+ wcd_cpe_set_param_data(param_d, &ids, CPE_MEDIA_FMT_PARAM_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+ media_fmt.minor_version = 1;
+ media_fmt.sample_rate = param->sample_rate;
+ media_fmt.num_channels = param->num_chs;
+ media_fmt.bit_width = param->bit_width;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &media_fmt);
+ if (ret)
+ dev_err(core->dev,
+ "%s: Set_param(media_format) failed, err=%d\n",
+ __func__, ret);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+done:
+ return ret;
+}
+
+static int wcd_cpe_lsm_set_port(void *core_handle,
+ struct cpe_lsm_session *session, void *data)
+{
+ u32 port_id;
+ int ret;
+ struct cpe_lsm_ids ids;
+ struct wcd_cpe_core *core = core_handle;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session, __func__);
+ if (ret)
+ goto done;
+
+ if (!data) {
+ dev_err(core->dev, "%s: data is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ port_id = *(u32 *)data;
+ dev_dbg(core->dev, "%s: port_id: %d\n", __func__, port_id);
+
+ memset(&ids, 0, sizeof(ids));
+ ids.module_id = LSM_MODULE_ID_FRAMEWORK;
+ ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT;
+
+ ret = wcd_cpe_send_param_connectport(core, session, NULL,
+ &ids, port_id);
+ if (ret)
+ dev_err(core->dev,
+ "%s: send_param_connectport failed, err %d\n",
+ __func__, ret);
+done:
+ return ret;
+}
+
+/*
+ * wcd_cpe_get_lsm_ops: register lsm driver to codec
+ * @lsm_ops: structure with lsm callbacks
+ * @codec: codec to which this lsm driver is registered to
+ */
+int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops)
+{
+ lsm_ops->lsm_alloc_session = wcd_cpe_alloc_lsm_session;
+ lsm_ops->lsm_dealloc_session = wcd_cpe_dealloc_lsm_session;
+ lsm_ops->lsm_open_tx = wcd_cpe_cmd_lsm_open_tx;
+ lsm_ops->lsm_close_tx = wcd_cpe_cmd_lsm_close_tx;
+ lsm_ops->lsm_shmem_alloc = wcd_cpe_cmd_lsm_shmem_alloc;
+ lsm_ops->lsm_shmem_dealloc = wcd_cpe_cmd_lsm_shmem_dealloc;
+ lsm_ops->lsm_register_snd_model = wcd_cpe_lsm_reg_snd_model;
+ lsm_ops->lsm_deregister_snd_model = wcd_cpe_lsm_dereg_snd_model;
+ lsm_ops->lsm_get_afe_out_port_id = wcd_cpe_lsm_get_afe_out_port_id;
+ lsm_ops->lsm_start = wcd_cpe_cmd_lsm_start;
+ lsm_ops->lsm_stop = wcd_cpe_cmd_lsm_stop;
+ lsm_ops->lsm_lab_control = wcd_cpe_lsm_lab_control;
+ lsm_ops->lab_ch_setup = wcd_cpe_lab_ch_setup;
+ lsm_ops->lsm_set_data = wcd_cpe_lsm_set_data;
+ lsm_ops->lsm_set_fmt_cfg = wcd_cpe_lsm_set_fmt_cfg;
+ lsm_ops->lsm_set_one_param = wcd_cpe_set_one_param;
+ lsm_ops->lsm_get_snd_model_offset = wcd_cpe_snd_model_offset;
+ lsm_ops->lsm_set_media_fmt_params = wcd_cpe_lsm_set_media_fmt_params;
+ lsm_ops->lsm_set_port = wcd_cpe_lsm_set_port;
+
+ return 0;
+}
+EXPORT_SYMBOL(wcd_cpe_get_lsm_ops);
+
+static int fill_afe_cmd_header(struct cmi_hdr *hdr, u8 port_id,
+ u16 opcode, u8 pld_size,
+ bool obm_flag)
+{
+ CMI_HDR_SET_SESSION(hdr, port_id);
+ CMI_HDR_SET_SERVICE(hdr, CMI_CPE_AFE_SERVICE_ID);
+
+ CMI_HDR_SET_PAYLOAD_SIZE(hdr, pld_size);
+
+ hdr->opcode = opcode;
+
+ if (obm_flag)
+ CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_OUT_BAND);
+ else
+ CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND);
+
+ return 0;
+}
+
+/*
+ * wcd_cpe_cmi_send_afe_msg: send message to AFE service
+ * @core: wcd cpe core handle
+ * @port_cfg: configuration data for the afe port
+ * for which this message is to be sent
+ * @message: actual message with header and payload
+ *
+ * Port specific lock needs to be acquired before this
+ * function can be invoked
+ */
+static int wcd_cpe_cmi_send_afe_msg(
+ struct wcd_cpe_core *core,
+ struct wcd_cmi_afe_port_data *port_d,
+ void *message)
+{
+ int ret = 0;
+ struct cmi_hdr *hdr = message;
+
+ pr_debug("%s: sending message with opcode 0x%x\n",
+ __func__, hdr->opcode);
+
+ if (unlikely(!wcd_cpe_is_online_state(core))) {
+ dev_err(core->dev, "%s: CPE offline\n", __func__);
+ return 0;
+ }
+
+ if (CMI_HDR_GET_OBM_FLAG(hdr))
+ wcd_cpe_bus_vote_max_bw(core, true);
+
+ ret = cmi_send_msg(message);
+ if (ret) {
+ pr_err("%s: cmd 0x%x send failed, err = %d\n",
+ __func__, hdr->opcode, ret);
+ goto rel_bus_vote;
+ }
+
+ ret = wait_for_completion_timeout(&port_d->afe_cmd_complete,
+ CMI_CMD_TIMEOUT);
+ if (ret > 0) {
+ pr_debug("%s: command 0x%x, received response 0x%x\n",
+ __func__, hdr->opcode, port_d->cmd_result);
+ if (port_d->cmd_result == CMI_SHMEM_ALLOC_FAILED)
+ port_d->cmd_result = CPE_ENOMEMORY;
+ if (port_d->cmd_result > 0)
+ pr_err("%s: CPE returned error[%s]\n",
+ __func__, cpe_err_get_err_str(
+ port_d->cmd_result));
+ ret = cpe_err_get_lnx_err_code(port_d->cmd_result);
+ goto rel_bus_vote;
+ } else {
+ pr_err("%s: command 0x%x send timed out\n",
+ __func__, hdr->opcode);
+ ret = -ETIMEDOUT;
+ goto rel_bus_vote;
+ }
+
+rel_bus_vote:
+ reinit_completion(&port_d->afe_cmd_complete);
+
+ if (CMI_HDR_GET_OBM_FLAG(hdr))
+ wcd_cpe_bus_vote_max_bw(core, false);
+
+ return ret;
+}
+
+
+
+/*
+ * wcd_cpe_afe_shmem_alloc: allocate the cpe memory for afe service
+ * @core: handle to cpe core
+ * @port_cfg: configuration data for the port which needs
+ * memory to be allocated on CPE
+ * @size: size of the memory to be allocated
+ */
+static int wcd_cpe_afe_shmem_alloc(
+ struct wcd_cpe_core *core,
+ struct wcd_cmi_afe_port_data *port_d,
+ u32 size)
+{
+ struct cpe_cmd_shmem_alloc cmd_shmem_alloc;
+ int ret = 0;
+
+ pr_debug("%s: enter: size = %d\n", __func__, size);
+
+ memset(&cmd_shmem_alloc, 0, sizeof(cmd_shmem_alloc));
+ if (fill_afe_cmd_header(&cmd_shmem_alloc.hdr, port_d->port_id,
+ CPE_AFE_PORT_CMD_SHARED_MEM_ALLOC,
+ SHMEM_ALLOC_CMD_PLD_SIZE, false)) {
+ ret = -EINVAL;
+ goto end_ret;
+ }
+
+ cmd_shmem_alloc.size = size;
+
+ ret = wcd_cpe_cmi_send_afe_msg(core, port_d, &cmd_shmem_alloc);
+ if (ret) {
+ pr_err("%s: afe_shmem_alloc fail,ret = %d\n",
+ __func__, ret);
+ goto end_ret;
+ }
+
+ pr_debug("%s: completed %s, mem_handle = 0x%x\n",
+ __func__, "CPE_AFE_CMD_SHARED_MEM_ALLOC",
+ port_d->mem_handle);
+
+end_ret:
+ return ret;
+}
+
+/*
+ * wcd_cpe_afe_shmem_dealloc: deallocate the cpe memory for
+ * afe service
+ * @core: handle to cpe core
+ * @port_d: configuration data for the port which needs
+ * memory to be deallocated on CPE
+ * The memory handle to be de-allocated is saved in the
+ * port configuration data
+ */
+static int wcd_cpe_afe_shmem_dealloc(
+ struct wcd_cpe_core *core,
+ struct wcd_cmi_afe_port_data *port_d)
+{
+ struct cpe_cmd_shmem_dealloc cmd_dealloc;
+ int ret = 0;
+
+ pr_debug("%s: enter, port_id = %d\n",
+ __func__, port_d->port_id);
+
+ memset(&cmd_dealloc, 0, sizeof(cmd_dealloc));
+ if (fill_afe_cmd_header(&cmd_dealloc.hdr, port_d->port_id,
+ CPE_AFE_PORT_CMD_SHARED_MEM_DEALLOC,
+ SHMEM_DEALLOC_CMD_PLD_SIZE, false)) {
+ ret = -EINVAL;
+ goto end_ret;
+ }
+
+ cmd_dealloc.addr = port_d->mem_handle;
+ ret = wcd_cpe_cmi_send_afe_msg(core, port_d, &cmd_dealloc);
+ if (ret) {
+ pr_err("failed to send shmem_dealloc cmd\n");
+ goto end_ret;
+ }
+ memset(&port_d->mem_handle, 0,
+ sizeof(port_d->mem_handle));
+
+end_ret:
+ return ret;
+}
+
+/*
+ * wcd_cpe_send_afe_cal: send the acdb calibration to AFE port
+ * @core: handle to cpe core
+ * @port_d: configuration data for the port for which the
+ * calibration needs to be appplied
+ */
+static int wcd_cpe_send_afe_cal(void *core_handle,
+ struct wcd_cmi_afe_port_data *port_d)
+{
+
+ struct cal_block_data *afe_cal = NULL;
+ struct wcd_cpe_core *core = core_handle;
+ struct cmi_obm_msg obm_msg;
+ void *inb_msg = NULL;
+ void *msg;
+ int rc = 0;
+ bool is_obm_msg;
+
+ if (core->cal_data[WCD_CPE_LSM_CAL_AFE] == NULL) {
+ pr_err("%s: LSM cal not allocated!\n",
+ __func__);
+ rc = -EINVAL;
+ goto rel_cal_mutex;
+ }
+
+ mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_AFE]->lock);
+ afe_cal = cal_utils_get_only_cal_block(
+ core->cal_data[WCD_CPE_LSM_CAL_AFE]);
+ if (!afe_cal) {
+ pr_err("%s: failed to get afe cal block\n",
+ __func__);
+ rc = -EINVAL;
+ goto rel_cal_mutex;
+ }
+
+ if (afe_cal->cal_data.size == 0) {
+ dev_dbg(core->dev, "%s: No AFE cal to send\n",
+ __func__);
+ rc = 0;
+ goto rel_cal_mutex;
+ }
+
+ is_obm_msg = (afe_cal->cal_data.size >
+ CMI_INBAND_MESSAGE_SIZE) ? true : false;
+
+ if (is_obm_msg) {
+ struct cmi_hdr *hdr = &(obm_msg.hdr);
+ struct cmi_obm *pld = &(obm_msg.pld);
+
+ rc = wcd_cpe_afe_shmem_alloc(core, port_d,
+ afe_cal->cal_data.size);
+ if (rc) {
+ dev_err(core->dev,
+ "%s: AFE shmem alloc fail %d\n",
+ __func__, rc);
+ goto rel_cal_mutex;
+ }
+
+ rc = fill_afe_cmd_header(hdr, port_d->port_id,
+ CPE_AFE_CMD_SET_PARAM,
+ CPE_AFE_PARAM_PAYLOAD_SIZE,
+ true);
+ if (rc) {
+ dev_err(core->dev,
+ "%s: invalid params for header, err = %d\n",
+ __func__, rc);
+ wcd_cpe_afe_shmem_dealloc(core, port_d);
+ goto rel_cal_mutex;
+ }
+
+ pld->version = 0;
+ pld->size = afe_cal->cal_data.size;
+ pld->data_ptr.kvaddr = afe_cal->cal_data.kvaddr;
+ pld->mem_handle = port_d->mem_handle;
+ msg = &obm_msg;
+
+ } else {
+ u8 *msg_pld;
+ struct cmi_hdr *hdr;
+ inb_msg = kzalloc(sizeof(struct cmi_hdr) +
+ afe_cal->cal_data.size,
+ GFP_KERNEL);
+ if (!inb_msg) {
+ dev_err(core->dev,
+ "%s: no memory for afe cal inband\n",
+ __func__);
+ rc = -ENOMEM;
+ goto rel_cal_mutex;
+ }
+
+ hdr = (struct cmi_hdr *) inb_msg;
+
+ rc = fill_afe_cmd_header(hdr, port_d->port_id,
+ CPE_AFE_CMD_SET_PARAM,
+ CPE_AFE_PARAM_PAYLOAD_SIZE,
+ false);
+ if (rc) {
+ dev_err(core->dev,
+ "%s: invalid params for header, err = %d\n",
+ __func__, rc);
+ kfree(inb_msg);
+ inb_msg = NULL;
+ goto rel_cal_mutex;
+ }
+
+ msg_pld = ((u8 *) inb_msg) + sizeof(struct cmi_hdr);
+ memcpy(msg_pld, afe_cal->cal_data.kvaddr,
+ afe_cal->cal_data.size);
+
+ msg = inb_msg;
+ }
+
+ rc = wcd_cpe_cmi_send_afe_msg(core, port_d, msg);
+ if (rc)
+ pr_err("%s: afe cal for listen failed, rc = %d\n",
+ __func__, rc);
+
+ if (is_obm_msg) {
+ wcd_cpe_afe_shmem_dealloc(core, port_d);
+ port_d->mem_handle = 0;
+ } else {
+ kfree(inb_msg);
+ inb_msg = NULL;
+ }
+
+rel_cal_mutex:
+ mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_AFE]->lock);
+ return rc;
+}
+
+/*
+ * wcd_cpe_is_valid_port: check validity of afe port id
+ * @core: handle to core to check for validity
+ * @afe_cfg: client provided afe configuration
+ * @func: function name invoking this validity check,
+ * used for logging purpose only.
+ */
+static int wcd_cpe_is_valid_port(struct wcd_cpe_core *core,
+ struct wcd_cpe_afe_port_cfg *afe_cfg,
+ const char *func)
+{
+ if (unlikely(IS_ERR_OR_NULL(core))) {
+ pr_err("%s: Invalid core handle\n", func);
+ return -EINVAL;
+ }
+
+ if (afe_cfg->port_id > WCD_CPE_AFE_MAX_PORTS) {
+ dev_err(core->dev,
+ "%s: invalid afe port (%u)\n",
+ func, afe_cfg->port_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(core->dev,
+ "%s: port_id = %u\n",
+ func, afe_cfg->port_id);
+
+ return 0;
+}
+
+static int wcd_cpe_afe_svc_cmd_mode(void *core_handle,
+ u8 mode)
+{
+ struct cpe_afe_svc_cmd_mode afe_mode;
+ struct wcd_cpe_core *core = core_handle;
+ struct wcd_cmi_afe_port_data *afe_port_d;
+ int ret;
+
+ afe_port_d = &afe_ports[0];
+ /*
+ * AFE SVC mode command is for the service and not port
+ * specific, hence use AFE port as 0 so the command will
+ * be applied to all AFE ports on CPE.
+ */
+ afe_port_d->port_id = 0;
+
+ WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+ memset(&afe_mode, 0, sizeof(afe_mode));
+ if (fill_afe_cmd_header(&afe_mode.hdr, afe_port_d->port_id,
+ CPE_AFE_SVC_CMD_LAB_MODE,
+ CPE_AFE_CMD_MODE_PAYLOAD_SIZE,
+ false)) {
+ ret = -EINVAL;
+ goto err_ret;
+ }
+
+ afe_mode.mode = mode;
+
+ ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &afe_mode);
+ if (ret)
+ dev_err(core->dev,
+ "%s: afe_svc_mode cmd failed, err = %d\n",
+ __func__, ret);
+
+err_ret:
+ WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+ return ret;
+}
+
+static int wcd_cpe_afe_cmd_port_cfg(void *core_handle,
+ struct wcd_cpe_afe_port_cfg *afe_cfg)
+{
+ struct cpe_afe_cmd_port_cfg port_cfg_cmd;
+ struct wcd_cpe_core *core = core_handle;
+ struct wcd_cmi_afe_port_data *afe_port_d;
+ int ret;
+
+ ret = wcd_cpe_is_valid_port(core, afe_cfg, __func__);
+ if (ret)
+ goto done;
+
+ afe_port_d = &afe_ports[afe_cfg->port_id];
+ afe_port_d->port_id = afe_cfg->port_id;
+
+ WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+ memset(&port_cfg_cmd, 0, sizeof(port_cfg_cmd));
+ if (fill_afe_cmd_header(&port_cfg_cmd.hdr,
+ afe_cfg->port_id,
+ CPE_AFE_PORT_CMD_GENERIC_CONFIG,
+ CPE_AFE_CMD_PORT_CFG_PAYLOAD_SIZE,
+ false)) {
+ ret = -EINVAL;
+ goto err_ret;
+ }
+
+ port_cfg_cmd.bit_width = afe_cfg->bit_width;
+ port_cfg_cmd.num_channels = afe_cfg->num_channels;
+ port_cfg_cmd.sample_rate = afe_cfg->sample_rate;
+
+ if (afe_port_d->port_id == CPE_AFE_PORT_3_TX)
+ port_cfg_cmd.buffer_size = WCD_CPE_EC_PP_BUF_SIZE;
+ else
+ port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width,
+ afe_cfg->sample_rate);
+
+ ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &port_cfg_cmd);
+ if (ret)
+ dev_err(core->dev,
+ "%s: afe_port_config failed, err = %d\n",
+ __func__, ret);
+
+err_ret:
+ WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+done:
+ return ret;
+}
+
+/*
+ * wcd_cpe_afe_set_params: set the parameters for afe port
+ * @afe_cfg: configuration data for the port for which the
+ * parameters are to be set
+ */
+static int wcd_cpe_afe_set_params(void *core_handle,
+ struct wcd_cpe_afe_port_cfg *afe_cfg, bool afe_mad_ctl)
+{
+ struct cpe_afe_params afe_params;
+ struct cpe_afe_hw_mad_ctrl *hw_mad_ctrl = &afe_params.hw_mad_ctrl;
+ struct cpe_afe_port_cfg *port_cfg = &afe_params.port_cfg;
+ struct wcd_cpe_core *core = core_handle;
+ struct wcd_cmi_afe_port_data *afe_port_d;
+ int ret = 0, pld_size = 0;
+
+ ret = wcd_cpe_is_valid_port(core, afe_cfg, __func__);
+ if (ret)
+ return ret;
+
+ afe_port_d = &afe_ports[afe_cfg->port_id];
+ afe_port_d->port_id = afe_cfg->port_id;
+
+ WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+
+ ret = wcd_cpe_send_afe_cal(core, afe_port_d);
+ if (ret) {
+ dev_err(core->dev,
+ "%s: afe acdb cal send failed, err = %d\n",
+ __func__, ret);
+ goto err_ret;
+ }
+
+ pld_size = CPE_AFE_PARAM_PAYLOAD_SIZE;
+ memset(&afe_params, 0, sizeof(afe_params));
+
+ if (fill_afe_cmd_header(&afe_params.hdr,
+ afe_cfg->port_id,
+ CPE_AFE_CMD_SET_PARAM,
+ (u8) pld_size, false)) {
+ ret = -EINVAL;
+ goto err_ret;
+ }
+
+ hw_mad_ctrl->param.module_id = CPE_AFE_MODULE_HW_MAD;
+ hw_mad_ctrl->param.param_id = CPE_AFE_PARAM_ID_HW_MAD_CTL;
+ hw_mad_ctrl->param.p_size.sr.param_size = PARAM_SIZE_AFE_HW_MAD_CTRL;
+ hw_mad_ctrl->param.p_size.sr.reserved = 0;
+ hw_mad_ctrl->minor_version = 1;
+ hw_mad_ctrl->mad_type = MAD_TYPE_AUDIO;
+ hw_mad_ctrl->mad_enable = afe_mad_ctl;
+
+ port_cfg->param.module_id = CPE_AFE_MODULE_AUDIO_DEV_INTERFACE;
+ port_cfg->param.param_id = CPE_AFE_PARAM_ID_GENERIC_PORT_CONFIG;
+ port_cfg->param.p_size.sr.param_size = PARAM_SIZE_AFE_PORT_CFG;
+ port_cfg->param.p_size.sr.reserved = 0;
+ port_cfg->minor_version = 1;
+ port_cfg->bit_width = afe_cfg->bit_width;
+ port_cfg->num_channels = afe_cfg->num_channels;
+ port_cfg->sample_rate = afe_cfg->sample_rate;
+
+ ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &afe_params);
+ if (ret)
+ dev_err(core->dev,
+ "%s: afe_port_config failed, err = %d\n",
+ __func__, ret);
+err_ret:
+ WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+ return ret;
+}
+
+/*
+ * wcd_cpe_afe_port_start: send the start command to afe service
+ * @core_handle: handle to the cpe core
+ * @port_cfg: configuration data for the afe port which needs
+ * to be started.
+ */
+static int wcd_cpe_afe_port_start(void *core_handle,
+ struct wcd_cpe_afe_port_cfg *port_cfg)
+{
+
+ struct cmi_hdr hdr;
+ struct wcd_cpe_core *core = core_handle;
+ struct wcd_cmi_afe_port_data *afe_port_d;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_port(core, port_cfg, __func__);
+ if (ret)
+ return ret;
+
+ afe_port_d = &afe_ports[port_cfg->port_id];
+ afe_port_d->port_id = port_cfg->port_id;
+
+ WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+
+ memset(&hdr, 0, sizeof(struct cmi_hdr));
+ fill_afe_cmd_header(&hdr, port_cfg->port_id,
+ CPE_AFE_PORT_CMD_START,
+ 0, false);
+ ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr);
+ if (ret)
+ dev_err(core->dev,
+ "%s: afe_port_start cmd failed, err = %d\n",
+ __func__, ret);
+ WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+ return ret;
+}
+
+/*
+ * wcd_cpe_afe_port_stop: send stop command to afe service
+ * @core_handle: handle to the cpe core
+ * @port_cfg: configuration data for the afe port which needs
+ * to be stopped.
+ */
+static int wcd_cpe_afe_port_stop(void *core_handle,
+ struct wcd_cpe_afe_port_cfg *port_cfg)
+{
+ struct cmi_hdr hdr;
+ struct wcd_cpe_core *core = core_handle;
+ struct wcd_cmi_afe_port_data *afe_port_d;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_port(core, port_cfg, __func__);
+ if (ret)
+ return ret;
+
+ afe_port_d = &afe_ports[port_cfg->port_id];
+ afe_port_d->port_id = port_cfg->port_id;
+
+ WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+
+ memset(&hdr, 0, sizeof(hdr));
+ fill_afe_cmd_header(&hdr, port_cfg->port_id,
+ CPE_AFE_PORT_CMD_STOP,
+ 0, false);
+ ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr);
+ if (ret)
+ dev_err(core->dev,
+ "%s: afe_stop cmd failed, err = %d\n",
+ __func__, ret);
+
+ WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+ return ret;
+}
+
+/*
+ * wcd_cpe_afe_port_suspend: send suspend command to afe service
+ * @core_handle: handle to the cpe core
+ * @port_cfg: configuration data for the afe port which needs
+ * to be suspended.
+ */
+static int wcd_cpe_afe_port_suspend(void *core_handle,
+ struct wcd_cpe_afe_port_cfg *port_cfg)
+{
+ struct cmi_hdr hdr;
+ struct wcd_cpe_core *core = core_handle;
+ struct wcd_cmi_afe_port_data *afe_port_d;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_port(core, port_cfg, __func__);
+ if (ret)
+ return ret;
+
+ afe_port_d = &afe_ports[port_cfg->port_id];
+ afe_port_d->port_id = port_cfg->port_id;
+
+ WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+
+ memset(&hdr, 0, sizeof(struct cmi_hdr));
+ fill_afe_cmd_header(&hdr, port_cfg->port_id,
+ CPE_AFE_PORT_CMD_SUSPEND,
+ 0, false);
+ ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr);
+ if (ret)
+ dev_err(core->dev,
+ "%s: afe_suspend cmd failed, err = %d\n",
+ __func__, ret);
+ WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+ return ret;
+}
+
+/*
+ * wcd_cpe_afe_port_resume: send the resume command to afe service
+ * @core_handle: handle to the cpe core
+ * @port_cfg: configuration data for the afe port which needs
+ * to be resumed.
+ */
+static int wcd_cpe_afe_port_resume(void *core_handle,
+ struct wcd_cpe_afe_port_cfg *port_cfg)
+{
+ struct cmi_hdr hdr;
+ struct wcd_cpe_core *core = core_handle;
+ struct wcd_cmi_afe_port_data *afe_port_d;
+ int ret = 0;
+
+ ret = wcd_cpe_is_valid_port(core, port_cfg, __func__);
+ if (ret)
+ return ret;
+
+ afe_port_d = &afe_ports[port_cfg->port_id];
+ afe_port_d->port_id = port_cfg->port_id;
+
+ WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+
+ memset(&hdr, 0, sizeof(hdr));
+ fill_afe_cmd_header(&hdr, port_cfg->port_id,
+ CPE_AFE_PORT_CMD_RESUME,
+ 0, false);
+ ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr);
+ if (ret)
+ dev_err(core->dev,
+ "%s: afe_resume cmd failed, err = %d\n",
+ __func__, ret);
+ WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+ return ret;
+
+}
+
+/*
+ * wcd_cpe_register_afe_driver: register lsm driver to codec
+ * @cpe_ops: structure with lsm callbacks
+ * @codec: codec to which this lsm driver is registered to
+ */
+int wcd_cpe_get_afe_ops(struct wcd_cpe_afe_ops *afe_ops)
+{
+ afe_ops->afe_set_params = wcd_cpe_afe_set_params;
+ afe_ops->afe_port_start = wcd_cpe_afe_port_start;
+ afe_ops->afe_port_stop = wcd_cpe_afe_port_stop;
+ afe_ops->afe_port_suspend = wcd_cpe_afe_port_suspend;
+ afe_ops->afe_port_resume = wcd_cpe_afe_port_resume;
+ afe_ops->afe_port_cmd_cfg = wcd_cpe_afe_cmd_port_cfg;
+
+ return 0;
+}
+EXPORT_SYMBOL(wcd_cpe_get_afe_ops);
+
+MODULE_DESCRIPTION("WCD CPE Core");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd_cpe_core.h b/sound/soc/codecs/wcd_cpe_core.h
new file mode 100644
index 000000000000..1ac393471197
--- /dev/null
+++ b/sound/soc/codecs/wcd_cpe_core.h
@@ -0,0 +1,229 @@
+/* Copyright (c) 2013-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WCD_CPE_CORE_H
+#define WCD_CPE_CORE_H
+
+#include <soc/qcom/ramdump.h>
+#include <linux/dma-mapping.h>
+#include "wcd_cpe_services.h"
+
+#define WCD_CPE_LAB_MAX_LATENCY 250
+#define WCD_CPE_MAD_SLIM_CHANNEL 140
+
+/* Indicates CPE block is ready for image re-download */
+#define WCD_CPE_BLK_READY (1 << 0)
+/* Indicates the underlying bus is ready */
+#define WCD_CPE_BUS_READY (1 << 1)
+
+/*
+ * only when the underlying bus and CPE block both are ready,
+ * the state will be ready to download
+ */
+#define WCD_CPE_READY_TO_DLOAD \
+ (WCD_CPE_BLK_READY | WCD_CPE_BUS_READY)
+
+#define WCD_CPE_LOAD_IMEM (1 << 0)
+#define WCD_CPE_LOAD_DATA (1 << 1)
+#define WCD_CPE_LOAD_ALL \
+ (WCD_CPE_LOAD_IMEM | WCD_CPE_LOAD_DATA)
+
+#define WCD_CPE_IMAGE_FNAME_MAX 64
+
+#define WCD_CPE_AFE_OUT_PORT_2 2
+#define WCD_CPE_AFE_OUT_PORT_4 4
+
+enum {
+ WCD_CPE_LSM_CAL_AFE = 0,
+ WCD_CPE_LSM_CAL_LSM,
+ WCD_CPE_LSM_CAL_TOPOLOGY_ID,
+ WCD_CPE_LSM_CAL_MAX,
+};
+
+enum cpe_err_irq_cntl_type {
+ CPE_ERR_IRQ_MASK = 0,
+ CPE_ERR_IRQ_UNMASK,
+ CPE_ERR_IRQ_CLEAR,
+ CPE_ERR_IRQ_STATUS,
+};
+
+struct wcd_cpe_cdc_cb {
+ /* codec provided callback to enable RCO */
+ int (*cdc_clk_en) (struct snd_soc_codec *, bool);
+
+ /* callback for FLL setup for codec */
+ int (*cpe_clk_en) (struct snd_soc_codec *, bool);
+ int (*cdc_ext_clk)(struct snd_soc_codec *codec, int enable, bool dapm);
+ int (*lab_cdc_ch_ctl)(struct snd_soc_codec *codec, u8 event);
+ int (*get_afe_out_port_id)(struct snd_soc_codec *codec, u16 *port_id);
+ int (*bus_vote_bw)(struct snd_soc_codec *codec,
+ bool vote);
+
+ /* Callback to control the cpe error interrupt mask/status/clear */
+ int (*cpe_err_irq_control)(struct snd_soc_codec *codec,
+ enum cpe_err_irq_cntl_type cntl_type,
+ u8 *status);
+};
+
+enum wcd_cpe_ssr_state_event {
+ /* Indicates CPE is initialized */
+ WCD_CPE_INITIALIZED = 0,
+ /* Indicates that IMEM is downloaded to CPE */
+ WCD_CPE_IMEM_DOWNLOADED,
+ /* Indicates CPE is enabled */
+ WCD_CPE_ENABLED,
+ /* Indicates that CPE is currently active */
+ WCD_CPE_ACTIVE,
+ /* Event from underlying bus notifying bus is down */
+ WCD_CPE_BUS_DOWN_EVENT,
+ /* Event from CPE block, notifying CPE is down */
+ WCD_CPE_SSR_EVENT,
+ /* Event from underlying bus notifying bus is up */
+ WCD_CPE_BUS_UP_EVENT,
+};
+
+struct wcd_cpe_ssr_entry {
+ int offline;
+ u32 offline_change;
+ wait_queue_head_t offline_poll_wait;
+ struct snd_info_entry *entry;
+};
+
+struct wcd_cpe_irq_info {
+ int cpe_engine_irq;
+ int cpe_err_irq;
+ u8 cpe_fatal_irqs;
+};
+
+struct wcd_cpe_hw_info {
+ u32 dram_offset;
+ size_t dram_size;
+ u32 iram_offset;
+ size_t iram_size;
+};
+
+struct wcd_cpe_core {
+ /* handle to cpe services */
+ void *cpe_handle;
+
+ /* registration handle to cpe services */
+ void *cpe_reg_handle;
+
+ /* cmi registration handle for afe service */
+ void *cmi_afe_handle;
+
+ /* handle to codec */
+ struct snd_soc_codec *codec;
+
+ /* codec device */
+ struct device *dev;
+
+ /* firmware image file name */
+ char fname[WCD_CPE_IMAGE_FNAME_MAX];
+
+ /* firmware image file name from sysfs */
+ char dyn_fname[WCD_CPE_IMAGE_FNAME_MAX];
+
+ /* codec information needed by cpe services */
+ struct cpe_svc_codec_info_v1 cdc_info;
+
+ /* work to perform image download */
+ struct work_struct load_fw_work;
+
+ /* flag to indicate mode in which cpe needs to be booted */
+ int cpe_debug_mode;
+
+ /* callbacks for codec specific implementation */
+ const struct wcd_cpe_cdc_cb *cpe_cdc_cb;
+
+ /* work to handle CPE SSR*/
+ struct work_struct ssr_work;
+
+ /* PM handle for suspend mode during SSR */
+ struct pm_qos_request pm_qos_req;
+
+ /* completion event indicating CPE OFFLINE */
+ struct completion offline_compl;
+
+ /* entry into snd card procfs indicating cpe status */
+ struct wcd_cpe_ssr_entry ssr_entry;
+
+ /*
+ * completion event to signal CPE is
+ * ready for image re-download
+ */
+ struct completion ready_compl;
+
+ /* maintains the status for cpe ssr */
+ u8 ready_status;
+
+ /* Indicate SSR type */
+ enum wcd_cpe_ssr_state_event ssr_type;
+
+ /* mutex to protect cpe ssr status variables */
+ struct mutex ssr_lock;
+
+ /* mutex to protect cpe session status variables */
+ struct mutex session_lock;
+
+ /* Store the calibration data needed for cpe */
+ struct cal_type_data *cal_data[WCD_CPE_LSM_CAL_MAX];
+
+ /* completion event to signal CPE is online */
+ struct completion online_compl;
+
+ /* reference counter for cpe usage */
+ u8 cpe_users;
+
+ /* Ramdump support */
+ void *cpe_ramdump_dev;
+ struct ramdump_segment cpe_ramdump_seg;
+ dma_addr_t cpe_dump_addr;
+ void *cpe_dump_v_addr;
+
+ /* SFR support */
+ u32 sfr_buf_addr;
+ size_t sfr_buf_size;
+
+ /* IRQ information for CPE interrupts */
+ struct wcd_cpe_irq_info irq_info;
+
+ /* Kobject for sysfs entry */
+ struct kobject cpe_kobj;
+
+ /* Reference count for cpe clk*/
+ int cpe_clk_ref;
+
+ /* codec based hardware info */
+ struct wcd_cpe_hw_info hw_info;
+};
+
+struct wcd_cpe_params {
+ struct snd_soc_codec *codec;
+ struct wcd_cpe_core * (*get_cpe_core) (
+ struct snd_soc_codec *);
+ const struct wcd_cpe_cdc_cb *cdc_cb;
+ int dbg_mode;
+ u16 cdc_major_ver;
+ u16 cdc_minor_ver;
+ u32 cdc_id;
+
+ struct wcd_cpe_irq_info cdc_irq_info;
+
+ struct cpe_svc_init_param *cpe_svc_params;
+};
+
+int wcd_cpe_ssr_event(void *core_handle,
+ enum wcd_cpe_ssr_state_event event);
+struct wcd_cpe_core *wcd_cpe_init(const char *,
+struct snd_soc_codec *, struct wcd_cpe_params *params);
+#endif
diff --git a/sound/soc/codecs/wcd_cpe_services.c b/sound/soc/codecs/wcd_cpe_services.c
new file mode 100644
index 000000000000..18cd44d3d9c7
--- /dev/null
+++ b/sound/soc/codecs/wcd_cpe_services.c
@@ -0,0 +1,3016 @@
+/* Copyright (c) 2014-2016, 2018, 2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <sound/cpe_cmi.h>
+#include <sound/soc.h>
+#include <linux/mfd/wcd9xxx/wcd9330_registers.h>
+#include <linux/mfd/wcd9335/registers.h>
+#include "wcd_cpe_services.h"
+#include "wcd_cmi_api.h"
+
+#define CPE_MSG_BUFFER_SIZE 132
+#define CPE_NO_SERVICE 0
+
+#define CMI_DRIVER_SUPPORTED_VERSION 0
+#define CMI_API_SUCCESS 0
+#define CMI_MSG_TRANSPORT (0x0002)
+#define CPE_SVC_INACTIVE_STATE_RETRIES_MAX 10
+
+#define TOMTOM_A_SVASS_SPE_DRAM_OFFSET 0x50000
+#define TOMTOM_A_SVASS_SPE_DRAM_SIZE 0x30000
+#define TOMTOM_A_SVASS_SPE_IRAM_OFFSET 0x80000
+#define TOMTOM_A_SVASS_SPE_IRAM_SIZE 0xC000
+#define TOMTOM_A_SVASS_SPE_INBOX_SIZE 12
+#define TOMTOM_A_SVASS_SPE_OUTBOX_SIZE 12
+
+#define MEM_ACCESS_NONE_VAL 0x0
+#define MEM_ACCESS_IRAM_VAL 0x1
+#define MEM_ACCESS_DRAM_VAL 0x2
+#define LISTEN_CTL_SPE_VAL 0x0
+#define LISTEN_CTL_MSM_VAL 0x1
+
+#define TOMTOM_A_SVASS_SPE_INBOX(N) (TOMTOM_A_SVASS_SPE_INBOX_0 + (N))
+#define TOMTOM_A_SVASS_SPE_OUTBOX(N) (TOMTOM_A_SVASS_SPE_OUTBOX_0 + (N))
+
+#define WCD9335_CPE_SS_SPE_DRAM_OFFSET 0x48000
+#define WCD9335_CPE_SS_SPE_DRAM_SIZE 0x34000
+#define WCD9335_CPE_SS_SPE_IRAM_OFFSET 0x80000
+#define WCD9335_CPE_SS_SPE_IRAM_SIZE 0x20000
+
+#define WCD9335_CPE_SS_SPE_INBOX_SIZE 16
+#define WCD9335_CPE_SS_SPE_OUTBOX_SIZE 16
+#define WCD9335_CPE_SS_SPE_MEM_BANK_SIZ 16
+
+#define WCD9335_CPE_SS_SPE_INBOX1(N) (WCD9335_CPE_SS_INBOX1_0 + (N))
+#define WCD9335_CPE_SS_SPE_OUTBOX1(N) (WCD9335_CPE_SS_OUTBOX1_0 + (N))
+#define WCD9335_CPE_SS_MEM_BANK(N) (WCD9335_CPE_SS_MEM_BANK_0 + (N))
+
+#define CHUNK_SIZE 16
+
+#define CPE_SVC_GRAB_LOCK(lock, name) \
+{ \
+ pr_debug("%s: %s lock acquire\n", \
+ __func__, name); \
+ mutex_lock(lock); \
+}
+
+#define CPE_SVC_REL_LOCK(lock, name) \
+{ \
+ pr_debug("%s: %s lock release\n", \
+ __func__, name); \
+ mutex_unlock(lock); \
+}
+
+static const struct cpe_svc_hw_cfg cpe_svc_tomtom_info = {
+ TOMTOM_A_SVASS_SPE_DRAM_SIZE,
+ TOMTOM_A_SVASS_SPE_DRAM_OFFSET,
+ TOMTOM_A_SVASS_SPE_IRAM_SIZE,
+ TOMTOM_A_SVASS_SPE_IRAM_OFFSET,
+ TOMTOM_A_SVASS_SPE_INBOX_SIZE,
+ TOMTOM_A_SVASS_SPE_OUTBOX_SIZE
+};
+
+static const struct cpe_svc_hw_cfg cpe_svc_wcd9335_info = {
+ WCD9335_CPE_SS_SPE_DRAM_SIZE,
+ WCD9335_CPE_SS_SPE_DRAM_OFFSET,
+ WCD9335_CPE_SS_SPE_IRAM_SIZE,
+ WCD9335_CPE_SS_SPE_IRAM_OFFSET,
+ WCD9335_CPE_SS_SPE_INBOX_SIZE,
+ WCD9335_CPE_SS_SPE_OUTBOX_SIZE
+};
+
+enum cpe_state {
+ CPE_STATE_UNINITIALIZED = 0,
+ CPE_STATE_INITIALIZED,
+ CPE_STATE_IDLE,
+ CPE_STATE_DOWNLOADING,
+ CPE_STATE_BOOTING,
+ CPE_STATE_SENDING_MSG,
+ CPE_STATE_OFFLINE,
+ CPE_STATE_BUFFERING,
+ CPE_STATE_BUFFERING_CANCELLED
+};
+
+enum cpe_substate {
+ CPE_SS_IDLE = 0,
+ CPE_SS_MSG_REQUEST_ACCESS,
+ CPE_SS_MSG_SEND_INBOX,
+ CPE_SS_MSG_SENT,
+ CPE_SS_DL_DOWNLOADING,
+ CPE_SS_DL_COMPLETED,
+ CPE_SS_BOOT,
+ CPE_SS_BOOT_INIT,
+ CPE_SS_ONLINE
+};
+
+enum cpe_command {
+ CPE_CMD_KILL_THREAD = 0,
+ CPE_CMD_BOOT,
+ CPE_CMD_BOOT_INITIALIZE,
+ CPE_CMD_BOOT_COMPLETE,
+ CPE_CMD_SEND_MSG,
+ CPE_CMD_SEND_TRANS_MSG,
+ CPE_CMD_SEND_MSG_COMPLETE,
+ CPE_CMD_PROCESS_IRQ,
+ CPE_CMD_RAMDUMP,
+ CPE_CMD_DL_SEGMENT,
+ CPE_CMD_SHUTDOWN,
+ CPE_CMD_RESET,
+ CPE_CMD_DEINITIALIZE,
+ CPE_CMD_READ,
+ CPE_CMD_ENABLE_LAB,
+ CPE_CMD_DISABLE_LAB,
+ CPE_CMD_SWAP_BUFFER,
+ CPE_LAB_CFG_SB,
+ CPE_CMD_CANCEL_MEMACCESS,
+ CPE_CMD_PROC_INCOMING_MSG,
+ CPE_CMD_FTM_TEST,
+};
+
+enum cpe_process_result {
+ CPE_PROC_SUCCESS = 0,
+ CPE_PROC_FAILED,
+ CPE_PROC_KILLED,
+ CPE_PROC_QUEUED,
+};
+
+struct cpe_command_node {
+ enum cpe_command command;
+ enum cpe_svc_result result;
+ void *data;
+ struct list_head list;
+};
+
+struct cpe_info {
+ struct list_head main_queue;
+ struct completion cmd_complete;
+ struct completion thread_comp;
+ void *thread_handler;
+ bool stop_thread;
+ struct mutex msg_lock;
+ enum cpe_state state;
+ enum cpe_substate substate;
+ struct list_head client_list;
+ enum cpe_process_result (*cpe_process_command)
+ (struct cpe_command_node *command_node);
+ enum cpe_svc_result (*cpe_cmd_validate)
+ (const struct cpe_info *i,
+ enum cpe_command command);
+ enum cpe_svc_result (*cpe_start_notification)
+ (struct cpe_info *i);
+ u32 initialized;
+ struct cpe_svc_tgt_abstraction *tgt;
+ void *pending;
+ void *data;
+ void *client_context;
+ u32 codec_id;
+ struct work_struct clk_plan_work;
+ struct completion core_svc_cmd_compl;
+};
+
+struct cpe_tgt_waiti_info {
+ u8 tgt_waiti_size;
+ u8 *tgt_waiti_data;
+};
+
+struct cpe_svc_tgt_abstraction {
+ enum cpe_svc_result (*tgt_boot) (int debug_mode);
+
+ u32 (*tgt_cpar_init_done) (void);
+
+ u32 (*tgt_is_active) (void);
+
+ enum cpe_svc_result (*tgt_reset) (void);
+
+ enum cpe_svc_result (*tgt_stop)(void);
+
+ enum cpe_svc_result (*tgt_read_mailbox)
+ (u8 *buffer, size_t size);
+
+ enum cpe_svc_result (*tgt_write_mailbox)
+ (u8 *buffer, size_t size);
+
+ enum cpe_svc_result (*tgt_read_ram)
+ (struct cpe_info *c,
+ struct cpe_svc_mem_segment *data);
+
+ enum cpe_svc_result (*tgt_write_ram)
+ (struct cpe_info *c,
+ const struct cpe_svc_mem_segment *data);
+
+ enum cpe_svc_result (*tgt_route_notification)
+ (enum cpe_svc_module module,
+ enum cpe_svc_route_dest dest);
+
+ enum cpe_svc_result (*tgt_set_debug_mode) (u32 enable);
+ const struct cpe_svc_hw_cfg *(*tgt_get_cpe_info) (void);
+ enum cpe_svc_result (*tgt_deinit)
+ (struct cpe_svc_tgt_abstraction *param);
+ enum cpe_svc_result (*tgt_voice_tx_lab)
+ (bool);
+ u8 *inbox;
+ u8 *outbox;
+ struct cpe_tgt_waiti_info *tgt_waiti_info;
+};
+
+static enum cpe_svc_result cpe_tgt_tomtom_init(
+ struct cpe_svc_codec_info_v1 *codec_info,
+ struct cpe_svc_tgt_abstraction *param);
+
+static enum cpe_svc_result cpe_tgt_wcd9335_init(
+ struct cpe_svc_codec_info_v1 *codec_info,
+ struct cpe_svc_tgt_abstraction *param);
+
+struct cpe_send_msg {
+ u8 *payload;
+ u32 isobm;
+ u32 address;
+ size_t size;
+};
+
+struct cpe_read_handle {
+ void *registration;
+ struct cpe_info t_info;
+ struct list_head buffers;
+ void *config;
+};
+
+struct generic_notification {
+ void (*notification)
+ (const struct cpe_svc_notification *parameter);
+ void (*cmi_notification)
+ (const struct cmi_api_notification *parameter);
+};
+
+struct cpe_notif_node {
+ struct generic_notification notif;
+ u32 mask;
+ u32 service;
+ const struct cpe_info *context;
+ const char *name;
+ u32 disabled;
+ struct list_head list;
+};
+
+struct cpe_priv {
+ struct cpe_info *cpe_default_handle;
+ void (*cpe_irq_control_callback)(u32 enable);
+ void (*cpe_query_freq_plans_cb)
+ (void *cdc_priv,
+ struct cpe_svc_cfg_clk_plan *clk_freq);
+ void (*cpe_change_freq_plan_cb)(void *cdc_priv,
+ u32 clk_freq);
+ u32 cpe_msg_buffer;
+ void *cpe_cmi_handle;
+ struct mutex cpe_api_mutex;
+ struct mutex cpe_svc_lock;
+ struct cpe_svc_boot_event cpe_debug_vector;
+ void *cdc_priv;
+};
+
+static struct cpe_priv cpe_d;
+
+static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle);
+
+static enum cpe_svc_result cpe_is_command_valid(
+ const struct cpe_info *t_info,
+ enum cpe_command command);
+
+static int cpe_register_read(u32 reg, u8 *val)
+{
+ *(val) = snd_soc_read(cpe_d.cdc_priv, reg);
+ return 0;
+}
+
+static enum cpe_svc_result cpe_update_bits(u32 reg,
+ u32 mask, u32 value)
+{
+ int ret = 0;
+ ret = snd_soc_update_bits(cpe_d.cdc_priv, reg,
+ mask, value);
+ if (ret < 0)
+ return CPE_SVC_FAILED;
+
+ return CPE_SVC_SUCCESS;
+}
+
+static int cpe_register_write(u32 reg, u32 val)
+{
+ int ret = 0;
+
+ if (reg != TOMTOM_A_SVASS_MEM_BANK &&
+ reg != WCD9335_CPE_SS_MEM_BANK_0)
+ pr_debug("%s: reg = 0x%x, value = 0x%x\n",
+ __func__, reg, val);
+
+ ret = snd_soc_write(cpe_d.cdc_priv, reg, val);
+ if (ret < 0)
+ return CPE_SVC_FAILED;
+
+ return CPE_SVC_SUCCESS;
+}
+
+static int cpe_register_write_repeat(u32 reg, u8 *ptr, u32 to_write)
+{
+ struct snd_soc_codec *codec = cpe_d.cdc_priv;
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ int ret = 0;
+
+ ret = wcd9xxx_slim_write_repeat(wcd9xxx, reg, to_write, ptr);
+ if (ret != 0)
+ pr_err("%s: slim_write_repeat failed\n", __func__);
+
+ if (ret < 0)
+ return CPE_SVC_FAILED;
+
+ return CPE_SVC_SUCCESS;
+}
+
+static bool cpe_register_read_autoinc_supported(void)
+{
+ return true;
+}
+
+
+/* Called under msgq locked context */
+static void cpe_cmd_received(struct cpe_info *t_info)
+{
+ struct cpe_command_node *node = NULL;
+ enum cpe_process_result proc_rc = CPE_PROC_SUCCESS;
+ if (!t_info) {
+ pr_err("%s: Invalid thread info\n",
+ __func__);
+ return;
+ }
+
+ while (!list_empty(&t_info->main_queue)) {
+ if (proc_rc != CPE_PROC_SUCCESS)
+ break;
+ node = list_first_entry(&t_info->main_queue,
+ struct cpe_command_node, list);
+ if (!node)
+ break;
+ list_del(&node->list);
+ proc_rc = t_info->cpe_process_command(node);
+ pr_debug("%s: process command return %d\n",
+ __func__, proc_rc);
+
+ switch (proc_rc) {
+ case CPE_PROC_SUCCESS:
+ kfree(node);
+ break;
+ case CPE_PROC_FAILED:
+ kfree(node);
+ pr_err("%s: cmd failed\n", __func__);
+ break;
+ case CPE_PROC_KILLED:
+ break;
+ default:
+ list_add(&node->list, &(t_info->main_queue));
+
+ }
+ }
+}
+
+static int cpe_worker_thread(void *context)
+{
+ struct cpe_info *t_info = (struct cpe_info *)context;
+
+ /*
+ * Thread will run until requested to stop explicitly
+ * by setting the t_info->stop_thread flag
+ */
+ while (1) {
+ /* Wait for command to be processed */
+ wait_for_completion(&t_info->cmd_complete);
+
+ CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock");
+ cpe_cmd_received(t_info);
+ reinit_completion(&t_info->cmd_complete);
+ /* Check if thread needs to be stopped */
+ if (t_info->stop_thread)
+ goto unlock_and_exit;
+ CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
+ };
+
+unlock_and_exit:
+ pr_debug("%s: thread stopped\n", __func__);
+ CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
+ complete_and_exit(&t_info->thread_comp, 0);
+}
+
+static void cpe_create_worker_thread(struct cpe_info *t_info)
+{
+ INIT_LIST_HEAD(&t_info->main_queue);
+ init_completion(&t_info->cmd_complete);
+ init_completion(&t_info->thread_comp);
+ t_info->stop_thread = false;
+ t_info->thread_handler = kthread_run(cpe_worker_thread,
+ (void *)t_info, "cpe-worker-thread");
+ pr_debug("%s: Created new worker thread\n",
+ __func__);
+}
+
+static void cpe_cleanup_worker_thread(struct cpe_info *t_info)
+{
+ if (!t_info->thread_handler) {
+ pr_err("%s: thread not created\n", __func__);
+ return;
+ }
+
+ /*
+ * Wake up the command handler in case
+ * it is waiting for an command to be processed.
+ */
+ CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock");
+ t_info->stop_thread = true;
+ complete(&t_info->cmd_complete);
+ CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
+
+ /* Wait for the thread to exit */
+ wait_for_completion(&t_info->thread_comp);
+ t_info->thread_handler = NULL;
+
+ pr_debug("%s: Thread cleaned up successfully\n",
+ __func__);
+}
+
+static enum cpe_svc_result
+cpe_send_cmd_to_thread(struct cpe_info *t_info,
+ enum cpe_command command, void *data,
+ bool high_prio)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ struct cpe_command_node *cmd = NULL;
+
+ rc = cpe_is_command_valid(t_info, command);
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: Invalid command %d\n",
+ __func__, command);
+ return rc;
+ }
+
+ cmd = kzalloc(sizeof(struct cpe_command_node),
+ GFP_ATOMIC);
+ if (!cmd) {
+ pr_err("%s: No memory for cmd node, size = %zu\n",
+ __func__, sizeof(struct cpe_command_node));
+ return CPE_SVC_NO_MEMORY;
+ }
+
+ cmd->command = command;
+ cmd->data = data;
+
+ CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock");
+ if (high_prio)
+ list_add(&(cmd->list),
+ &(t_info->main_queue));
+ else
+ list_add_tail(&(cmd->list),
+ &(t_info->main_queue));
+ complete(&t_info->cmd_complete);
+ CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
+
+ return rc;
+}
+
+static enum cpe_svc_result cpe_change_state(
+ struct cpe_info *t_info,
+ enum cpe_state state, enum cpe_substate ss)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ t_info->state = state;
+ t_info->substate = ss;
+
+ pr_debug("%s: current state: %d,%d, new_state: %d,%d\n",
+ __func__, t_info->state, t_info->substate,
+ state, ss);
+
+ return rc;
+}
+
+static enum cpe_svc_result
+cpe_is_command_valid(const struct cpe_info *t_info,
+ enum cpe_command command)
+{
+ enum cpe_svc_result rc = CPE_SVC_INVALID_HANDLE;
+ if (t_info && t_info->cpe_cmd_validate)
+ rc = t_info->cpe_cmd_validate(t_info, command);
+ else
+ pr_err("%s: invalid handle or callback\n",
+ __func__);
+ return rc;
+}
+
+static void cpe_notify_client(struct cpe_notif_node *client,
+ struct cpe_svc_notification *payload)
+{
+ if (!client || !payload) {
+ pr_err("%s: invalid client or payload\n",
+ __func__);
+ return;
+ }
+
+ if (!(client->mask & payload->event)) {
+ pr_debug("%s: client mask 0x%x not registered for event 0x%x\n",
+ __func__, client->mask, payload->event);
+ return;
+ }
+
+ if (client->notif.notification && !client->disabled)
+ client->notif.notification(payload);
+
+ if ((client->mask & CPE_SVC_CMI_MSG) &&
+ client->notif.cmi_notification)
+ client->notif.cmi_notification(
+ (const struct cmi_api_notification *)payload);
+}
+
+static void cpe_broadcast_notification(const struct cpe_info *t_info,
+ struct cpe_svc_notification *payload)
+{
+ struct cpe_notif_node *n = NULL;
+
+ if (!t_info || !payload) {
+ pr_err("%s: invalid handle\n", __func__);
+ return;
+ }
+
+ pr_debug("%s: notify clients, event = %d\n",
+ __func__, payload->event);
+ payload->private_data = cpe_d.cdc_priv;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+ list_for_each_entry(n, &t_info->client_list, list) {
+ if (!(n->mask & CPE_SVC_CMI_MSG)) {
+ cpe_notify_client(n, payload);
+ }
+ }
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+}
+
+static void *cpe_register_generic(struct cpe_info *t_info,
+ void notification_callback(
+ const struct cpe_svc_notification *parameter),
+ void cmi_callback(
+ const struct cmi_api_notification *parameter),
+ u32 mask, u32 service, const char *name)
+{
+ struct cpe_notif_node *n = NULL;
+
+ n = kzalloc(sizeof(struct cpe_notif_node),
+ GFP_KERNEL);
+ if (!n) {
+ pr_err("%s: No memory for notification, size = %zu\n",
+ __func__, sizeof(struct cpe_notif_node));
+ return NULL;
+ }
+ n->mask = mask;
+ n->service = service;
+ n->notif.notification = notification_callback;
+ n->notif.cmi_notification = cmi_callback;
+ n->context = t_info;
+ n->disabled = false;
+ n->name = name;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+ /* Make sure CPE core service is first */
+ if (service == CMI_CPE_CORE_SERVICE_ID)
+ list_add(&n->list, &t_info->client_list);
+ else
+ list_add_tail(&n->list, &t_info->client_list);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+
+ return n;
+}
+
+static enum cpe_svc_result cpe_deregister_generic(struct cpe_info *t_info,
+ void *reg_handle)
+{
+ struct cpe_notif_node *n = (struct cpe_notif_node *)reg_handle;
+
+ if (!t_info || !reg_handle) {
+ pr_err("%s: invalid handle\n", __func__);
+ return CPE_SVC_INVALID_HANDLE;
+ }
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+ list_del(&(n->list));
+ kfree(reg_handle);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+
+ return CPE_SVC_SUCCESS;
+}
+
+static enum cpe_svc_result cpe_svc_tgt_init(struct cpe_svc_codec_info_v1 *i,
+ struct cpe_svc_tgt_abstraction *abs)
+{
+ if (!i || !abs) {
+ pr_err("%s: Incorrect information provided\n",
+ __func__);
+ return CPE_SVC_FAILED;
+ }
+
+ switch (i->id) {
+ case CPE_SVC_CODEC_TOMTOM:
+ return cpe_tgt_tomtom_init(i, abs);
+ case CPE_SVC_CODEC_WCD9335:
+ return cpe_tgt_wcd9335_init(i, abs);
+ default:
+ pr_err("%s: Codec type %d not supported\n",
+ __func__, i->id);
+ return CPE_SVC_FAILED;
+ }
+
+ return CPE_SVC_SUCCESS;
+}
+
+static void cpe_notify_cmi_client(struct cpe_info *t_info, u8 *payload,
+ enum cpe_svc_result result)
+{
+ struct cpe_notif_node *n = NULL;
+ struct cmi_api_notification notif;
+ struct cmi_hdr *hdr;
+ u8 service = 0;
+
+ if (!t_info || !payload) {
+ pr_err("%s: invalid payload/handle\n",
+ __func__);
+ return;
+ }
+
+ hdr = CMI_GET_HEADER(payload);
+ service = CMI_HDR_GET_SERVICE(hdr);
+
+ notif.event = (enum cmi_api_event)CPE_SVC_CMI_MSG;
+ notif.result = result;
+ notif.message = payload;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+ list_for_each_entry(n, &t_info->client_list, list) {
+
+ if ((n->mask & CPE_SVC_CMI_MSG) &&
+ n->service == service &&
+ n->notif.cmi_notification) {
+ n->notif.cmi_notification(&notif);
+ break;
+ }
+ }
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+}
+
+static void cpe_toggle_irq_notification(struct cpe_info *t_info, u32 value)
+{
+ if (cpe_d.cpe_irq_control_callback)
+ cpe_d.cpe_irq_control_callback(value);
+}
+
+static void cpe_command_cleanup(struct cpe_command_node *command_node)
+{
+ switch (command_node->command) {
+ case CPE_CMD_SEND_MSG:
+ case CPE_CMD_SEND_TRANS_MSG:
+ case CPE_CMD_SEND_MSG_COMPLETE:
+ case CPE_CMD_SHUTDOWN:
+ case CPE_CMD_READ:
+ kfree(command_node->data);
+ command_node->data = NULL;
+ break;
+ default:
+ pr_err("%s: unhandled command\n",
+ __func__);
+ break;
+ }
+}
+
+static enum cpe_svc_result cpe_send_msg_to_inbox(
+ struct cpe_info *t_info, u32 opcode,
+ struct cpe_send_msg *msg)
+{
+ size_t bytes = 0;
+ size_t inbox_size =
+ t_info->tgt->tgt_get_cpe_info()->inbox_size;
+ struct cmi_hdr *hdr;
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+ memset(t_info->tgt->inbox, 0, inbox_size);
+ hdr = CMI_GET_HEADER(t_info->tgt->inbox);
+ CMI_HDR_SET_SESSION(hdr, 1);
+ CMI_HDR_SET_SERVICE(hdr, CMI_CPE_CORE_SERVICE_ID);
+ CMI_HDR_SET_VERSION(hdr, CMI_DRIVER_SUPPORTED_VERSION);
+ CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND);
+
+ switch (opcode) {
+ case CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC: {
+ struct cmi_core_svc_cmd_shared_mem_alloc *m;
+ CMI_HDR_SET_OPCODE(hdr,
+ CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC);
+ CMI_HDR_SET_PAYLOAD_SIZE(hdr,
+ sizeof(struct cmi_core_svc_cmd_shared_mem_alloc));
+ m = (struct cmi_core_svc_cmd_shared_mem_alloc *)
+ CMI_GET_PAYLOAD(t_info->tgt->inbox);
+ m->size = CPE_MSG_BUFFER_SIZE;
+ pr_debug("send shared mem alloc msg to cpe inbox\n");
+ }
+ break;
+ case CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ:
+ CMI_HDR_SET_OPCODE(hdr,
+ CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ);
+ CMI_HDR_SET_PAYLOAD_SIZE(hdr, 0);
+ pr_debug("%s: Creating DRAM acces request msg\n",
+ __func__);
+ break;
+
+ case CPE_CMI_BASIC_RSP_OPCODE: {
+ struct cmi_basic_rsp_result *rsp;
+ CMI_HDR_SET_OPCODE(hdr,
+ CPE_CMI_BASIC_RSP_OPCODE);
+ CMI_HDR_SET_PAYLOAD_SIZE(hdr,
+ sizeof(struct cmi_basic_rsp_result));
+ rsp = (struct cmi_basic_rsp_result *)
+ CMI_GET_PAYLOAD(t_info->tgt->inbox);
+ rsp->status = 0;
+ pr_debug("%s: send basic response\n", __func__);
+ }
+ break;
+
+ default:
+ if (msg->address != 0) {
+ struct cmi_msg_transport *m = NULL;
+ struct cpe_svc_mem_segment mem_seg;
+
+ mem_seg.type = CPE_SVC_DATA_MEM;
+ if (msg->isobm) {
+ struct cmi_obm *obm = (struct cmi_obm *)
+ CMI_GET_PAYLOAD(msg->payload);
+ mem_seg.cpe_addr = obm->mem_handle;
+ mem_seg.data = (u8 *)obm->data_ptr.kvaddr;
+ mem_seg.size = obm->size;
+ t_info->tgt->tgt_write_ram(t_info, &mem_seg);
+ }
+
+ mem_seg.cpe_addr = msg->address;
+ mem_seg.data = msg->payload;
+ mem_seg.size = msg->size;
+ t_info->tgt->tgt_write_ram(t_info, &mem_seg);
+
+ hdr = CMI_GET_HEADER(t_info->tgt->inbox);
+ CMI_HDR_SET_OPCODE(hdr, CMI_MSG_TRANSPORT);
+ m = (struct cmi_msg_transport *)
+ CMI_GET_PAYLOAD(t_info->tgt->inbox);
+ m->addr = msg->address;
+ m->size = msg->size;
+ CMI_HDR_SET_PAYLOAD_SIZE(hdr,
+ sizeof(struct cmi_msg_transport));
+ } else {
+ memcpy(t_info->tgt->inbox, msg->payload,
+ msg->size);
+ }
+
+ break;
+ }
+
+ pr_debug("%s: sending message to cpe inbox\n",
+ __func__);
+ bytes = sizeof(struct cmi_hdr);
+ hdr = CMI_GET_HEADER(t_info->tgt->inbox);
+ bytes += CMI_HDR_GET_PAYLOAD_SIZE(hdr);
+ rc = t_info->tgt->tgt_write_mailbox(t_info->tgt->inbox, bytes);
+
+ return rc;
+}
+
+static bool cpe_is_cmd_clk_req(void *cmd)
+{
+ struct cmi_hdr *hdr;
+
+ hdr = CMI_GET_HEADER(cmd);
+
+ if ((CMI_HDR_GET_SERVICE(hdr) ==
+ CMI_CPE_CORE_SERVICE_ID)) {
+ if (CMI_GET_OPCODE(cmd) ==
+ CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST)
+ return true;
+ }
+
+ return false;
+}
+
+static enum cpe_svc_result cpe_process_clk_change_req(
+ struct cpe_info *t_info)
+{
+ struct cmi_core_svc_cmd_clk_freq_request *req;
+ req = (struct cmi_core_svc_cmd_clk_freq_request *)
+ CMI_GET_PAYLOAD(t_info->tgt->outbox);
+
+ if (!cpe_d.cpe_change_freq_plan_cb) {
+ pr_err("%s: No support for clk freq change\n",
+ __func__);
+ return CPE_SVC_FAILED;
+ }
+
+ cpe_d.cpe_change_freq_plan_cb(cpe_d.cdc_priv,
+ req->clk_freq);
+
+ /*send a basic response*/
+ cpe_send_msg_to_inbox(t_info,
+ CPE_CMI_BASIC_RSP_OPCODE, NULL);
+
+ return CPE_SVC_SUCCESS;
+}
+
+static void cpe_process_irq_int(u32 irq,
+ struct cpe_info *t_info)
+{
+ struct cpe_command_node temp_node;
+ struct cpe_send_msg *m;
+ u8 size = 0;
+ bool err_irq = false;
+ struct cmi_hdr *hdr;
+
+ pr_debug("%s: irq = %u\n", __func__, irq);
+
+ if (!t_info) {
+ pr_err("%s: Invalid handle\n",
+ __func__);
+ return;
+ }
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ switch (irq) {
+ case CPE_IRQ_OUTBOX_IRQ:
+ size = t_info->tgt->tgt_get_cpe_info()->outbox_size;
+ t_info->tgt->tgt_read_mailbox(t_info->tgt->outbox, size);
+ break;
+
+ case CPE_IRQ_MEM_ACCESS_ERROR:
+ err_irq = true;
+ cpe_change_state(t_info, CPE_STATE_OFFLINE, CPE_SS_IDLE);
+ break;
+
+ case CPE_IRQ_WDOG_BITE:
+ case CPE_IRQ_RCO_WDOG_INT:
+ err_irq = true;
+ __cpe_svc_shutdown(t_info);
+ break;
+
+ case CPE_IRQ_FLL_LOCK_LOST:
+ default:
+ err_irq = true;
+ break;
+ }
+
+ if (err_irq) {
+ pr_err("%s: CPE error IRQ %u occured\n",
+ __func__, irq);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return;
+ }
+
+ switch (t_info->state) {
+ case CPE_STATE_BOOTING:
+
+ switch (t_info->substate) {
+ case CPE_SS_BOOT:
+ temp_node.command = CPE_CMD_BOOT_INITIALIZE;
+ temp_node.result = CPE_SVC_SUCCESS;
+ t_info->substate = CPE_SS_BOOT_INIT;
+ t_info->cpe_process_command(&temp_node);
+ break;
+
+ case CPE_SS_BOOT_INIT:
+ temp_node.command = CPE_CMD_BOOT_COMPLETE;
+ temp_node.result = CPE_SVC_SUCCESS;
+ t_info->substate = CPE_SS_ONLINE;
+ t_info->cpe_process_command(&temp_node);
+ break;
+
+ default:
+ pr_debug("%s: unhandled substate %d for state %d\n",
+ __func__, t_info->state, t_info->substate);
+ break;
+ }
+ break;
+
+ case CPE_STATE_SENDING_MSG:
+ hdr = CMI_GET_HEADER(t_info->tgt->outbox);
+ if (CMI_GET_OPCODE(t_info->tgt->outbox) ==
+ CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2) {
+ pr_debug("%s: session_id: %u, state: %d,%d, event received\n",
+ __func__, CMI_HDR_GET_SESSION_ID(hdr),
+ t_info->state, t_info->substate);
+ temp_node.command = CPE_CMD_PROC_INCOMING_MSG;
+ temp_node.data = NULL;
+ t_info->cpe_process_command(&temp_node);
+ break;
+ }
+
+ m = (struct cpe_send_msg *)t_info->pending;
+
+ switch (t_info->substate) {
+ case CPE_SS_MSG_REQUEST_ACCESS:
+ cpe_send_cmd_to_thread(t_info,
+ CPE_CMD_SEND_TRANS_MSG, m, true);
+ break;
+
+ case CPE_SS_MSG_SEND_INBOX:
+ if (cpe_is_cmd_clk_req(t_info->tgt->outbox))
+ cpe_process_clk_change_req(t_info);
+ else
+ cpe_send_cmd_to_thread(t_info,
+ CPE_CMD_SEND_MSG_COMPLETE, m, true);
+ break;
+
+ default:
+ pr_debug("%s: unhandled substate %d for state %d\n",
+ __func__, t_info->state, t_info->substate);
+ break;
+ }
+ break;
+
+ case CPE_STATE_IDLE:
+ pr_debug("%s: Message received, notifying client\n",
+ __func__);
+ temp_node.command = CPE_CMD_PROC_INCOMING_MSG;
+ temp_node.data = NULL;
+ t_info->cpe_process_command(&temp_node);
+ break;
+
+ default:
+ pr_debug("%s: unhandled state %d\n",
+ __func__, t_info->state);
+ break;
+ }
+
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+}
+
+
+static void broacast_boot_failed(void)
+{
+ struct cpe_info *t_info = cpe_d.cpe_default_handle;
+ struct cpe_svc_notification payload;
+
+ payload.event = CPE_SVC_BOOT_FAILED;
+ payload.result = CPE_SVC_FAILED;
+ payload.payload = NULL;
+ if (t_info)
+ payload.private_data =
+ t_info->client_context;
+ cpe_broadcast_notification(t_info, &payload);
+}
+
+static enum cpe_svc_result broadcast_boot_event(
+ struct cpe_info *t_info)
+{
+ struct cpe_svc_notification payload;
+
+ payload.event = CPE_SVC_ONLINE;
+ payload.result = CPE_SVC_SUCCESS;
+ payload.payload = NULL;
+ if (t_info)
+ payload.private_data =
+ t_info->client_context;
+ cpe_broadcast_notification(t_info, &payload);
+
+ return CPE_SVC_SUCCESS;
+}
+
+static enum cpe_process_result cpe_boot_initialize(struct cpe_info *t_info,
+ enum cpe_svc_result *cpe_rc)
+{
+ enum cpe_process_result rc = CPE_SVC_FAILED;
+ struct cpe_svc_notification payload;
+ struct cmi_core_svc_event_system_boot *p = NULL;
+
+ if (CMI_GET_OPCODE(t_info->tgt->outbox) !=
+ CPE_CORE_SVC_EVENT_SYSTEM_BOOT) {
+ broacast_boot_failed();
+ return rc;
+ }
+
+ p = (struct cmi_core_svc_event_system_boot *)
+ CMI_GET_PAYLOAD(t_info->tgt->outbox);
+ if (p->status != CPE_BOOT_SUCCESS) {
+ pr_err("%s: cpe boot failed, status = %d\n",
+ __func__, p->status);
+ broacast_boot_failed();
+ return rc;
+ }
+
+ /* boot was successful */
+ if (p->version ==
+ CPE_CORE_VERSION_SYSTEM_BOOT_EVENT) {
+ cpe_d.cpe_debug_vector.debug_address =
+ p->sfr_buff_address;
+ cpe_d.cpe_debug_vector.debug_buffer_size =
+ p->sfr_buff_size;
+ cpe_d.cpe_debug_vector.status = p->status;
+ payload.event = CPE_SVC_BOOT;
+ payload.result = CPE_SVC_SUCCESS;
+ payload.payload = (void *)&cpe_d.cpe_debug_vector;
+ payload.private_data = t_info->client_context;
+ cpe_broadcast_notification(t_info, &payload);
+ }
+ cpe_change_state(t_info, CPE_STATE_BOOTING,
+ CPE_SS_BOOT_INIT);
+ (*cpe_rc) = cpe_send_msg_to_inbox(t_info,
+ CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC, NULL);
+ rc = CPE_PROC_SUCCESS;
+ return rc;
+}
+
+static void cpe_svc_core_cmi_handler(
+ const struct cmi_api_notification *parameter)
+{
+ struct cmi_hdr *hdr;
+
+ if (!parameter)
+ return;
+
+ pr_debug("%s: event = %d\n",
+ __func__, parameter->event);
+
+ if (parameter->event != CMI_API_MSG)
+ return;
+
+ hdr = (struct cmi_hdr *) parameter->message;
+
+ if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) {
+ struct cmi_basic_rsp_result *result;
+
+ result = (struct cmi_basic_rsp_result *)
+ ((u8 *)parameter->message) + (sizeof(*hdr));
+ if (result->status)
+ pr_err("%s: error response, error code = %u\n",
+ __func__, result->status);
+ complete(&cpe_d.cpe_default_handle->core_svc_cmd_compl);
+ }
+}
+
+static void cpe_clk_plan_work(struct work_struct *work)
+{
+ struct cpe_info *t_info = NULL;
+ size_t size = 0;
+ struct cpe_svc_cfg_clk_plan plan;
+ u8 *cmi_msg;
+ struct cmi_hdr *hdr;
+ int rc;
+
+ t_info = container_of(work, struct cpe_info, clk_plan_work);
+ if (!t_info) {
+ pr_err("%s: Invalid handle for cpe_info\n",
+ __func__);
+ return;
+ }
+
+ /* Register the core service */
+ cpe_d.cpe_cmi_handle = cmi_register(
+ cpe_svc_core_cmi_handler,
+ CMI_CPE_CORE_SERVICE_ID);
+
+ /* send the clk plan command */
+ if (!cpe_d.cpe_query_freq_plans_cb) {
+ pr_err("%s: No support for querying clk plans\n",
+ __func__);
+ return;
+ }
+
+ cpe_d.cpe_query_freq_plans_cb(cpe_d.cdc_priv, &plan);
+ size = sizeof(plan.current_clk_feq) +
+ sizeof(plan.num_clk_freqs);
+ size += plan.num_clk_freqs *
+ sizeof(plan.clk_freqs[0]);
+ cmi_msg = kzalloc(size + sizeof(struct cmi_hdr),
+ GFP_KERNEL);
+ if (!cmi_msg) {
+ pr_err("%s: no memory for cmi_msg\n",
+ __func__);
+ return;
+ }
+
+ hdr = (struct cmi_hdr *) cmi_msg;
+ CMI_HDR_SET_OPCODE(hdr,
+ CPE_CORE_SVC_CMD_CFG_CLK_PLAN);
+ CMI_HDR_SET_SERVICE(hdr, CMI_CPE_CORE_SERVICE_ID);
+ CMI_HDR_SET_SESSION(hdr, 1);
+ CMI_HDR_SET_VERSION(hdr, CMI_DRIVER_SUPPORTED_VERSION);
+ CMI_HDR_SET_PAYLOAD_SIZE(hdr, size);
+ memcpy(CMI_GET_PAYLOAD(cmi_msg), &plan,
+ size);
+ cmi_send_msg(cmi_msg);
+
+ /* Wait for clk plan command to complete */
+ rc = wait_for_completion_timeout(&t_info->core_svc_cmd_compl,
+ (10 * HZ));
+ if (!rc) {
+ pr_err("%s: clk plan cmd timed out\n",
+ __func__);
+ goto cmd_fail;
+ }
+
+ /* clk plan cmd is successful, send start notification */
+ if (t_info->cpe_start_notification)
+ t_info->cpe_start_notification(t_info);
+ else
+ pr_err("%s: no start notification\n",
+ __func__);
+
+cmd_fail:
+ kfree(cmi_msg);
+ cmi_deregister(cpe_d.cpe_cmi_handle);
+}
+
+static enum cpe_process_result cpe_boot_complete(
+ struct cpe_info *t_info)
+{
+ struct cmi_core_svc_cmdrsp_shared_mem_alloc *p = NULL;
+
+ if (CMI_GET_OPCODE(t_info->tgt->outbox) !=
+ CPE_CORE_SVC_CMDRSP_SHARED_MEM_ALLOC) {
+ broacast_boot_failed();
+ return CPE_PROC_FAILED;
+ }
+
+ p = (struct cmi_core_svc_cmdrsp_shared_mem_alloc *)
+ CMI_GET_PAYLOAD(t_info->tgt->outbox);
+ cpe_d.cpe_msg_buffer = p->addr;
+
+ if (cpe_d.cpe_msg_buffer == 0) {
+ pr_err("%s: Invalid cpe buffer for message\n",
+ __func__);
+ broacast_boot_failed();
+ return CPE_PROC_FAILED;
+ }
+
+ cpe_change_state(t_info, CPE_STATE_IDLE, CPE_SS_IDLE);
+ cpe_create_worker_thread(t_info);
+
+ if (t_info->codec_id != CPE_SVC_CODEC_TOMTOM) {
+ schedule_work(&t_info->clk_plan_work);
+ } else {
+ if (t_info->cpe_start_notification)
+ t_info->cpe_start_notification(t_info);
+ else
+ pr_err("%s: no start notification\n",
+ __func__);
+ }
+
+ pr_debug("%s: boot complete\n", __func__);
+ return CPE_PROC_SUCCESS;
+}
+
+static enum cpe_process_result cpe_process_send_msg(
+ struct cpe_info *t_info,
+ enum cpe_svc_result *cpe_rc,
+ struct cpe_command_node *command_node)
+{
+ enum cpe_process_result rc = CPE_PROC_SUCCESS;
+ struct cpe_send_msg *m =
+ (struct cpe_send_msg *)command_node->data;
+ u32 size = m->size;
+
+ if (t_info->pending) {
+ pr_debug("%s: message queued\n", __func__);
+ *cpe_rc = CPE_SVC_SUCCESS;
+ return CPE_PROC_QUEUED;
+ }
+
+ pr_debug("%s: Send CMI message, size = %u\n",
+ __func__, size);
+
+ if (size <= t_info->tgt->tgt_get_cpe_info()->inbox_size) {
+ pr_debug("%s: Msg fits mailbox, size %u\n",
+ __func__, size);
+ cpe_change_state(t_info, CPE_STATE_SENDING_MSG,
+ CPE_SS_MSG_SEND_INBOX);
+ t_info->pending = m;
+ *cpe_rc = cpe_send_msg_to_inbox(t_info, 0, m);
+ } else if (size < CPE_MSG_BUFFER_SIZE) {
+ m->address = cpe_d.cpe_msg_buffer;
+ pr_debug("%s: Message req CMI mem access\n",
+ __func__);
+ t_info->pending = m;
+ cpe_change_state(t_info, CPE_STATE_SENDING_MSG,
+ CPE_SS_MSG_REQUEST_ACCESS);
+ *cpe_rc = cpe_send_msg_to_inbox(t_info,
+ CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ, m);
+ } else {
+ pr_debug("%s: Invalid msg size %u\n",
+ __func__, size);
+ cpe_command_cleanup(command_node);
+ rc = CPE_PROC_FAILED;
+ cpe_change_state(t_info, CPE_STATE_IDLE,
+ CPE_SS_IDLE);
+ }
+
+ return rc;
+}
+
+static enum cpe_process_result cpe_process_incoming(
+ struct cpe_info *t_info)
+{
+ enum cpe_process_result rc = CPE_PROC_FAILED;
+ struct cmi_hdr *hdr;
+
+ hdr = CMI_GET_HEADER(t_info->tgt->outbox);
+
+ if (CMI_HDR_GET_SERVICE(hdr) ==
+ CMI_CPE_CORE_SERVICE_ID) {
+ pr_debug("%s: core service message received\n",
+ __func__);
+
+ switch (CMI_GET_OPCODE(t_info->tgt->outbox)) {
+ case CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST:
+ cpe_process_clk_change_req(t_info);
+ rc = CPE_PROC_SUCCESS;
+ break;
+ case CMI_MSG_TRANSPORT:
+ pr_debug("%s: transport msg received\n",
+ __func__);
+ rc = CPE_PROC_SUCCESS;
+ break;
+ case CPE_CMI_BASIC_RSP_OPCODE:
+ pr_debug("%s: received basic rsp\n",
+ __func__);
+ rc = CPE_PROC_SUCCESS;
+ break;
+ default:
+ pr_debug("%s: unknown message received\n",
+ __func__);
+ break;
+ }
+ } else {
+ /* if service id if for a CMI client, notify client */
+ pr_debug("%s: Message received, notifying client\n",
+ __func__);
+ cpe_notify_cmi_client(t_info,
+ t_info->tgt->outbox, CPE_SVC_SUCCESS);
+ rc = CPE_PROC_SUCCESS;
+ }
+
+ return rc;
+}
+
+static enum cpe_process_result cpe_process_kill_thread(
+ struct cpe_info *t_info,
+ struct cpe_command_node *command_node)
+{
+ struct cpe_svc_notification payload;
+
+ cpe_d.cpe_msg_buffer = 0;
+ payload.result = CPE_SVC_SHUTTING_DOWN;
+ payload.event = CPE_SVC_OFFLINE;
+ payload.payload = NULL;
+ payload.private_data = t_info->client_context;
+ /*
+ * Make state as offline before broadcasting
+ * the message to clients.
+ */
+ cpe_change_state(t_info, CPE_STATE_OFFLINE,
+ CPE_SS_IDLE);
+ cpe_broadcast_notification(t_info, &payload);
+
+ return CPE_PROC_KILLED;
+}
+
+static enum cpe_process_result cpe_mt_process_cmd(
+ struct cpe_command_node *command_node)
+{
+ struct cpe_info *t_info = cpe_d.cpe_default_handle;
+ enum cpe_svc_result cpe_rc = CPE_SVC_SUCCESS;
+ enum cpe_process_result rc = CPE_PROC_SUCCESS;
+ struct cpe_send_msg *m;
+ struct cmi_hdr *hdr;
+ u8 service = 0;
+ u8 retries = 0;
+
+ if (!t_info || !command_node) {
+ pr_err("%s: Invalid handle/command node\n",
+ __func__);
+ return CPE_PROC_FAILED;
+ }
+
+ pr_debug("%s: cmd = %u\n", __func__, command_node->command);
+
+ cpe_rc = cpe_is_command_valid(t_info, command_node->command);
+
+ if (cpe_rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: Invalid command %d, err = %d\n",
+ __func__, command_node->command, cpe_rc);
+ return CPE_PROC_FAILED;
+ }
+
+ switch (command_node->command) {
+
+ case CPE_CMD_BOOT_INITIALIZE:
+ rc = cpe_boot_initialize(t_info, &cpe_rc);
+ break;
+
+ case CPE_CMD_BOOT_COMPLETE:
+ rc = cpe_boot_complete(t_info);
+ break;
+
+ case CPE_CMD_SEND_MSG:
+ rc = cpe_process_send_msg(t_info, &cpe_rc,
+ command_node);
+ break;
+
+ case CPE_CMD_SEND_TRANS_MSG:
+ m = (struct cpe_send_msg *)command_node->data;
+
+ while (retries < CPE_SVC_INACTIVE_STATE_RETRIES_MAX) {
+ if (t_info->tgt->tgt_is_active()) {
+ ++retries;
+ /* Wait for CPE to be inactive */
+ usleep_range(5000, 5100);
+ } else {
+ break;
+ }
+ }
+
+ pr_debug("%s: cpe inactive after %d attempts\n",
+ __func__, retries);
+
+ cpe_change_state(t_info, CPE_STATE_SENDING_MSG,
+ CPE_SS_MSG_SEND_INBOX);
+ rc = cpe_send_msg_to_inbox(t_info, 0, m);
+ break;
+
+ case CPE_CMD_SEND_MSG_COMPLETE:
+ hdr = CMI_GET_HEADER(t_info->tgt->outbox);
+ service = CMI_HDR_GET_SERVICE(hdr);
+ pr_debug("%s: msg send success, notifying clients\n",
+ __func__);
+ cpe_command_cleanup(command_node);
+ t_info->pending = NULL;
+ cpe_change_state(t_info,
+ CPE_STATE_IDLE, CPE_SS_IDLE);
+ cpe_notify_cmi_client(t_info,
+ t_info->tgt->outbox, CPE_SVC_SUCCESS);
+ break;
+
+ case CPE_CMD_PROC_INCOMING_MSG:
+ rc = cpe_process_incoming(t_info);
+ break;
+
+ case CPE_CMD_KILL_THREAD:
+ rc = cpe_process_kill_thread(t_info, command_node);
+ break;
+
+ default:
+ pr_err("%s: unhandled cpe cmd = %d\n",
+ __func__, command_node->command);
+ break;
+ }
+
+ if (cpe_rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: failed to execute command\n", __func__);
+ if (t_info->pending) {
+ m = (struct cpe_send_msg *)t_info->pending;
+ cpe_notify_cmi_client(t_info, m->payload,
+ CPE_SVC_FAILED);
+ t_info->pending = NULL;
+ }
+
+ cpe_command_cleanup(command_node);
+ rc = CPE_PROC_FAILED;
+ cpe_change_state(t_info, CPE_STATE_IDLE,
+ CPE_SS_IDLE);
+ }
+
+ return rc;
+}
+
+static enum cpe_svc_result cpe_mt_validate_cmd(
+ const struct cpe_info *t_info,
+ enum cpe_command command)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+ if ((t_info == NULL) || t_info->initialized == false) {
+ pr_err("%s: cpe service is not ready\n",
+ __func__);
+ return CPE_SVC_NOT_READY;
+ }
+
+ switch (t_info->state) {
+ case CPE_STATE_UNINITIALIZED:
+ case CPE_STATE_INITIALIZED:
+ switch (command) {
+ case CPE_CMD_RESET:
+ case CPE_CMD_DL_SEGMENT:
+ case CPE_CMD_RAMDUMP:
+ case CPE_CMD_PROCESS_IRQ:
+ case CPE_CMD_KILL_THREAD:
+ case CPE_CMD_DEINITIALIZE:
+ case CPE_CMD_FTM_TEST:
+ rc = CPE_SVC_SUCCESS;
+ break;
+ default:
+ rc = CPE_SVC_NOT_READY;
+ break;
+ }
+ break;
+
+ case CPE_STATE_DOWNLOADING:
+ switch (command) {
+ case CPE_CMD_RESET:
+ case CPE_CMD_DL_SEGMENT:
+ case CPE_CMD_BOOT:
+ case CPE_CMD_FTM_TEST:
+ rc = CPE_SVC_SUCCESS;
+ break;
+ default:
+ rc = CPE_SVC_NOT_READY;
+ break;
+ }
+ break;
+
+ case CPE_STATE_BOOTING:
+ switch (command) {
+ case CPE_CMD_PROCESS_IRQ:
+ case CPE_CMD_BOOT_INITIALIZE:
+ case CPE_CMD_BOOT_COMPLETE:
+ case CPE_CMD_SHUTDOWN:
+ rc = CPE_SVC_SUCCESS;
+ break;
+ case CPE_CMD_FTM_TEST:
+ rc = CPE_SVC_BUSY;
+ break;
+ default:
+ rc = CPE_SVC_NOT_READY;
+ break;
+ }
+ break;
+
+ case CPE_STATE_IDLE:
+ switch (command) {
+ case CPE_CMD_SEND_MSG:
+ case CPE_CMD_SEND_TRANS_MSG:
+ case CPE_CMD_SEND_MSG_COMPLETE:
+ case CPE_CMD_PROCESS_IRQ:
+ case CPE_CMD_RESET:
+ case CPE_CMD_SHUTDOWN:
+ case CPE_CMD_KILL_THREAD:
+ case CPE_CMD_PROC_INCOMING_MSG:
+ rc = CPE_SVC_SUCCESS;
+ break;
+ case CPE_CMD_FTM_TEST:
+ rc = CPE_SVC_BUSY;
+ break;
+ default:
+ rc = CPE_SVC_FAILED;
+ break;
+ }
+ break;
+
+ case CPE_STATE_SENDING_MSG:
+ switch (command) {
+ case CPE_CMD_SEND_MSG:
+ case CPE_CMD_SEND_TRANS_MSG:
+ case CPE_CMD_SEND_MSG_COMPLETE:
+ case CPE_CMD_PROCESS_IRQ:
+ case CPE_CMD_SHUTDOWN:
+ case CPE_CMD_KILL_THREAD:
+ case CPE_CMD_PROC_INCOMING_MSG:
+ rc = CPE_SVC_SUCCESS;
+ break;
+ case CPE_CMD_FTM_TEST:
+ rc = CPE_SVC_BUSY;
+ break;
+ default:
+ rc = CPE_SVC_FAILED;
+ break;
+ }
+ break;
+
+ case CPE_STATE_OFFLINE:
+ switch (command) {
+ case CPE_CMD_RESET:
+ case CPE_CMD_RAMDUMP:
+ case CPE_CMD_KILL_THREAD:
+ rc = CPE_SVC_SUCCESS;
+ break;
+ default:
+ rc = CPE_SVC_NOT_READY;
+ break;
+ }
+ break;
+
+ default:
+ pr_debug("%s: unhandled state %d\n",
+ __func__, t_info->state);
+ break;
+ }
+
+ if (rc != CPE_SVC_SUCCESS)
+ pr_err("%s: invalid command %d, state = %d\n",
+ __func__, command, t_info->state);
+ return rc;
+}
+
+void *cpe_svc_initialize(
+ void irq_control_callback(u32 enable),
+ const void *codec_info, void *context)
+{
+ struct cpe_info *t_info = NULL;
+ const struct cpe_svc_hw_cfg *cap = NULL;
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ struct cpe_svc_init_param *init_context =
+ (struct cpe_svc_init_param *) context;
+ void *client_context = NULL;
+
+ if (cpe_d.cpe_default_handle &&
+ cpe_d.cpe_default_handle->initialized == true)
+ return (void *)cpe_d.cpe_default_handle;
+ cpe_d.cpe_query_freq_plans_cb = NULL;
+ cpe_d.cpe_change_freq_plan_cb = NULL;
+
+ if (context) {
+ client_context = init_context->context;
+ switch (init_context->version) {
+ case CPE_SVC_INIT_PARAM_V1:
+ cpe_d.cpe_query_freq_plans_cb =
+ init_context->query_freq_plans_cb;
+ cpe_d.cpe_change_freq_plan_cb =
+ init_context->change_freq_plan_cb;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!cpe_d.cpe_default_handle) {
+ cpe_d.cpe_default_handle = kzalloc(sizeof(struct cpe_info),
+ GFP_KERNEL);
+ if (!cpe_d.cpe_default_handle) {
+ pr_err("%s: no memory for cpe handle, size = %zu\n",
+ __func__, sizeof(struct cpe_info));
+ goto err_register;
+ }
+
+ memset(cpe_d.cpe_default_handle, 0,
+ sizeof(struct cpe_info));
+ }
+
+ t_info = cpe_d.cpe_default_handle;
+ t_info->client_context = client_context;
+
+ INIT_LIST_HEAD(&t_info->client_list);
+ cpe_d.cdc_priv = client_context;
+ INIT_WORK(&t_info->clk_plan_work, cpe_clk_plan_work);
+ init_completion(&t_info->core_svc_cmd_compl);
+
+ t_info->tgt = kzalloc(sizeof(struct cpe_svc_tgt_abstraction),
+ GFP_KERNEL);
+ if (!t_info->tgt) {
+ pr_err("%s: target allocation failed, size = %zu\n",
+ __func__,
+ sizeof(struct cpe_svc_tgt_abstraction));
+ goto err_tgt_alloc;
+ }
+ t_info->codec_id =
+ ((struct cpe_svc_codec_info_v1 *) codec_info)->id;
+
+ rc = cpe_svc_tgt_init((struct cpe_svc_codec_info_v1 *)codec_info,
+ t_info->tgt);
+
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: target initialization failed, err = %d\n",
+ __func__, rc);
+ goto err_tgt_init;
+ }
+
+ cap = t_info->tgt->tgt_get_cpe_info();
+
+ memset(t_info->tgt->outbox, 0, cap->outbox_size);
+ memset(t_info->tgt->inbox, 0, cap->inbox_size);
+ mutex_init(&t_info->msg_lock);
+ cpe_d.cpe_irq_control_callback = irq_control_callback;
+ t_info->cpe_process_command = cpe_mt_process_cmd;
+ t_info->cpe_cmd_validate = cpe_mt_validate_cmd;
+ t_info->cpe_start_notification = broadcast_boot_event;
+ mutex_init(&cpe_d.cpe_api_mutex);
+ mutex_init(&cpe_d.cpe_svc_lock);
+ pr_debug("%s: cpe services initialized\n", __func__);
+ t_info->state = CPE_STATE_INITIALIZED;
+ t_info->initialized = true;
+
+ return t_info;
+
+err_tgt_init:
+ kfree(t_info->tgt);
+
+err_tgt_alloc:
+ kfree(cpe_d.cpe_default_handle);
+ cpe_d.cpe_default_handle = NULL;
+
+err_register:
+ return NULL;
+}
+
+enum cpe_svc_result cpe_svc_deinitialize(void *cpe_handle)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ rc = cpe_is_command_valid(t_info, CPE_CMD_DEINITIALIZE);
+
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: Invalid command %d\n",
+ __func__, CPE_CMD_DEINITIALIZE);
+ return rc;
+ }
+
+ if (cpe_d.cpe_default_handle == t_info)
+ cpe_d.cpe_default_handle = NULL;
+
+ t_info->tgt->tgt_deinit(t_info->tgt);
+ cpe_change_state(t_info, CPE_STATE_UNINITIALIZED,
+ CPE_SS_IDLE);
+ mutex_destroy(&t_info->msg_lock);
+ kfree(t_info->tgt);
+ kfree(t_info);
+ mutex_destroy(&cpe_d.cpe_api_mutex);
+ mutex_destroy(&cpe_d.cpe_svc_lock);
+
+ return rc;
+}
+
+void *cpe_svc_register(void *cpe_handle,
+ void (*notification_callback)
+ (const struct cpe_svc_notification *parameter),
+ u32 mask, const char *name)
+{
+ void *reg_handle;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ if (!cpe_d.cpe_default_handle) {
+ cpe_d.cpe_default_handle = kzalloc(sizeof(struct cpe_info),
+ GFP_KERNEL);
+ if (!cpe_d.cpe_default_handle) {
+ pr_err("%s: no_mem for cpe handle, sz = %zu\n",
+ __func__, sizeof(struct cpe_info));
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return NULL;
+ }
+
+ memset(cpe_d.cpe_default_handle, 0,
+ sizeof(struct cpe_info));
+ }
+
+ if (!cpe_handle)
+ cpe_handle = cpe_d.cpe_default_handle;
+
+ reg_handle = cpe_register_generic((struct cpe_info *)cpe_handle,
+ notification_callback,
+ NULL,
+ mask, CPE_NO_SERVICE, name);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+ return reg_handle;
+}
+
+enum cpe_svc_result cpe_svc_deregister(void *cpe_handle, void *reg_handle)
+{
+ enum cpe_svc_result rc;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ if (!cpe_handle)
+ cpe_handle = cpe_d.cpe_default_handle;
+
+ rc = cpe_deregister_generic((struct cpe_info *)cpe_handle,
+ reg_handle);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+ return rc;
+}
+
+enum cpe_svc_result cpe_svc_download_segment(void *cpe_handle,
+ const struct cpe_svc_mem_segment *segment)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ rc = cpe_is_command_valid(t_info, CPE_CMD_DL_SEGMENT);
+
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: cmd validation fail, cmd = %d\n",
+ __func__, CPE_CMD_DL_SEGMENT);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return rc;
+ }
+
+ cpe_toggle_irq_notification(t_info, false);
+ t_info->state = CPE_STATE_DOWNLOADING;
+ t_info->substate = CPE_SS_DL_DOWNLOADING;
+ rc = t_info->tgt->tgt_write_ram(t_info, segment);
+ cpe_toggle_irq_notification(t_info, true);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+ return rc;
+}
+
+enum cpe_svc_result cpe_svc_boot(void *cpe_handle, int debug_mode)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ rc = cpe_is_command_valid(t_info, CPE_CMD_BOOT);
+
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: cmd validation fail, cmd = %d\n",
+ __func__, CPE_CMD_BOOT);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return rc;
+ }
+
+ if (rc == CPE_SVC_SUCCESS) {
+ t_info->tgt->tgt_boot(debug_mode);
+ t_info->state = CPE_STATE_BOOTING;
+ t_info->substate = CPE_SS_BOOT;
+ pr_debug("%s: cpe service booting\n",
+ __func__);
+ }
+
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return rc;
+}
+
+enum cpe_svc_result cpe_svc_process_irq(void *cpe_handle, u32 cpe_irq)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ cpe_toggle_irq_notification(t_info, false);
+ cpe_process_irq_int(cpe_irq, t_info);
+ cpe_toggle_irq_notification(t_info, true);
+
+ return rc;
+}
+
+enum cpe_svc_result cpe_svc_route_notification(void *cpe_handle,
+ enum cpe_svc_module module, enum cpe_svc_route_dest dest)
+{
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+ enum cpe_svc_result rc = CPE_SVC_NOT_READY;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ if (t_info->tgt)
+ rc = t_info->tgt->tgt_route_notification(module, dest);
+
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return rc;
+}
+
+static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+ struct cpe_command_node *n = NULL;
+ struct cpe_command_node kill_cmd;
+
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ rc = cpe_is_command_valid(t_info, CPE_CMD_SHUTDOWN);
+
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: cmd validation fail, cmd = %d\n",
+ __func__, CPE_CMD_SHUTDOWN);
+ return rc;
+ }
+
+ while (!list_empty(&t_info->main_queue)) {
+ n = list_first_entry(&t_info->main_queue,
+ struct cpe_command_node, list);
+
+ if (n->command == CPE_CMD_SEND_MSG) {
+ cpe_notify_cmi_client(t_info, (u8 *)n->data,
+ CPE_SVC_SHUTTING_DOWN);
+ }
+ /*
+ * Since command cannot be processed,
+ * delete it from the list and perform cleanup
+ */
+ list_del(&n->list);
+ cpe_command_cleanup(n);
+ kfree(n);
+ }
+
+ pr_debug("%s: cpe service OFFLINE state\n", __func__);
+
+ t_info->state = CPE_STATE_OFFLINE;
+ t_info->substate = CPE_SS_IDLE;
+
+ memset(&kill_cmd, 0, sizeof(kill_cmd));
+ kill_cmd.command = CPE_CMD_KILL_THREAD;
+
+ if (t_info->pending) {
+ struct cpe_send_msg *m =
+ (struct cpe_send_msg *)t_info->pending;
+ cpe_notify_cmi_client(t_info, m->payload,
+ CPE_SVC_SHUTTING_DOWN);
+ kfree(t_info->pending);
+ t_info->pending = NULL;
+ }
+
+ cpe_cleanup_worker_thread(t_info);
+ t_info->cpe_process_command(&kill_cmd);
+
+ return rc;
+}
+
+enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ rc = __cpe_svc_shutdown(cpe_handle);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return rc;
+}
+
+enum cpe_svc_result cpe_svc_reset(void *cpe_handle)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ rc = cpe_is_command_valid(t_info, CPE_CMD_RESET);
+
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: cmd validation fail, cmd = %d\n",
+ __func__, CPE_CMD_RESET);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return rc;
+ }
+
+ if (t_info && t_info->tgt) {
+ rc = t_info->tgt->tgt_reset();
+ pr_debug("%s: cpe services in INITIALIZED state\n",
+ __func__);
+ t_info->state = CPE_STATE_INITIALIZED;
+ t_info->substate = CPE_SS_IDLE;
+ }
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+ return rc;
+}
+
+enum cpe_svc_result cpe_svc_ramdump(void *cpe_handle,
+ struct cpe_svc_mem_segment *buffer)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ rc = cpe_is_command_valid(t_info, CPE_CMD_RAMDUMP);
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: cmd validation fail, cmd = %d\n",
+ __func__, CPE_CMD_RAMDUMP);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return rc;
+ }
+
+ if (t_info->tgt) {
+ rc = t_info->tgt->tgt_read_ram(t_info, buffer);
+ } else {
+ pr_err("%s: cpe service not ready\n", __func__);
+ rc = CPE_SVC_NOT_READY;
+ }
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+ return rc;
+}
+
+enum cpe_svc_result cpe_svc_set_debug_mode(void *cpe_handle, u32 mode)
+{
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+ enum cpe_svc_result rc = CPE_SVC_INVALID_HANDLE;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ if (t_info->tgt)
+ rc = t_info->tgt->tgt_set_debug_mode(mode);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+ return rc;
+}
+
+const struct cpe_svc_hw_cfg *cpe_svc_get_hw_cfg(void *cpe_handle)
+{
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ if (t_info->tgt)
+ return t_info->tgt->tgt_get_cpe_info();
+
+ return NULL;
+}
+
+void *cmi_register(
+ void notification_callback(
+ const struct cmi_api_notification *parameter),
+ u32 service)
+{
+ void *reg_handle = NULL;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ reg_handle = cpe_register_generic(cpe_d.cpe_default_handle,
+ NULL,
+ notification_callback,
+ (CPE_SVC_CMI_MSG | CPE_SVC_OFFLINE |
+ CPE_SVC_ONLINE),
+ service,
+ "CMI_CLIENT");
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+ return reg_handle;
+}
+
+enum cmi_api_result cmi_deregister(void *reg_handle)
+{
+ u32 clients = 0;
+ struct cpe_notif_node *n = NULL;
+ enum cmi_api_result rc = CMI_API_SUCCESS;
+ struct cpe_svc_notification payload;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ rc = (enum cmi_api_result) cpe_deregister_generic(
+ cpe_d.cpe_default_handle, reg_handle);
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+ list_for_each_entry(n, &cpe_d.cpe_default_handle->client_list, list) {
+ if (n->mask & CPE_SVC_CMI_MSG)
+ clients++;
+ }
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+
+ if (clients == 0) {
+ payload.event = CPE_SVC_CMI_CLIENTS_DEREG;
+ payload.payload = NULL;
+ payload.result = CPE_SVC_SUCCESS;
+ cpe_broadcast_notification(cpe_d.cpe_default_handle, &payload);
+ }
+
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return rc;
+}
+
+enum cmi_api_result cmi_send_msg(void *message)
+{
+ enum cmi_api_result rc = CMI_API_SUCCESS;
+ struct cpe_send_msg *msg = NULL;
+ struct cmi_hdr *hdr;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ hdr = CMI_GET_HEADER(message);
+ msg = kzalloc(sizeof(struct cpe_send_msg),
+ GFP_ATOMIC);
+ if (!msg) {
+ pr_err("%s: no memory for cmi msg, sz = %zu\n",
+ __func__, sizeof(struct cpe_send_msg));
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return CPE_SVC_NO_MEMORY;
+ }
+
+ if (CMI_HDR_GET_OBM_FLAG(hdr) == CMI_OBM_FLAG_OUT_BAND)
+ msg->isobm = 1;
+ else
+ msg->isobm = 0;
+
+ msg->size = sizeof(struct cmi_hdr) +
+ CMI_HDR_GET_PAYLOAD_SIZE(hdr);
+
+ msg->payload = kzalloc(msg->size, GFP_ATOMIC);
+ if (!msg->payload) {
+ pr_err("%s: no memory for cmi payload, sz = %zd\n",
+ __func__, msg->size);
+ kfree(msg);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return CPE_SVC_NO_MEMORY;
+ }
+
+ msg->address = 0;
+ memcpy((void *)msg->payload, message, msg->size);
+
+ rc = (enum cmi_api_result) cpe_send_cmd_to_thread(
+ cpe_d.cpe_default_handle,
+ CPE_CMD_SEND_MSG,
+ (void *)msg, false);
+
+ if (rc != 0) {
+ pr_err("%s: Failed to queue message\n", __func__);
+ kfree(msg->payload);
+ kfree(msg);
+ }
+
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return rc;
+}
+
+enum cpe_svc_result cpe_svc_ftm_test(void *cpe_handle, u32 *status)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+ struct cpe_svc_mem_segment backup_seg;
+ struct cpe_svc_mem_segment waiti_seg;
+ u8 *backup_data = NULL;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ rc = cpe_is_command_valid(t_info, CPE_CMD_FTM_TEST);
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: cmd validation fail, cmd = %d\n",
+ __func__, CPE_CMD_FTM_TEST);
+ goto fail_cmd;
+ }
+
+ if (t_info && t_info->tgt) {
+ backup_data = kzalloc(
+ t_info->tgt->tgt_waiti_info->tgt_waiti_size,
+ GFP_KERNEL);
+
+ /* CPE reset */
+ rc = t_info->tgt->tgt_reset();
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: CPE reset fail! err = %d\n",
+ __func__, rc);
+ goto err_return;
+ }
+
+ /* Back up the 4 byte IRAM data first */
+ backup_seg.type = CPE_SVC_INSTRUCTION_MEM;
+ backup_seg.cpe_addr =
+ t_info->tgt->tgt_get_cpe_info()->IRAM_offset;
+ backup_seg.size = t_info->tgt->tgt_waiti_info->tgt_waiti_size;
+ backup_seg.data = backup_data;
+
+ pr_debug("%s: Backing up IRAM data from CPE\n",
+ __func__);
+
+ rc = t_info->tgt->tgt_read_ram(t_info, &backup_seg);
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: Fail to backup CPE IRAM data, err = %d\n",
+ __func__, rc);
+ goto err_return;
+ }
+
+ pr_debug("%s: Complete backing up IRAM data from CPE\n",
+ __func__);
+
+ /* Write the WAITI instruction data */
+ waiti_seg.type = CPE_SVC_INSTRUCTION_MEM;
+ waiti_seg.cpe_addr =
+ t_info->tgt->tgt_get_cpe_info()->IRAM_offset;
+ waiti_seg.size = t_info->tgt->tgt_waiti_info->tgt_waiti_size;
+ waiti_seg.data = t_info->tgt->tgt_waiti_info->tgt_waiti_data;
+
+ rc = t_info->tgt->tgt_write_ram(t_info, &waiti_seg);
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: Fail to write the WAITI data, err = %d\n",
+ __func__, rc);
+ goto restore_iram;
+ }
+
+ /* Boot up cpe to execute the WAITI instructions */
+ rc = t_info->tgt->tgt_boot(1);
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: Fail to boot CPE, err = %d\n",
+ __func__, rc);
+ goto reset;
+ }
+
+ /*
+ * 1ms delay is suggested by the hw team to
+ * wait for cpe to boot up.
+ */
+ usleep_range(1000, 1100);
+
+ /* Check if the cpe init is done after executing the WAITI */
+ *status = t_info->tgt->tgt_cpar_init_done();
+
+reset:
+ /* Set the cpe back to reset state */
+ rc = t_info->tgt->tgt_reset();
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: CPE reset fail! err = %d\n",
+ __func__, rc);
+ goto restore_iram;
+ }
+
+restore_iram:
+ /* Restore the IRAM 4 bytes data */
+ rc = t_info->tgt->tgt_write_ram(t_info, &backup_seg);
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: Fail to restore the IRAM data, err = %d\n",
+ __func__, rc);
+ goto err_return;
+ }
+ }
+
+err_return:
+ kfree(backup_data);
+fail_cmd:
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_boot(int debug_mode)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+ if (!debug_mode)
+ rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_WDOG_CFG,
+ 0x3F, 0x31);
+ else
+ pr_info("%s: CPE in debug mode, WDOG disabled\n",
+ __func__);
+
+ rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL,
+ 0x02, 0x00);
+ rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL,
+ 0x0C, 0x04);
+ rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_CFG,
+ 0x01, 0x01);
+
+ return rc;
+}
+
+static u32 cpe_tgt_tomtom_is_cpar_init_done(void)
+{
+ u8 status = 0;
+ cpe_register_read(TOMTOM_A_SVASS_STATUS, &status);
+ return status & 0x01;
+}
+
+static u32 cpe_tgt_tomtom_is_active(void)
+{
+ u8 status = 0;
+ cpe_register_read(TOMTOM_A_SVASS_STATUS, &status);
+ return status & 0x04;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_reset(void)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+ rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_WDOG_CFG,
+ 0x30, 0x00);
+
+ rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_CFG,
+ 0x01, 0x00);
+ rc = cpe_update_bits(TOMTOM_A_MEM_LEAKAGE_CTL,
+ 0x07, 0x03);
+ rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL,
+ 0x08, 0x08);
+ rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL,
+ 0x02, 0x02);
+ return rc;
+}
+
+enum cpe_svc_result cpe_tgt_tomtom_voicetx(bool enable)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u8 val = 0;
+
+ if (enable)
+ val = 0x02;
+ else
+ val = 0x00;
+ rc = cpe_update_bits(TOMTOM_A_SVASS_CFG,
+ 0x02, val);
+ val = 0;
+ cpe_register_read(TOMTOM_A_SVASS_CFG, &val);
+ return rc;
+}
+
+enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable)
+{
+
+ struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+ if (!t_info)
+ t_info = cpe_d.cpe_default_handle;
+
+ if (t_info->tgt)
+ return t_info->tgt->tgt_voice_tx_lab(enable);
+ else
+ return CPE_SVC_INVALID_HANDLE;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_read_mailbox(u8 *buffer,
+ size_t size)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u32 cnt = 0;
+
+ if (size >= TOMTOM_A_SVASS_SPE_OUTBOX_SIZE)
+ size = TOMTOM_A_SVASS_SPE_OUTBOX_SIZE - 1;
+ for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) {
+ rc = cpe_register_read(TOMTOM_A_SVASS_SPE_OUTBOX(cnt),
+ &(buffer[cnt]));
+ }
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_write_mailbox(u8 *buffer,
+ size_t size)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u32 cnt = 0;
+
+ if (size >= TOMTOM_A_SVASS_SPE_INBOX_SIZE)
+ size = TOMTOM_A_SVASS_SPE_INBOX_SIZE - 1;
+ for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) {
+ rc = cpe_register_write(TOMTOM_A_SVASS_SPE_INBOX(cnt),
+ buffer[cnt]);
+ }
+
+ if (rc == CPE_SVC_SUCCESS)
+ rc = cpe_register_write(TOMTOM_A_SVASS_SPE_INBOX_TRG, 1);
+
+ return rc;
+}
+
+static enum cpe_svc_result cpe_get_mem_addr(struct cpe_info *t_info,
+ const struct cpe_svc_mem_segment *mem_seg,
+ u32 *addr, u8 *mem)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u32 offset, mem_sz, address;
+ u8 mem_type;
+
+ switch (mem_seg->type) {
+
+ case CPE_SVC_DATA_MEM:
+ mem_type = MEM_ACCESS_DRAM_VAL;
+ offset = TOMTOM_A_SVASS_SPE_DRAM_OFFSET;
+ mem_sz = TOMTOM_A_SVASS_SPE_DRAM_SIZE;
+ break;
+
+ case CPE_SVC_INSTRUCTION_MEM:
+ mem_type = MEM_ACCESS_IRAM_VAL;
+ offset = TOMTOM_A_SVASS_SPE_IRAM_OFFSET;
+ mem_sz = TOMTOM_A_SVASS_SPE_IRAM_SIZE;
+ break;
+
+ default:
+ pr_err("%s: Invalid mem type = %u\n",
+ __func__, mem_seg->type);
+ return CPE_SVC_INVALID_HANDLE;
+ }
+
+ if (mem_seg->cpe_addr < offset) {
+ pr_err("%s: Invalid addr %x for mem type %u\n",
+ __func__, mem_seg->cpe_addr, mem_type);
+ return CPE_SVC_INVALID_HANDLE;
+ }
+
+ address = mem_seg->cpe_addr - offset;
+ if (address + mem_seg->size > mem_sz) {
+ pr_err("%s: wrong size %zu, start adress %x, mem_type %u\n",
+ __func__, mem_seg->size, address, mem_type);
+ return CPE_SVC_INVALID_HANDLE;
+ }
+
+ (*addr) = address;
+ (*mem) = mem_type;
+
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_read_RAM(struct cpe_info *t_info,
+ struct cpe_svc_mem_segment *mem_seg)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u8 mem_reg_val = 0;
+ u32 cnt = 0;
+ bool autoinc;
+ u8 mem = MEM_ACCESS_NONE_VAL;
+ u32 addr = 0;
+ u32 ptr_update = true;
+
+ if (!mem_seg) {
+ pr_err("%s: Invalid mem segment\n",
+ __func__);
+ return CPE_SVC_INVALID_HANDLE;
+ }
+
+ rc = cpe_get_mem_addr(t_info, mem_seg, &addr, &mem);
+
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: Cannot obtain address, mem_type %u\n",
+ __func__, mem_seg->type);
+ return rc;
+ }
+
+ rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0);
+ autoinc = cpe_register_read_autoinc_supported();
+ if (autoinc)
+ mem_reg_val |= 0x04;
+
+ mem_reg_val |= 0x08;
+ mem_reg_val |= mem;
+
+ do {
+ if (!autoinc || ptr_update) {
+ rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR0,
+ (addr & 0xFF));
+ rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR1,
+ ((addr >> 8) & 0xFF));
+ rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR2,
+ ((addr >> 16) & 0xFF));
+
+ rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL,
+ mem_reg_val);
+
+ ptr_update = false;
+ }
+ rc = cpe_register_read(TOMTOM_A_SVASS_MEM_BANK,
+ &mem_seg->data[cnt]);
+
+ if (!autoinc)
+ rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0);
+ } while (++cnt < mem_seg->size);
+
+ rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0);
+
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_write_RAM(struct cpe_info *t_info,
+ const struct cpe_svc_mem_segment *mem_seg)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u8 mem_reg_val = 0;
+ u8 mem = MEM_ACCESS_NONE_VAL;
+ u32 addr = 0;
+ u8 *temp_ptr = NULL;
+ u32 temp_size = 0;
+ bool autoinc;
+
+ if (!mem_seg) {
+ pr_err("%s: Invalid mem segment\n",
+ __func__);
+ return CPE_SVC_INVALID_HANDLE;
+ }
+
+ rc = cpe_get_mem_addr(t_info, mem_seg, &addr, &mem);
+
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: Cannot obtain address, mem_type %u\n",
+ __func__, mem_seg->type);
+ return rc;
+ }
+
+ autoinc = cpe_register_read_autoinc_supported();
+ if (autoinc)
+ mem_reg_val |= 0x04;
+ mem_reg_val |= mem;
+
+ rc = cpe_update_bits(TOMTOM_A_SVASS_MEM_CTL,
+ 0x0F, mem_reg_val);
+
+ rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR0,
+ (addr & 0xFF));
+ rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR1,
+ ((addr >> 8) & 0xFF));
+
+ rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR2,
+ ((addr >> 16) & 0xFF));
+
+ temp_size = 0;
+ temp_ptr = mem_seg->data;
+
+ while (temp_size <= mem_seg->size) {
+ u32 to_write = (mem_seg->size >= temp_size+CHUNK_SIZE)
+ ? CHUNK_SIZE : (mem_seg->size-temp_size);
+
+ if (t_info->state == CPE_STATE_OFFLINE) {
+ pr_err("%s: CPE is offline\n", __func__);
+ return CPE_SVC_FAILED;
+ }
+
+ cpe_register_write_repeat(TOMTOM_A_SVASS_MEM_BANK,
+ temp_ptr, to_write);
+ temp_size += CHUNK_SIZE;
+ temp_ptr += CHUNK_SIZE;
+ }
+
+ rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0);
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_route_notification(
+ enum cpe_svc_module module,
+ enum cpe_svc_route_dest dest)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u8 ctl_reg_val = 0;
+
+ switch (module) {
+ case CPE_SVC_LISTEN_PROC:
+ switch (dest) {
+ case CPE_SVC_EXTERNAL:
+ ctl_reg_val = LISTEN_CTL_MSM_VAL;
+ break;
+ case CPE_SVC_INTERNAL:
+ ctl_reg_val = LISTEN_CTL_SPE_VAL;
+ break;
+ default:
+ pr_err("%s: Invalid dest %d\n",
+ __func__, dest);
+ return CPE_SVC_FAILED;
+ }
+
+ rc = cpe_update_bits(TOMTOM_A_SVASS_CFG,
+ 0x01, ctl_reg_val);
+ break;
+ default:
+ pr_err("%s: Invalid module %d\n",
+ __func__, module);
+ rc = CPE_SVC_FAILED;
+ break;
+ }
+
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_set_debug_mode(u32 enable)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u8 dbg_reg_val = 0x00;
+ if (enable)
+ dbg_reg_val = 0x08;
+ rc = cpe_update_bits(TOMTOM_A_SVASS_DEBUG,
+ 0x08, dbg_reg_val);
+ return rc;
+}
+
+static const struct cpe_svc_hw_cfg *cpe_tgt_tomtom_get_cpe_info(void)
+{
+ return &cpe_svc_tomtom_info;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_deinit(
+ struct cpe_svc_tgt_abstraction *param)
+{
+ kfree(param->inbox);
+ param->inbox = NULL;
+ kfree(param->outbox);
+ param->outbox = NULL;
+ memset(param, 0, sizeof(struct cpe_svc_tgt_abstraction));
+ return CPE_SVC_SUCCESS;
+}
+
+static u8 cpe_tgt_tomtom_waiti_data[] = {0x00, 0x70, 0x00, 0x00};
+
+static struct cpe_tgt_waiti_info cpe_tgt_tomtom_waiti_info = {
+ .tgt_waiti_size = ARRAY_SIZE(cpe_tgt_tomtom_waiti_data),
+ .tgt_waiti_data = cpe_tgt_tomtom_waiti_data,
+};
+
+static enum cpe_svc_result cpe_tgt_tomtom_init(
+ struct cpe_svc_codec_info_v1 *codec_info,
+ struct cpe_svc_tgt_abstraction *param)
+{
+ if (!codec_info)
+ return CPE_SVC_INVALID_HANDLE;
+ if (!param)
+ return CPE_SVC_INVALID_HANDLE;
+
+ if (codec_info->id == CPE_SVC_CODEC_TOMTOM) {
+ param->tgt_boot = cpe_tgt_tomtom_boot;
+ param->tgt_cpar_init_done = cpe_tgt_tomtom_is_cpar_init_done;
+ param->tgt_is_active = cpe_tgt_tomtom_is_active;
+ param->tgt_reset = cpe_tgt_tomtom_reset;
+ param->tgt_read_mailbox = cpe_tgt_tomtom_read_mailbox;
+ param->tgt_write_mailbox = cpe_tgt_tomtom_write_mailbox;
+ param->tgt_read_ram = cpe_tgt_tomtom_read_RAM;
+ param->tgt_write_ram = cpe_tgt_tomtom_write_RAM;
+ param->tgt_route_notification =
+ cpe_tgt_tomtom_route_notification;
+ param->tgt_set_debug_mode = cpe_tgt_tomtom_set_debug_mode;
+ param->tgt_get_cpe_info = cpe_tgt_tomtom_get_cpe_info;
+ param->tgt_deinit = cpe_tgt_tomtom_deinit;
+ param->tgt_voice_tx_lab = cpe_tgt_tomtom_voicetx;
+ param->tgt_waiti_info = &cpe_tgt_tomtom_waiti_info;
+
+ param->inbox = kzalloc(TOMTOM_A_SVASS_SPE_INBOX_SIZE,
+ GFP_KERNEL);
+ if (!param->inbox) {
+ pr_err("%s: no memory for inbox, sz = %d\n",
+ __func__, TOMTOM_A_SVASS_SPE_INBOX_SIZE);
+ return CPE_SVC_NO_MEMORY;
+ }
+
+ param->outbox = kzalloc(TOMTOM_A_SVASS_SPE_OUTBOX_SIZE,
+ GFP_KERNEL);
+ if (!param->outbox) {
+ kfree(param->inbox);
+ pr_err("%s: no memory for inbox, sz = %d\n",
+ __func__, TOMTOM_A_SVASS_SPE_OUTBOX_SIZE);
+ return CPE_SVC_NO_MEMORY;
+ }
+ }
+
+ return CPE_SVC_SUCCESS;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_boot(int debug_mode)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+ if (!debug_mode)
+ rc |= cpe_update_bits(
+ WCD9335_CPE_SS_WDOG_CFG,
+ 0x3f, 0x31);
+ else
+ pr_info("%s: CPE in debug mode, WDOG disabled\n",
+ __func__);
+
+ rc |= cpe_register_write(WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 19);
+ rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x04, 0x00);
+ rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x02, 0x02);
+ rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x01, 0x01);
+
+ if (unlikely(rc)) {
+ pr_err("%s: Failed to boot, err = %d\n",
+ __func__, rc);
+ rc = CPE_SVC_FAILED;
+ }
+
+ return rc;
+}
+
+static u32 cpe_tgt_wcd9335_is_cpar_init_done(void)
+{
+ u8 temp = 0;
+ cpe_register_read(WCD9335_CPE_SS_STATUS, &temp);
+ return temp & 0x1;
+}
+
+static u32 cpe_tgt_wcd9335_is_active(void)
+{
+ u8 temp = 0;
+ cpe_register_read(WCD9335_CPE_SS_STATUS, &temp);
+ return temp & 0x4;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_reset(void)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+ rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CFG, 0x01, 0x00);
+
+ rc |= cpe_register_write(
+ WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN, 0x00);
+ rc |= cpe_register_write(
+ WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, 0x00);
+ rc |= cpe_register_write(
+ WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1, 0x00);
+ rc |= cpe_register_write(
+ WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2, 0x00);
+
+ rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x04, 0x04);
+
+ if (unlikely(rc)) {
+ pr_err("%s: failed to reset cpe, err = %d\n",
+ __func__, rc);
+ rc = CPE_SVC_FAILED;
+ }
+
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_read_mailbox(u8 *buffer,
+ size_t size)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u32 cnt = 0;
+
+ pr_debug("%s: size=%zd\n", __func__, size);
+
+ if (size > WCD9335_CPE_SS_SPE_OUTBOX_SIZE)
+ size = WCD9335_CPE_SS_SPE_OUTBOX_SIZE;
+
+ for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++)
+ rc = cpe_register_read(WCD9335_CPE_SS_SPE_OUTBOX1(cnt),
+ &buffer[cnt]);
+
+ rc = cpe_register_write(WCD9335_CPE_SS_OUTBOX1_ACK, 0x01);
+
+ if (unlikely(rc)) {
+ pr_err("%s: failed to ACK outbox, err = %d\n",
+ __func__, rc);
+ rc = CPE_SVC_FAILED;
+ }
+
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_write_mailbox(u8 *buffer,
+ size_t size)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u32 cnt = 0;
+
+ pr_debug("%s: size = %zd\n", __func__, size);
+ if (size > WCD9335_CPE_SS_SPE_INBOX_SIZE)
+ size = WCD9335_CPE_SS_SPE_INBOX_SIZE;
+ for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) {
+ rc |= cpe_register_write(WCD9335_CPE_SS_SPE_INBOX1(cnt),
+ buffer[cnt]);
+ }
+
+ if (unlikely(rc)) {
+ pr_err("%s: Error %d writing mailbox registers\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = cpe_register_write(WCD9335_CPE_SS_INBOX1_TRG, 1);
+ return rc;
+}
+
+static enum cpe_svc_result cpe_wcd9335_get_mem_addr(struct cpe_info *t_info,
+ const struct cpe_svc_mem_segment *mem_seg,
+ u32 *addr, u8 *mem)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u32 offset, mem_sz, address;
+ u8 mem_type;
+
+ switch (mem_seg->type) {
+ case CPE_SVC_DATA_MEM:
+ mem_type = MEM_ACCESS_DRAM_VAL;
+ offset = WCD9335_CPE_SS_SPE_DRAM_OFFSET;
+ mem_sz = WCD9335_CPE_SS_SPE_DRAM_SIZE;
+ break;
+
+ case CPE_SVC_INSTRUCTION_MEM:
+ mem_type = MEM_ACCESS_IRAM_VAL;
+ offset = WCD9335_CPE_SS_SPE_IRAM_OFFSET;
+ mem_sz = WCD9335_CPE_SS_SPE_IRAM_SIZE;
+ break;
+
+ default:
+ pr_err("%s: Invalid mem type = %u\n",
+ __func__, mem_seg->type);
+ return CPE_SVC_INVALID_HANDLE;
+ }
+
+ if (mem_seg->cpe_addr < offset) {
+ pr_err("%s: Invalid addr %x for mem type %u\n",
+ __func__, mem_seg->cpe_addr, mem_type);
+ return CPE_SVC_INVALID_HANDLE;
+ }
+
+ address = mem_seg->cpe_addr - offset;
+ if (address + mem_seg->size > mem_sz) {
+ pr_err("%s: wrong size %zu, start adress %x, mem_type %u\n",
+ __func__, mem_seg->size, address, mem_type);
+ return CPE_SVC_INVALID_HANDLE;
+ }
+
+ (*addr) = address;
+ (*mem) = mem_type;
+
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_read_RAM(struct cpe_info *t_info,
+ struct cpe_svc_mem_segment *mem_seg)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u8 temp = 0;
+ u32 cnt = 0;
+ u8 mem = 0x0;
+ u32 addr = 0;
+ u32 lastaddr = 0;
+ u32 ptr_update = true;
+ bool autoinc;
+
+ if (!mem_seg) {
+ pr_err("%s: Invalid buffer\n", __func__);
+ return CPE_SVC_INVALID_HANDLE;
+ }
+
+ rc = cpe_wcd9335_get_mem_addr(t_info, mem_seg, &addr, &mem);
+
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: Cannot obtain address, mem_type %u\n",
+ __func__, mem_seg->type);
+ return rc;
+ }
+
+ rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0);
+ autoinc = cpe_register_read_autoinc_supported();
+
+ if (autoinc)
+ temp = 0x18;
+ else
+ temp = 0x10;
+
+ temp |= mem;
+
+ lastaddr = ~addr;
+ do {
+ if (!autoinc || (ptr_update)) {
+ /* write LSB only if modified */
+ if ((lastaddr & 0xFF) != (addr & 0xFF))
+ rc |= cpe_register_write(
+ WCD9335_CPE_SS_MEM_PTR_0,
+ (addr & 0xFF));
+ /* write middle byte only if modified */
+ if (((lastaddr >> 8) & 0xFF) != ((addr >> 8) & 0xFF))
+ rc |= cpe_register_write(
+ WCD9335_CPE_SS_MEM_PTR_1,
+ ((addr>>8) & 0xFF));
+ /* write MSB only if modified */
+ if (((lastaddr >> 16) & 0xFF) != ((addr >> 16) & 0xFF))
+ rc |= cpe_register_write(
+ WCD9335_CPE_SS_MEM_PTR_2,
+ ((addr>>16) & 0xFF));
+
+ rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, temp);
+ lastaddr = addr;
+ addr++;
+ ptr_update = false;
+ }
+
+ rc |= cpe_register_read(WCD9335_CPE_SS_MEM_BANK_0,
+ &mem_seg->data[cnt]);
+
+ if (!autoinc)
+ rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0);
+ } while ((++cnt < mem_seg->size) ||
+ (rc != CPE_SVC_SUCCESS));
+
+ rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0);
+
+ if (rc)
+ pr_err("%s: Failed to read registers, err = %d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_write_RAM(struct cpe_info *t_info,
+ const struct cpe_svc_mem_segment *mem_seg)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u8 mem_reg_val = 0;
+ u8 mem = MEM_ACCESS_NONE_VAL;
+ u32 addr = 0;
+ u8 *temp_ptr = NULL;
+ u32 temp_size = 0;
+ bool autoinc;
+
+ if (!mem_seg) {
+ pr_err("%s: Invalid mem segment\n",
+ __func__);
+ return CPE_SVC_INVALID_HANDLE;
+ }
+
+ rc = cpe_wcd9335_get_mem_addr(t_info, mem_seg, &addr, &mem);
+
+ if (rc != CPE_SVC_SUCCESS) {
+ pr_err("%s: Cannot obtain address, mem_type %u\n",
+ __func__, mem_seg->type);
+ return rc;
+ }
+
+ autoinc = cpe_register_read_autoinc_supported();
+ if (autoinc)
+ mem_reg_val = 0x18;
+ else
+ mem_reg_val = 0x10;
+
+ mem_reg_val |= mem;
+
+ rc = cpe_update_bits(WCD9335_CPE_SS_MEM_CTRL,
+ 0x0F, mem_reg_val);
+
+ rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_0,
+ (addr & 0xFF));
+ rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_1,
+ ((addr >> 8) & 0xFF));
+
+ rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_2,
+ ((addr >> 16) & 0xFF));
+
+ temp_size = 0;
+ temp_ptr = mem_seg->data;
+
+ while (temp_size <= mem_seg->size) {
+ u32 to_write = (mem_seg->size >= temp_size+CHUNK_SIZE)
+ ? CHUNK_SIZE : (mem_seg->size - temp_size);
+
+ if (t_info->state == CPE_STATE_OFFLINE) {
+ pr_err("%s: CPE is offline\n", __func__);
+ return CPE_SVC_FAILED;
+ }
+
+ cpe_register_write_repeat(WCD9335_CPE_SS_MEM_BANK_0,
+ temp_ptr, to_write);
+ temp_size += CHUNK_SIZE;
+ temp_ptr += CHUNK_SIZE;
+ }
+
+ rc = cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0);
+
+ if (rc)
+ pr_err("%s: Failed to write registers, err = %d\n",
+ __func__, rc);
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_route_notification(
+ enum cpe_svc_module module,
+ enum cpe_svc_route_dest dest)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+ pr_debug("%s: Module = %d, Destination = %d\n",
+ __func__, module, dest);
+
+ switch (module) {
+ case CPE_SVC_LISTEN_PROC:
+ switch (dest) {
+ case CPE_SVC_EXTERNAL:
+ rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x01, 0x01);
+ break;
+ case CPE_SVC_INTERNAL:
+ rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x01, 0x00);
+ break;
+ default:
+ pr_err("%s: Invalid destination %d\n",
+ __func__, dest);
+ return CPE_SVC_FAILED;
+ }
+ break;
+ default:
+ pr_err("%s: Invalid module %d\n",
+ __func__, module);
+ rc = CPE_SVC_FAILED;
+ break;
+ }
+ return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_set_debug_mode(u32 enable)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+ pr_debug("%s: enable = %s\n", __func__,
+ (enable) ? "true" : "false");
+
+ return rc;
+}
+
+static const struct cpe_svc_hw_cfg *cpe_tgt_wcd9335_get_cpe_info(void)
+{
+ return &cpe_svc_wcd9335_info;
+}
+
+static enum cpe_svc_result
+cpe_tgt_wcd9335_deinit(struct cpe_svc_tgt_abstraction *param)
+{
+ kfree(param->inbox);
+ param->inbox = NULL;
+ kfree(param->outbox);
+ param->outbox = NULL;
+ memset(param, 0, sizeof(struct cpe_svc_tgt_abstraction));
+
+ return CPE_SVC_SUCCESS;
+}
+
+static enum cpe_svc_result
+ cpe_tgt_wcd9335_voicetx(bool enable)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+ u8 val = 0;
+
+ pr_debug("%s: enable = %u\n", __func__, enable);
+ if (enable)
+ val = 0x02;
+ else
+ val = 0x00;
+
+ rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x02, val);
+ val = 0;
+ cpe_register_read(WCD9335_CPE_SS_CFG, &val);
+
+ return rc;
+}
+
+static u8 cpe_tgt_wcd9335_waiti_data[] = {0x00, 0x70, 0x00, 0x00};
+
+static struct cpe_tgt_waiti_info cpe_tgt_wcd9335_waiti_info = {
+ .tgt_waiti_size = ARRAY_SIZE(cpe_tgt_wcd9335_waiti_data),
+ .tgt_waiti_data = cpe_tgt_wcd9335_waiti_data,
+};
+
+static enum cpe_svc_result cpe_tgt_wcd9335_init(
+ struct cpe_svc_codec_info_v1 *codec_info,
+ struct cpe_svc_tgt_abstraction *param)
+{
+ if (!codec_info)
+ return CPE_SVC_INVALID_HANDLE;
+ if (!param)
+ return CPE_SVC_INVALID_HANDLE;
+
+ if (codec_info->id == CPE_SVC_CODEC_WCD9335) {
+ param->tgt_boot = cpe_tgt_wcd9335_boot;
+ param->tgt_cpar_init_done = cpe_tgt_wcd9335_is_cpar_init_done;
+ param->tgt_is_active = cpe_tgt_wcd9335_is_active;
+ param->tgt_reset = cpe_tgt_wcd9335_reset;
+ param->tgt_read_mailbox = cpe_tgt_wcd9335_read_mailbox;
+ param->tgt_write_mailbox = cpe_tgt_wcd9335_write_mailbox;
+ param->tgt_read_ram = cpe_tgt_wcd9335_read_RAM;
+ param->tgt_write_ram = cpe_tgt_wcd9335_write_RAM;
+ param->tgt_route_notification =
+ cpe_tgt_wcd9335_route_notification;
+ param->tgt_set_debug_mode = cpe_tgt_wcd9335_set_debug_mode;
+ param->tgt_get_cpe_info = cpe_tgt_wcd9335_get_cpe_info;
+ param->tgt_deinit = cpe_tgt_wcd9335_deinit;
+ param->tgt_voice_tx_lab = cpe_tgt_wcd9335_voicetx;
+ param->tgt_waiti_info = &cpe_tgt_wcd9335_waiti_info;
+
+ param->inbox = kzalloc(WCD9335_CPE_SS_SPE_INBOX_SIZE,
+ GFP_KERNEL);
+ if (!param->inbox) {
+ pr_err("%s: no memory for inbox, sz = %d\n",
+ __func__, WCD9335_CPE_SS_SPE_INBOX_SIZE);
+ return CPE_SVC_NO_MEMORY;
+ }
+
+ param->outbox = kzalloc(WCD9335_CPE_SS_SPE_OUTBOX_SIZE,
+ GFP_KERNEL);
+ if (!param->outbox) {
+ kfree(param->inbox);
+ pr_err("%s: no memory for inbox, sz = %d\n",
+ __func__, WCD9335_CPE_SS_SPE_OUTBOX_SIZE);
+ return CPE_SVC_NO_MEMORY;
+ }
+ }
+
+ return CPE_SVC_SUCCESS;
+}
+
+MODULE_DESCRIPTION("WCD CPE Services");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd_cpe_services.h b/sound/soc/codecs/wcd_cpe_services.h
new file mode 100644
index 000000000000..68eb61996a69
--- /dev/null
+++ b/sound/soc/codecs/wcd_cpe_services.h
@@ -0,0 +1,179 @@
+/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CPE_SERVICES__
+#define __CPE_SERVICES__
+
+#define CPE_IRQ_OUTBOX_IRQ 0x01
+#define CPE_IRQ_MEM_ACCESS_ERROR 0x02
+#define CPE_IRQ_WDOG_BITE 0x04
+#define CPE_IRQ_BUFFER_OVERFLOW 0x08
+#define CPE_IRQ_LAB_OVFUNF 0x10
+#define CPE_IRQ_FLL_LOCK_LOST 0x20
+#define CPE_IRQ_RCO_WDOG_INT 0x40
+
+#define EFAILED (MAX_ERRNO - 1)
+#define ENOTREADY (MAX_ERRNO - 2)
+
+#define MAX_SUPPORTED_CLKFREQ 8
+#define CPE_SVC_INIT_PARAM_V1 1
+
+enum cpe_svc_result {
+ CPE_SVC_SUCCESS = 0,
+ CPE_SVC_FAILED = -EFAILED,
+ CPE_SVC_NO_MEMORY = -ENOMEM,
+ CPE_SVC_INVALID_HANDLE = -EINVAL,
+ CPE_SVC_NOT_READY = -ENOTREADY,
+ CPE_SVC_SHUTTING_DOWN = -ESHUTDOWN,
+ CPE_SVC_BUSY = -EBUSY,
+};
+
+enum cpe_svc_event {
+ CPE_SVC_CMI_MSG = 0x01,
+ CPE_SVC_OFFLINE = 0x02,
+ CPE_SVC_ONLINE = 0x04,
+ CPE_SVC_BOOT_FAILED = 0x08,
+ CPE_SVC_READ_COMPLETE = 0x10,
+ CPE_SVC_READ_ERROR = 0x20,
+ CPE_SVC_BOOT = 0x40,
+ CPE_SVC_CMI_CLIENTS_DEREG = 0x100,
+ CPE_SVC_EVENT_ANCHOR = 0x7FFF
+};
+
+enum cpe_svc_module {
+ CPE_SVC_LISTEN_PROC = 1,
+ CPE_SVC_MODULE_ANCHOR = 0x7F
+};
+
+enum cpe_svc_route_dest {
+ CPE_SVC_EXTERNAL = 1,
+ CPE_SVC_INTERNAL = 2,
+ CPE_SVC_ROUTE_ANCHOR = 0x7F
+};
+
+enum cpe_svc_mem_type {
+ CPE_SVC_DATA_MEM = 1,
+ CPE_SVC_INSTRUCTION_MEM = 2,
+ CPE_SVC_IPC_MEM = 3,
+ CPE_SVC_MEM_TYPE_ANCHOR = 0x7F
+};
+
+enum cpe_svc_codec_id {
+ CPE_SVC_CODEC_TOMTOM = 5,
+ CPE_SVC_CODEC_WCD9335 = 7,
+ CPE_SVC_CODEC_WCD9326 = 8,
+ CPE_SVC_CODEC_ID_ANCHOR = 0x7ffffff
+};
+
+enum cpe_svc_codec_version {
+ CPE_SVC_CODEC_V1P0 = 1,
+ CPE_SVC_CODEC_VERSION_ANCHOR = 0x7fffffff
+};
+
+struct cpe_svc_codec_info_v1 {
+ u16 major_version;/*must be 1*/
+ u16 minor_version;/*must be 0*/
+ u32 id;
+ u32 version;
+ /*Add 1.1 version fields after this line*/
+};
+
+struct cpe_svc_notification {
+ enum cpe_svc_event event;
+ enum cpe_svc_result result;
+ void *payload;
+ void *private_data;
+};
+
+struct cpe_svc_msg_payload {
+ u8 *cmi_msg;
+};
+
+struct cpe_svc_read_complete {
+ u8 *buffer;
+ size_t size;
+};
+
+struct cpe_svc_boot_event {
+ u32 debug_address;
+ size_t debug_buffer_size;
+ u32 status;
+};
+
+struct cpe_svc_mem_segment {
+ enum cpe_svc_mem_type type;
+ u32 cpe_addr;
+ size_t size;
+ u8 *data;
+};
+
+struct cpe_svc_hw_cfg {
+ size_t DRAM_size;
+ u32 DRAM_offset;
+ size_t IRAM_size;
+ u32 IRAM_offset;
+ u8 inbox_size;
+ u8 outbox_size;
+};
+
+struct cpe_svc_cfg_clk_plan {
+ u32 current_clk_feq;
+ u32 num_clk_freqs;
+ u32 clk_freqs[MAX_SUPPORTED_CLKFREQ];
+};
+
+struct cpe_svc_init_param {
+ void *context;
+ u32 version;
+ void (*query_freq_plans_cb)(void *cdc_priv,
+ struct cpe_svc_cfg_clk_plan *clk_freq);
+ void (*change_freq_plan_cb)(void *cdc_priv,
+ u32 clk_freq);
+};
+
+
+void *cpe_svc_initialize(
+ void irq_control_callback(u32 enable),
+ const void *codec_info, void *context);
+enum cpe_svc_result cpe_svc_deinitialize(void *cpe_handle);
+
+void *cpe_svc_register(void *cpe_handle,
+ void (*notification_callback)(
+ const struct cpe_svc_notification *parameter),
+ u32 mask, const char *name);
+
+enum cpe_svc_result cpe_svc_deregister(void *cpe_handle, void *reg_handle);
+
+enum cpe_svc_result cpe_svc_download_segment(void *cpe_handle,
+ const struct cpe_svc_mem_segment *segment);
+
+enum cpe_svc_result cpe_svc_boot(void *cpe_handle, int debug_mode);
+
+enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle);
+
+enum cpe_svc_result cpe_svc_reset(void *cpe_handle);
+
+enum cpe_svc_result cpe_svc_process_irq(void *cpe_handle, u32 cpe_irq);
+
+enum cpe_svc_result
+cpe_svc_route_notification(void *cpe_handle, enum cpe_svc_module module,
+ enum cpe_svc_route_dest dest);
+
+enum cpe_svc_result cpe_svc_ramdump(void *cpe_handle,
+ struct cpe_svc_mem_segment *buffer);
+
+enum cpe_svc_result cpe_svc_set_debug_mode(void *cpe_handle, u32 mode);
+
+const struct cpe_svc_hw_cfg *cpe_svc_get_hw_cfg(void *cpe_handle);
+enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable);
+enum cpe_svc_result cpe_svc_ftm_test(void *cpe_handle, u32 *status);
+#endif /*__CPE_SERVICES__*/
diff --git a/sound/soc/codecs/wcdcal-hwdep.c b/sound/soc/codecs/wcdcal-hwdep.c
new file mode 100755
index 000000000000..58e51542ed66
--- /dev/null
+++ b/sound/soc/codecs/wcdcal-hwdep.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <linux/bitops.h>
+#include <sound/hwdep.h>
+#include <sound/msmcal-hwdep.h>
+#include <sound/soc.h>
+#include "wcdcal-hwdep.h"
+
+const int cal_size_info[WCD9XXX_MAX_CAL] = {
+ [WCD9XXX_ANC_CAL] = 16384,
+ [WCD9XXX_MBHC_CAL] = 4096,
+ [WCD9XXX_MAD_CAL] = 4096,
+ [WCD9XXX_VBAT_CAL] = 72,
+};
+
+const char *cal_name_info[WCD9XXX_MAX_CAL] = {
+ [WCD9XXX_ANC_CAL] = "anc",
+ [WCD9XXX_MBHC_CAL] = "mbhc",
+ [WCD9XXX_MAD_CAL] = "mad",
+ [WCD9XXX_VBAT_CAL] = "vbat",
+};
+
+struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
+ enum wcd_cal_type type)
+{
+ if (!fw_data) {
+ pr_err("%s: fw_data is NULL\n", __func__);
+ return NULL;
+ }
+ if (type >= WCD9XXX_MAX_CAL ||
+ type < WCD9XXX_MIN_CAL) {
+ pr_err("%s: wrong cal type sent %d\n", __func__, type);
+ return NULL;
+ }
+ mutex_lock(&fw_data->lock);
+ if (!test_bit(WCDCAL_RECIEVED,
+ &fw_data->wcdcal_state[type])) {
+ pr_err("%s: cal not sent by userspace %d\n",
+ __func__, type);
+ mutex_unlock(&fw_data->lock);
+ return NULL;
+ }
+ mutex_unlock(&fw_data->lock);
+ return fw_data->fw[type];
+}
+EXPORT_SYMBOL(wcdcal_get_fw_cal);
+
+static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw,
+ struct wcdcal_ioctl_buffer fw_user)
+{
+ struct fw_info *fw_data = hw->private_data;
+ struct firmware_cal **fw = fw_data->fw;
+ void *data;
+
+ if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) {
+ pr_err("%s: codec didn't set this %d!!\n",
+ __func__, fw_user.cal_type);
+ return -EFAULT;
+ }
+ if (fw_user.cal_type >= WCD9XXX_MAX_CAL ||
+ fw_user.cal_type < WCD9XXX_MIN_CAL) {
+ pr_err("%s: wrong cal type sent %d\n",
+ __func__, fw_user.cal_type);
+ return -EFAULT;
+ }
+ if (fw_user.size > cal_size_info[fw_user.cal_type] ||
+ fw_user.size <= 0) {
+ pr_err("%s: incorrect firmware size %d for %s\n",
+ __func__, fw_user.size,
+ cal_name_info[fw_user.cal_type]);
+ return -EFAULT;
+ }
+ data = fw[fw_user.cal_type]->data;
+ if (copy_from_user(data, fw_user.buffer, fw_user.size))
+ return -EFAULT;
+ fw[fw_user.cal_type]->size = fw_user.size;
+ mutex_lock(&fw_data->lock);
+ set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]);
+ mutex_unlock(&fw_data->lock);
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+struct wcdcal_ioctl_buffer32 {
+ u32 size;
+ compat_uptr_t buffer;
+ enum wcd_cal_type cal_type;
+};
+
+enum {
+ SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 =
+ _IOW('U', 0x1, struct wcdcal_ioctl_buffer32),
+};
+
+static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
+ struct wcdcal_ioctl_buffer32 fw_user32;
+ struct wcdcal_ioctl_buffer fw_user_compat;
+
+ if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) {
+ pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd);
+ return -ENOIOCTLCMD;
+ }
+ if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) {
+ pr_err("%s: failed to copy\n", __func__);
+ return -EFAULT;
+ }
+ fw_user_compat.size = fw_user32.size;
+ fw_user_compat.buffer = compat_ptr(fw_user32.buffer);
+ fw_user_compat.cal_type = fw_user32.cal_type;
+ return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat);
+}
+#else
+#define wcdcal_hwdep_ioctl_compat NULL
+#endif
+
+static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
+ struct wcdcal_ioctl_buffer fw_user;
+
+ if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) {
+ pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd);
+ return -ENOIOCTLCMD;
+ }
+ if (copy_from_user(&fw_user, argp, sizeof(fw_user))) {
+ pr_err("%s: failed to copy\n", __func__);
+ return -EFAULT;
+ }
+ return wcdcal_hwdep_ioctl_shared(hw, fw_user);
+}
+
+static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+ struct fw_info *fw_data = hw->private_data;
+ mutex_lock(&fw_data->lock);
+ /* clear all the calibrations */
+ memset(fw_data->wcdcal_state, 0,
+ sizeof(fw_data->wcdcal_state));
+ mutex_unlock(&fw_data->lock);
+ return 0;
+}
+
+int wcd_cal_create_hwdep(void *data, int node, struct snd_soc_codec *codec)
+{
+ char hwname[40];
+ struct snd_hwdep *hwdep;
+ struct firmware_cal **fw;
+ struct fw_info *fw_data = data;
+ int err, cal_bit;
+
+ if (!fw_data || !codec) {
+ pr_err("%s: wrong arguments passed\n", __func__);
+ return -EINVAL;
+ }
+
+ fw = fw_data->fw;
+ snprintf(hwname, strlen("Codec %s"), "Codec %s",
+ codec->component.name);
+ err = snd_hwdep_new(codec->component.card->snd_card,
+ hwname, node, &hwdep);
+ if (err < 0) {
+ dev_err(codec->dev, "%s: new hwdep failed %d\n",
+ __func__, err);
+ return err;
+ }
+ snprintf(hwdep->name, strlen("Codec %s"), "Codec %s",
+ codec->component.name);
+ hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC;
+ hwdep->private_data = fw_data;
+ hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat;
+ hwdep->ops.ioctl = wcdcal_hwdep_ioctl;
+ hwdep->ops.release = wcdcal_hwdep_release;
+ mutex_init(&fw_data->lock);
+
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ set_bit(WCDCAL_UNINITIALISED,
+ &fw_data->wcdcal_state[cal_bit]);
+ fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL);
+ if (!fw[cal_bit]) {
+ dev_err(codec->dev, "%s: no memory for %s cal\n",
+ __func__, cal_name_info[cal_bit]);
+ goto end;
+ }
+ }
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit],
+ GFP_KERNEL);
+ if (!fw[cal_bit]->data) {
+ dev_err(codec->dev, "%s: no memory for %s cal data\n",
+ __func__, cal_name_info[cal_bit]);
+ goto exit;
+ }
+ set_bit(WCDCAL_INITIALISED,
+ &fw_data->wcdcal_state[cal_bit]);
+ }
+ return 0;
+exit:
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ kfree(fw[cal_bit]->data);
+ fw[cal_bit]->data = NULL;
+ }
+end:
+ for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+ kfree(fw[cal_bit]);
+ fw[cal_bit] = NULL;
+ }
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(wcd_cal_create_hwdep);
diff --git a/sound/soc/codecs/wcdcal-hwdep.h b/sound/soc/codecs/wcdcal-hwdep.h
new file mode 100644
index 000000000000..632e2f11f323
--- /dev/null
+++ b/sound/soc/codecs/wcdcal-hwdep.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_HWDEP_H__
+#define __WCD9XXX_HWDEP_H__
+#include <sound/msmcal-hwdep.h>
+
+enum wcd_cal_states {
+ WCDCAL_UNINITIALISED,
+ WCDCAL_INITIALISED,
+ WCDCAL_RECIEVED
+};
+
+struct fw_info {
+ struct firmware_cal *fw[WCD9XXX_MAX_CAL];
+ DECLARE_BITMAP(cal_bit, WCD9XXX_MAX_CAL);
+ /* for calibration tracking */
+ unsigned long wcdcal_state[WCD9XXX_MAX_CAL];
+ struct mutex lock;
+};
+
+struct firmware_cal {
+ u8 *data;
+ size_t size;
+};
+
+struct snd_soc_codec;
+int wcd_cal_create_hwdep(void *fw, int node, struct snd_soc_codec *codec);
+struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
+ enum wcd_cal_type type);
+#endif /* __WCD9XXX_HWDEP_H__ */
diff --git a/sound/soc/codecs/wsa881x-analog.c b/sound/soc/codecs/wsa881x-analog.c
new file mode 100644
index 000000000000..ff160dcb0952
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-analog.c
@@ -0,0 +1,1445 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/bitops.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/q6afe-v2.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include "wsa881x-analog.h"
+#include "wsa881x-temp-sensor.h"
+#include "../msm/msm-audio-pinctrl.h"
+
+#define SPK_GAIN_12DB 4
+#define WIDGET_NAME_MAX_SIZE 80
+
+/*
+ * Private data Structure for wsa881x. All parameters related to
+ * WSA881X codec needs to be defined here.
+ */
+struct wsa881x_pdata {
+ struct regmap *regmap[2];
+ struct i2c_client *client[2];
+ struct snd_soc_codec *codec;
+
+ /* track wsa881x status during probe */
+ int status;
+ bool boost_enable;
+ bool visense_enable;
+ int spk_pa_gain;
+ struct i2c_msg xfer_msg[2];
+ struct mutex xfer_lock;
+ bool regmap_flag;
+ bool wsa_active;
+ int index;
+ int (*enable_mclk)(struct snd_soc_card *, bool);
+ struct wsa881x_tz_priv tz_pdata;
+ int bg_cnt;
+ int clk_cnt;
+ int enable_cnt;
+ int version;
+ struct mutex bg_lock;
+ struct mutex res_lock;
+ struct delayed_work ocp_ctl_work;
+};
+
+enum {
+ WSA881X_STATUS_PROBING,
+ WSA881X_STATUS_I2C,
+};
+
+#define WSA881X_OCP_CTL_TIMER_SEC 2
+#define WSA881X_OCP_CTL_TEMP_CELSIUS 25
+#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60
+
+static int wsa881x_ocp_poll_timer_sec = WSA881X_OCP_CTL_POLL_TIMER_SEC;
+module_param(wsa881x_ocp_poll_timer_sec, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(wsa881x_ocp_poll_timer_sec, "timer for ocp ctl polling");
+
+static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec,
+ bool enable);
+
+const char *wsa_tz_names[] = {"wsa881x.0e", "wsa881x.0f"};
+
+struct wsa881x_pdata wsa_pdata[MAX_WSA881X_DEVICE];
+
+static bool pinctrl_init;
+
+static int wsa881x_populate_dt_pdata(struct device *dev);
+static int wsa881x_reset(struct wsa881x_pdata *pdata, bool enable);
+static int wsa881x_startup(struct wsa881x_pdata *pdata);
+static int wsa881x_shutdown(struct wsa881x_pdata *pdata);
+
+static int delay_array_msec[] = {10, 20, 30, 40, 50};
+
+static int wsa881x_i2c_addr = -1;
+static int wsa881x_probing_count;
+static int wsa881x_presence_count;
+
+static const char * const wsa881x_spk_pa_gain_text[] = {
+"POS_13P5_DB", "POS_12_DB", "POS_10P5_DB", "POS_9_DB", "POS_7P5_DB",
+"POS_6_DB", "POS_4P5_DB", "POS_3_DB", "POS_1P5_DB", "POS_0_DB"};
+
+static const struct soc_enum wsa881x_spk_pa_gain_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa881x_spk_pa_gain_text),
+ wsa881x_spk_pa_gain_text),
+};
+
+static int wsa881x_spk_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wsa881x->spk_pa_gain;
+
+ dev_dbg(codec->dev, "%s: spk_pa_gain = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int wsa881x_spk_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] > 0xC) {
+ dev_err(codec->dev, "%s: Unsupported gain val %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return -EINVAL;
+ }
+ wsa881x->spk_pa_gain = ucontrol->value.integer.value[0];
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int get_i2c_wsa881x_device_index(u16 reg)
+{
+ u16 mask = 0x0f00;
+ int value = 0;
+
+ value = ((reg & mask) >> 8) & 0x000f;
+
+ switch (value) {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int wsa881x_i2c_write_device(struct wsa881x_pdata *wsa881x,
+ unsigned int reg, unsigned int val)
+{
+ int i = 0, rc = 0;
+ int wsa881x_index;
+ struct i2c_msg *msg;
+ int ret = 0;
+ int bytes = 1;
+ u8 reg_addr = 0;
+ u8 data[bytes + 1];
+
+ wsa881x_index = get_i2c_wsa881x_device_index(reg);
+ if (wsa881x_index < 0) {
+ pr_err("%s:invalid register to write\n", __func__);
+ return -EINVAL;
+ }
+ if (wsa881x->regmap_flag) {
+ rc = regmap_write(wsa881x->regmap[wsa881x_index], reg, val);
+ for (i = 0; rc && i < ARRAY_SIZE(delay_array_msec); i++) {
+ pr_err("Failed writing reg=%u - retry(%d)\n", reg, i);
+ /* retry after delay of increasing order */
+ msleep(delay_array_msec[i]);
+ rc = regmap_write(wsa881x->regmap[wsa881x_index],
+ reg, val);
+ }
+ if (rc)
+ pr_err("Failed writing reg=%u rc=%d\n", reg, rc);
+ else
+ pr_err("write success register = %x val = %x\n",
+ reg, val);
+ } else {
+ reg_addr = (u8)reg;
+ msg = &wsa881x->xfer_msg[0];
+ msg->addr = wsa881x->client[wsa881x_index]->addr;
+ msg->len = bytes + 1;
+ msg->flags = 0;
+ data[0] = reg;
+ data[1] = (u8)val;
+ msg->buf = data;
+ ret = i2c_transfer(wsa881x->client[wsa881x_index]->adapter,
+ wsa881x->xfer_msg, 1);
+ /* Try again if the write fails */
+ if (ret != 1) {
+ ret = i2c_transfer(
+ wsa881x->client[wsa881x_index]->adapter,
+ wsa881x->xfer_msg, 1);
+ if (ret != 1) {
+ pr_err("failed to write the device\n");
+ return ret;
+ }
+ }
+ pr_debug("write success reg = %x val = %x\n", reg, data[1]);
+ }
+ return rc;
+}
+
+static int wsa881x_i2c_read_device(struct wsa881x_pdata *wsa881x,
+ unsigned int reg)
+{
+ int wsa881x_index;
+ int i = 0, rc = 0;
+ unsigned int val;
+ struct i2c_msg *msg;
+ int ret = 0;
+ u8 reg_addr = 0;
+ u8 dest[5];
+
+ wsa881x_index = get_i2c_wsa881x_device_index(reg);
+ if (wsa881x_index < 0) {
+ pr_err("%s:invalid register to read\n", __func__);
+ return -EINVAL;
+ }
+ if (wsa881x->regmap_flag) {
+ rc = regmap_read(wsa881x->regmap[wsa881x_index], reg, &val);
+ for (i = 0; rc && i < ARRAY_SIZE(delay_array_msec); i++) {
+ pr_err("Failed reading reg=%u - retry(%d)\n", reg, i);
+ /* retry after delay of increasing order */
+ msleep(delay_array_msec[i]);
+ rc = regmap_read(wsa881x->regmap[wsa881x_index],
+ reg, &val);
+ }
+ if (rc) {
+ pr_err("Failed reading reg=%u rc=%d\n", reg, rc);
+ return rc;
+ }
+ pr_debug("read success reg = %x val = %x\n",
+ reg, val);
+ } else {
+ reg_addr = (u8)reg;
+ msg = &wsa881x->xfer_msg[0];
+ msg->addr = wsa881x->client[wsa881x_index]->addr;
+ msg->len = 1;
+ msg->flags = 0;
+ msg->buf = &reg_addr;
+
+ msg = &wsa881x->xfer_msg[1];
+ msg->addr = wsa881x->client[wsa881x_index]->addr;
+ msg->len = 1;
+ msg->flags = I2C_M_RD;
+ msg->buf = dest;
+ ret = i2c_transfer(wsa881x->client[wsa881x_index]->adapter,
+ wsa881x->xfer_msg, 2);
+
+ /* Try again if read fails first time */
+ if (ret != 2) {
+ ret = i2c_transfer(
+ wsa881x->client[wsa881x_index]->adapter,
+ wsa881x->xfer_msg, 2);
+ if (ret != 2) {
+ pr_err("failed to read wsa register:%d\n",
+ reg);
+ return ret;
+ }
+ }
+ val = dest[0];
+ }
+ return val;
+}
+
+static unsigned int wsa881x_i2c_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct wsa881x_pdata *wsa881x;
+ unsigned int val;
+ int ret;
+
+ if (codec == NULL) {
+ pr_err("%s: invalid codec\n", __func__);
+ return -EINVAL;
+ }
+ wsa881x = snd_soc_codec_get_drvdata(codec);
+ if (!wsa881x->wsa_active) {
+ ret = snd_soc_cache_read(codec, reg, &val);
+ if (ret >= 0)
+ return val;
+ dev_err(codec->dev,
+ "cache read failed for reg: 0x%x ret: %d\n",
+ reg, ret);
+ return ret;
+ }
+ return wsa881x_i2c_read_device(wsa881x, reg);
+}
+
+static int wsa881x_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ struct wsa881x_pdata *wsa881x;
+ int ret = 0;
+
+ if (codec == NULL) {
+ pr_err("%s: invalid codec\n", __func__);
+ return -EINVAL;
+ }
+ wsa881x = snd_soc_codec_get_drvdata(codec);
+ if (!wsa881x->wsa_active) {
+ ret = snd_soc_cache_write(codec, reg, val);
+ if (ret != 0)
+ dev_err(codec->dev, "cache write to %x failed: %d\n",
+ reg, ret);
+ return ret;
+ }
+ return wsa881x_i2c_write_device(wsa881x, reg, val);
+}
+
+static int wsa881x_i2c_get_client_index(struct i2c_client *client,
+ int *wsa881x_index)
+{
+ int ret = 0;
+
+ switch (client->addr) {
+ case WSA881X_I2C_SPK0_SLAVE0_ADDR:
+ case WSA881X_I2C_SPK0_SLAVE1_ADDR:
+ *wsa881x_index = WSA881X_I2C_SPK0_SLAVE0;
+ break;
+ case WSA881X_I2C_SPK1_SLAVE0_ADDR:
+ case WSA881X_I2C_SPK1_SLAVE1_ADDR:
+ *wsa881x_index = WSA881X_I2C_SPK1_SLAVE0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int wsa881x_boost_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: enable:%d\n", __func__, enable);
+ if (enable) {
+ if (!WSA881X_IS_2_0(wsa881x->version)) {
+ snd_soc_update_bits(codec, WSA881X_ANA_CTL,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec, WSA881X_ANA_CTL,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, WSA881X_BOOST_PS_CTL,
+ 0x40, 0x00);
+ snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1,
+ 0xF0, 0xB0);
+ snd_soc_update_bits(codec, WSA881X_BOOST_ZX_CTL,
+ 0x20, 0x00);
+ snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL,
+ 0x80, 0x80);
+ } else {
+ snd_soc_update_bits(codec, WSA881X_BOOST_LOOP_STABILITY,
+ 0x03, 0x03);
+ snd_soc_update_bits(codec, WSA881X_BOOST_MISC2_CTL,
+ 0xFF, 0x14);
+ snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL,
+ 0x03, 0x00);
+ snd_soc_update_bits(codec,
+ WSA881X_BOOST_SLOPE_COMP_ISENSE_FB,
+ 0x0C, 0x04);
+ snd_soc_update_bits(codec,
+ WSA881X_BOOST_SLOPE_COMP_ISENSE_FB,
+ 0x03, 0x00);
+ snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1,
+ 0xF0, 0x70);
+ snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x03, 0x01);
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN,
+ 0x08, 0x08);
+ snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x04, 0x04);
+ snd_soc_update_bits(codec, WSA881X_BOOST_CURRENT_LIMIT,
+ 0x0F, 0x08);
+ snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL,
+ 0x80, 0x80);
+ }
+ /* For WSA8810, start-up time is 1500us as per qcrg sequence */
+ usleep_range(1500, 1510);
+ } else {
+ /* ENSURE: Class-D amp is shutdown. CLK is still on */
+ snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x00);
+ /* boost settle time is 1500us as per qcrg sequence */
+ usleep_range(1500, 1510);
+ }
+ return 0;
+}
+
+static int wsa881x_visense_txfe_ctrl(struct snd_soc_codec *codec, bool enable,
+ u8 isense1_gain, u8 isense2_gain,
+ u8 vsense_gain)
+{
+ u8 value = 0;
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: enable:%d\n", __func__, enable);
+
+ if (enable) {
+ if (WSA881X_IS_2_0(wsa881x->version)) {
+ snd_soc_update_bits(codec, WSA881X_OTP_REG_28,
+ 0x3F, 0x3A);
+ snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG1,
+ 0xFF, 0xB2);
+ snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG2,
+ 0xFF, 0x05);
+ }
+ snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_VSENSE_VCM,
+ 0x08, 0x00);
+ if (WSA881X_IS_2_0(wsa881x->version)) {
+ snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2,
+ 0x1C, 0x04);
+ } else {
+ snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2,
+ 0x08, 0x08);
+ snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2,
+ 0x02, 0x02);
+ }
+ value = ((isense2_gain << 6) | (isense1_gain << 4) |
+ (vsense_gain << 3));
+ snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN,
+ 0xF8, value);
+ snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN,
+ 0x01, 0x01);
+ } else {
+ if (WSA881X_IS_2_0(wsa881x->version))
+ snd_soc_update_bits(codec,
+ WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x10, 0x10);
+ else
+ snd_soc_update_bits(codec,
+ WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x08, 0x08);
+ /*
+ * 200us sleep is needed after visense txfe disable as per
+ * HW requirement.
+ */
+ usleep_range(200, 210);
+
+ snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN,
+ 0x01, 0x00);
+ }
+ return 0;
+}
+
+static int wsa881x_visense_adc_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: enable:%d\n", __func__, enable);
+ if (enable) {
+ if (!WSA881X_IS_2_0(wsa881x->version))
+ snd_soc_update_bits(codec, WSA881X_ADC_SEL_IBIAS,
+ 0x70, 0x40);
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_SEL_IBIAS,
+ 0x07, 0x04);
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, 0x80, 0x80);
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, 0x80, 0x80);
+ } else {
+ /* Ensure: Speaker Protection has been stopped */
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, 0x80, 0x00);
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, 0x80, 0x00);
+ }
+
+ return 0;
+}
+
+static void wsa881x_bandgap_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: enable:%d, bg_count:%d\n", __func__,
+ enable, wsa881x->bg_cnt);
+ mutex_lock(&wsa881x->bg_lock);
+ if (enable) {
+ ++wsa881x->bg_cnt;
+ if (wsa881x->bg_cnt == 1) {
+ snd_soc_update_bits(codec, WSA881X_TEMP_OP,
+ 0x08, 0x08);
+ /* 400usec sleep is needed as per HW requirement */
+ usleep_range(400, 410);
+ snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x04);
+ }
+ } else {
+ --wsa881x->bg_cnt;
+ if (wsa881x->bg_cnt <= 0) {
+ WARN_ON(wsa881x->bg_cnt < 0);
+ wsa881x->bg_cnt = 0;
+ snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x00);
+ snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x08, 0x00);
+ }
+ }
+ mutex_unlock(&wsa881x->bg_lock);
+}
+
+static void wsa881x_clk_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s:ss enable:%d, clk_count:%d\n", __func__,
+ enable, wsa881x->clk_cnt);
+ mutex_lock(&wsa881x->res_lock);
+ if (enable) {
+ ++wsa881x->clk_cnt;
+ if (wsa881x->clk_cnt == 1) {
+ snd_soc_write(codec, WSA881X_CDC_RST_CTL, 0x02);
+ snd_soc_write(codec, WSA881X_CDC_RST_CTL, 0x03);
+ snd_soc_write(codec, WSA881X_CLOCK_CONFIG, 0x01);
+ snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x01);
+ snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x01);
+ }
+ } else {
+ --wsa881x->clk_cnt;
+ if (wsa881x->clk_cnt <= 0) {
+ WARN_ON(wsa881x->clk_cnt < 0);
+ wsa881x->clk_cnt = 0;
+ snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x00);
+ snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x00);
+ if (WSA881X_IS_2_0(wsa881x->version))
+ snd_soc_update_bits(codec,
+ WSA881X_CDC_TOP_CLK_CTL, 0x01, 0x00);
+ }
+ }
+ mutex_unlock(&wsa881x->res_lock);
+}
+
+static int wsa881x_rdac_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: enable:%d\n", __func__, enable);
+ if (enable) {
+ snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x08, 0x00);
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0x08, 0x08);
+ snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x20, 0x20);
+ snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x20, 0x00);
+ snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x40, 0x40);
+ snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x80, 0x80);
+ if (WSA881X_IS_2_0(wsa881x->version)) {
+ snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL,
+ 0x30, 0x30);
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL,
+ 0x0C, 0x00);
+ }
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0xF0, 0x40);
+ snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x01, 0x01);
+ } else {
+ /* Ensure class-D amp is off */
+ snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x80, 0x00);
+ }
+ return 0;
+}
+
+static int wsa881x_spkr_pa_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+ int ret = 0;
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: enable:%d\n", __func__, enable);
+ if (enable) {
+ /*
+ * Ensure: Boost is enabled and stable, Analog input is up
+ * and outputting silence
+ */
+ if (!WSA881X_IS_2_0(wsa881x->version)) {
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_I,
+ 0xFF, 0x01);
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_V,
+ 0xFF, 0x10);
+ snd_soc_update_bits(codec, WSA881X_SPKR_PWRSTG_DBG,
+ 0xA0, 0xA0);
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN,
+ 0x80, 0x80);
+ usleep_range(700, 710);
+ snd_soc_update_bits(codec, WSA881X_SPKR_PWRSTG_DBG,
+ 0x00, 0x00);
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_V,
+ 0xFF, 0x00);
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V,
+ 0x02, 0x00);
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_I,
+ 0xFF, 0x00);
+ } else
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN,
+ 0x80, 0x80);
+ /* add 1000us delay as per qcrg */
+ usleep_range(1000, 1010);
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x01, 0x01);
+ if (WSA881X_IS_2_0(wsa881x->version))
+ snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL,
+ 0x01, 0x00);
+ usleep_range(1000, 1010);
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0xF0,
+ (wsa881x->spk_pa_gain << 4));
+ if (wsa881x->visense_enable) {
+ ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1,
+ "wsa_vi");
+ if (ret) {
+ pr_err("%s: gpio set cannot be activated %s\n",
+ __func__, "wsa_vi");
+ return ret;
+ }
+ wsa881x_visense_txfe_ctrl(codec, true,
+ 0x00, 0x01, 0x00);
+ wsa881x_visense_adc_ctrl(codec, true);
+ }
+ } else {
+ /*
+ * Ensure: Boost is still on, Stream from Analog input and
+ * Speaker Protection has been stopped and input is at 0V
+ */
+ if (WSA881X_IS_2_0(wsa881x->version)) {
+ snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL,
+ 0x01, 0x01);
+ usleep_range(1000, 1010);
+ snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL,
+ 0x01, 0x00);
+ msleep(20);
+ snd_soc_update_bits(codec, WSA881X_ANA_CTL,
+ 0x03, 0x00);
+ usleep_range(200, 210);
+ }
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x80, 0x00);
+ }
+ return 0;
+}
+
+static int wsa881x_get_boost(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wsa881x->boost_enable;
+ return 0;
+}
+
+static int wsa881x_set_boost(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int value = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: Boost enable current %d, new %d\n",
+ __func__, wsa881x->boost_enable, value);
+ wsa881x->boost_enable = value;
+ return 0;
+}
+
+static int wsa881x_get_visense(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wsa881x->visense_enable;
+ return 0;
+}
+
+static int wsa881x_set_visense(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int value = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: VIsense enable current %d, new %d\n",
+ __func__, wsa881x->visense_enable, value);
+ wsa881x->visense_enable = value;
+ return 0;
+}
+
+static const struct snd_kcontrol_new wsa881x_snd_controls[] = {
+ SOC_SINGLE_EXT("BOOST Switch", SND_SOC_NOPM, 0, 1, 0,
+ wsa881x_get_boost, wsa881x_set_boost),
+
+ SOC_SINGLE_EXT("VISENSE Switch", SND_SOC_NOPM, 0, 1, 0,
+ wsa881x_get_visense, wsa881x_set_visense),
+
+ SOC_ENUM_EXT("WSA_SPK PA Gain", wsa881x_spk_pa_gain_enum[0],
+ wsa881x_spk_pa_gain_get, wsa881x_spk_pa_gain_put),
+};
+
+static const char * const rdac_text[] = {
+ "ZERO", "Switch",
+};
+
+static const struct soc_enum rdac_enum =
+ SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(rdac_text), rdac_text);
+
+static const struct snd_kcontrol_new rdac_mux[] = {
+ SOC_DAPM_ENUM("RDAC", rdac_enum)
+};
+
+static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s: %s %d boost %d visense %d\n",
+ __func__, w->name, event,
+ wsa881x->boost_enable, wsa881x->visense_enable);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = wsa881x_startup(wsa881x);
+ if (ret) {
+ pr_err("%s: wsa startup failed ret: %d", __func__, ret);
+ return ret;
+ }
+ wsa881x_clk_ctrl(codec, true);
+ snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x02, 0x02);
+ if (!WSA881X_IS_2_0(wsa881x->version))
+ snd_soc_update_bits(codec, WSA881X_BIAS_REF_CTRL,
+ 0x0F, 0x08);
+ wsa881x_bandgap_ctrl(codec, true);
+ if (!WSA881X_IS_2_0(wsa881x->version))
+ snd_soc_update_bits(codec, WSA881X_SPKR_BBM_CTL,
+ 0x02, 0x02);
+ snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80);
+ snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06);
+ if (!WSA881X_IS_2_0(wsa881x->version)) {
+ snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL2,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_INT,
+ 0x09, 0x09);
+ }
+ snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0xF0, 0x20);
+ if (WSA881X_IS_2_0(wsa881x->version))
+ snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT,
+ 0x0E, 0x0E);
+ if (wsa881x->boost_enable)
+ wsa881x_boost_ctrl(codec, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ wsa881x_rdac_ctrl(codec, true);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ wsa881x_rdac_ctrl(codec, false);
+ if (wsa881x->visense_enable) {
+ wsa881x_visense_adc_ctrl(codec, false);
+ wsa881x_visense_txfe_ctrl(codec, false,
+ 0x00, 0x01, 0x00);
+ ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1,
+ "wsa_vi");
+ if (ret) {
+ pr_err("%s: gpio set cannot be suspended %s\n",
+ __func__, "wsa_vi");
+ return ret;
+ }
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (wsa881x->boost_enable)
+ wsa881x_boost_ctrl(codec, false);
+ wsa881x_clk_ctrl(codec, false);
+ wsa881x_bandgap_ctrl(codec, false);
+ ret = wsa881x_shutdown(wsa881x);
+ if (ret < 0) {
+ pr_err("%s: wsa shutdown failed ret: %d",
+ __func__, ret);
+ return ret;
+ }
+ break;
+ default:
+ pr_err("%s: invalid event:%d\n", __func__, event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void wsa881x_ocp_ctl_work(struct work_struct *work)
+{
+ struct wsa881x_pdata *wsa881x;
+ struct delayed_work *dwork;
+ struct snd_soc_codec *codec;
+ unsigned long temp_val;
+
+ dwork = to_delayed_work(work);
+ wsa881x = container_of(dwork, struct wsa881x_pdata, ocp_ctl_work);
+
+ if (!wsa881x)
+ return;
+
+ codec = wsa881x->codec;
+ wsa881x_get_temp(wsa881x->tz_pdata.tz_dev, &temp_val);
+ dev_dbg(codec->dev, " temp = %ld\n", temp_val);
+
+ if (temp_val <= WSA881X_OCP_CTL_TEMP_CELSIUS)
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x00);
+ else
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0);
+
+ schedule_delayed_work(&wsa881x->ocp_ctl_work,
+ msecs_to_jiffies(wsa881x_ocp_poll_timer_sec * 1000));
+}
+
+static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s: %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x80);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ wsa881x_spkr_pa_ctrl(codec, true);
+ schedule_delayed_work(&wsa881x->ocp_ctl_work,
+ msecs_to_jiffies(WSA881X_OCP_CTL_TIMER_SEC * 1000));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ wsa881x_spkr_pa_ctrl(codec, false);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cancel_delayed_work_sync(&wsa881x->ocp_ctl_work);
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0);
+ break;
+ default:
+ pr_err("%s: invalid event:%d\n", __func__, event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("WSA_IN"),
+
+ SND_SOC_DAPM_DAC_E("RDAC Analog", NULL, SND_SOC_NOPM, 0, 0,
+ wsa881x_rdac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("WSA_RDAC", SND_SOC_NOPM, 0, 0,
+ rdac_mux),
+
+ SND_SOC_DAPM_PGA_S("WSA_SPKR PGA", 1, SND_SOC_NOPM, 0, 0,
+ wsa881x_spkr_pa_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("WSA_SPKR"),
+};
+
+static const struct snd_soc_dapm_route wsa881x_audio_map[] = {
+ {"WSA_RDAC", "Switch", "WSA_IN"},
+ {"RDAC Analog", NULL, "WSA_RDAC"},
+ {"WSA_SPKR PGA", NULL, "RDAC Analog"},
+ {"WSA_SPKR", NULL, "WSA_SPKR PGA"},
+};
+
+
+static int wsa881x_startup(struct wsa881x_pdata *pdata)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = pdata->codec;
+ struct snd_soc_card *card = codec->component.card;
+
+ pr_debug("%s(): wsa startup, enable_cnt:%d\n", __func__,
+ pdata->enable_cnt);
+
+ if (pdata->enable_cnt++ > 0)
+ return 0;
+ ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_clk");
+ if (ret) {
+ pr_err("%s: gpio set cannot be activated %s\n",
+ __func__, "wsa_clk");
+ return ret;
+ }
+ if (pdata->enable_mclk) {
+ ret = pdata->enable_mclk(card, true);
+ if (ret < 0) {
+ dev_err_ratelimited(codec->dev,
+ "%s: mclk enable failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ ret = wsa881x_reset(pdata, true);
+ return ret;
+}
+
+static int wsa881x_shutdown(struct wsa881x_pdata *pdata)
+{
+ int ret = 0, reg;
+ struct snd_soc_codec *codec = pdata->codec;
+ struct snd_soc_card *card = codec->component.card;
+
+ pr_debug("%s(): wsa shutdown, enable_cnt:%d\n", __func__,
+ pdata->enable_cnt);
+ if (--pdata->enable_cnt > 0)
+ return 0;
+ ret = wsa881x_reset(pdata, false);
+ if (ret) {
+ pr_err("%s: wsa reset failed suspend %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (pdata->enable_mclk) {
+ ret = pdata->enable_mclk(card, false);
+ if (ret < 0) {
+ pr_err("%s: mclk disable failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_clk");
+ if (ret) {
+ pr_err("%s: gpio set cannot be suspended %s\n",
+ __func__, "wsa_clk");
+ return ret;
+ }
+ if (pdata->codec) {
+ /* restore defaults to cache */
+ for (reg = 0; reg < ARRAY_SIZE(wsa881x_ana_reg_defaults);
+ reg++) {
+ if (wsa881x_ana_reg_readable[reg])
+ snd_soc_cache_write(pdata->codec,
+ wsa881x_ana_reg_defaults[reg].reg,
+ wsa881x_ana_reg_defaults[reg].def);
+ }
+ }
+ return 0;
+}
+
+static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec,
+ bool enable)
+{
+ int ret = 0;
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ if (enable) {
+ ret = wsa881x_startup(wsa881x);
+ if (ret < 0) {
+ dev_err_ratelimited(codec->dev,
+ "%s: failed to startup\n", __func__);
+ return ret;
+ }
+ }
+ wsa881x_clk_ctrl(codec, enable);
+ wsa881x_bandgap_ctrl(codec, enable);
+ if (!enable) {
+ ret = wsa881x_shutdown(wsa881x);
+ if (ret < 0)
+ dev_err_ratelimited(codec->dev,
+ "%s: failed to shutdown\n", __func__);
+ }
+ return ret;
+}
+
+static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec,
+ struct wsa_temp_register *wsa_temp_reg)
+{
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if (!wsa881x) {
+ dev_err(codec->dev, "%s: wsa881x is NULL\n", __func__);
+ return -EINVAL;
+ }
+ ret = wsa881x_resource_acquire(codec, true);
+ if (ret) {
+ dev_err_ratelimited(codec->dev,
+ "%s: resource acquire fail\n", __func__);
+ return ret;
+ }
+
+ if (WSA881X_IS_2_0(wsa881x->version)) {
+ snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00);
+ wsa_temp_reg->dmeas_msb = snd_soc_read(codec, WSA881X_TEMP_MSB);
+ wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, WSA881X_TEMP_LSB);
+ snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x01);
+ } else {
+ wsa_temp_reg->dmeas_msb = snd_soc_read(codec,
+ WSA881X_TEMP_DOUT_MSB);
+ wsa_temp_reg->dmeas_lsb = snd_soc_read(codec,
+ WSA881X_TEMP_DOUT_LSB);
+ }
+ wsa_temp_reg->d1_msb = snd_soc_read(codec, WSA881X_OTP_REG_1);
+ wsa_temp_reg->d1_lsb = snd_soc_read(codec, WSA881X_OTP_REG_2);
+ wsa_temp_reg->d2_msb = snd_soc_read(codec, WSA881X_OTP_REG_3);
+ wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4);
+
+ ret = wsa881x_resource_acquire(codec, false);
+ if (ret)
+ dev_err_ratelimited(codec->dev,
+ "%s: resource release fail\n", __func__);
+
+ return ret;
+}
+
+static int wsa881x_probe(struct snd_soc_codec *codec)
+{
+ struct i2c_client *client;
+ int ret = 0;
+ int wsa881x_index = 0;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ char *widget_name = NULL;
+ struct snd_soc_card *card = codec->component.card;
+ struct snd_soc_codec_conf *codec_conf = card->codec_conf;
+
+ client = dev_get_drvdata(codec->dev);
+ ret = wsa881x_i2c_get_client_index(client, &wsa881x_index);
+ if (ret != 0) {
+ dev_err(&client->dev, "%s: I2C get codec I2C\n"
+ "client failed\n", __func__);
+ return ret;
+ }
+ mutex_init(&wsa_pdata[wsa881x_index].bg_lock);
+ mutex_init(&wsa_pdata[wsa881x_index].res_lock);
+ snprintf(wsa_pdata[wsa881x_index].tz_pdata.name, 100, "%s",
+ wsa_tz_names[wsa881x_index]);
+ wsa_pdata[wsa881x_index].codec = codec;
+ wsa_pdata[wsa881x_index].spk_pa_gain = SPK_GAIN_12DB;
+ wsa_pdata[wsa881x_index].codec = codec;
+ wsa_pdata[wsa881x_index].tz_pdata.codec = codec;
+ wsa_pdata[wsa881x_index].tz_pdata.wsa_temp_reg_read =
+ wsa881x_temp_reg_read;
+ snd_soc_codec_set_drvdata(codec, &wsa_pdata[wsa881x_index]);
+ wsa881x_init_thermal(&wsa_pdata[wsa881x_index].tz_pdata);
+ INIT_DELAYED_WORK(&wsa_pdata[wsa881x_index].ocp_ctl_work,
+ wsa881x_ocp_ctl_work);
+
+ if (codec_conf->name_prefix) {
+ widget_name = kcalloc(WIDGET_NAME_MAX_SIZE, sizeof(char),
+ GFP_KERNEL);
+ if (!widget_name)
+ return -ENOMEM;
+
+ snprintf(widget_name, WIDGET_NAME_MAX_SIZE,
+ "%s WSA_SPKR", codec_conf->name_prefix);
+ snd_soc_dapm_ignore_suspend(dapm, widget_name);
+ snprintf(widget_name, WIDGET_NAME_MAX_SIZE,
+ "%s WSA_IN", codec_conf->name_prefix);
+ snd_soc_dapm_ignore_suspend(dapm, widget_name);
+ kfree(widget_name);
+ } else {
+ snd_soc_dapm_ignore_suspend(dapm, "WSA_SPKR");
+ snd_soc_dapm_ignore_suspend(dapm, "WSA_IN");
+ }
+
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static int wsa881x_remove(struct snd_soc_codec *codec)
+{
+ struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ if (wsa881x->tz_pdata.tz_dev)
+ wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev);
+
+ mutex_destroy(&wsa881x->bg_lock);
+ mutex_destroy(&wsa881x->res_lock);
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wsa881x = {
+ .probe = wsa881x_probe,
+ .remove = wsa881x_remove,
+
+ .read = wsa881x_i2c_read,
+ .write = wsa881x_i2c_write,
+
+ .reg_cache_size = WSA881X_CACHE_SIZE,
+ .reg_cache_default = wsa881x_ana_reg_defaults,
+ .reg_word_size = 1,
+
+ .controls = wsa881x_snd_controls,
+ .num_controls = ARRAY_SIZE(wsa881x_snd_controls),
+ .dapm_widgets = wsa881x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets),
+ .dapm_routes = wsa881x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map),
+};
+
+static int wsa881x_reset(struct wsa881x_pdata *pdata, bool enable)
+{
+ int ret = 0;
+
+ /*
+ * shutdown the GPIOs WSA_EN, WSA_MCLK, regulators
+ * and restore defaults in soc cache when shutdown.
+ * Enable regulators, GPIOs WSA_MCLK, WSA_EN when powerup.
+ */
+ if (enable) {
+ if (pdata->wsa_active)
+ return 0;
+ ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_reset");
+ if (ret) {
+ pr_err("%s: gpio set cannot be activated %s\n",
+ __func__, "wsa_reset");
+ return ret;
+ }
+ ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_reset");
+ if (ret) {
+ pr_err("%s: gpio set cannot be suspended(powerup) %s\n",
+ __func__, "wsa_reset");
+ return ret;
+ }
+ ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_reset");
+ if (ret) {
+ pr_err("%s: gpio set cannot be activated %s\n",
+ __func__, "wsa_reset");
+ return ret;
+ }
+ pdata->wsa_active = true;
+ } else {
+ if (!pdata->wsa_active)
+ return 0;
+ ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_reset");
+ if (ret) {
+ pr_err("%s: gpio set cannot be suspended %s\n",
+ __func__, "wsa_reset");
+ return ret;
+ }
+ pdata->wsa_active = false;
+ }
+ return ret;
+}
+
+int wsa881x_get_client_index(void)
+{
+ return wsa881x_i2c_addr;
+}
+EXPORT_SYMBOL(wsa881x_get_client_index);
+
+int wsa881x_get_probing_count(void)
+{
+ return wsa881x_probing_count;
+}
+EXPORT_SYMBOL(wsa881x_get_probing_count);
+
+int wsa881x_get_presence_count(void)
+{
+ return wsa881x_presence_count;
+}
+EXPORT_SYMBOL(wsa881x_get_presence_count);
+
+int wsa881x_set_mclk_callback(
+ int (*enable_mclk_callback)(struct snd_soc_card *, bool))
+{
+ int i;
+
+ for (i = 0; i < MAX_WSA881X_DEVICE; i++) {
+ if (wsa_pdata[i].status == WSA881X_STATUS_I2C)
+ wsa_pdata[i].enable_mclk = enable_mclk_callback;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(wsa881x_set_mclk_callback);
+
+static int check_wsa881x_presence(struct i2c_client *client)
+{
+ int ret = 0;
+ int wsa881x_index = 0;
+
+ ret = wsa881x_i2c_get_client_index(client, &wsa881x_index);
+ if (ret != 0) {
+ dev_err(&client->dev, "%s: I2C get codec I2C\n"
+ "client failed\n", __func__);
+ return ret;
+ }
+ ret = wsa881x_i2c_read_device(&wsa_pdata[wsa881x_index],
+ WSA881X_CDC_RST_CTL);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to read wsa881x with addr %x\n",
+ client->addr);
+ return ret;
+ }
+ ret = wsa881x_i2c_write_device(&wsa_pdata[wsa881x_index],
+ WSA881X_CDC_RST_CTL, 0x01);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed write addr %x reg:0x5 val:0x1\n",
+ client->addr);
+ return ret;
+ }
+ /* allow 20ms before trigger next write to verify bongo presence */
+ msleep(20);
+ ret = wsa881x_i2c_write_device(&wsa_pdata[wsa881x_index],
+ WSA881X_CDC_RST_CTL, 0x00);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed write addr %x reg:0x5 val:0x0\n",
+ client->addr);
+ return ret;
+ }
+ return ret;
+}
+
+static int wsa881x_populate_dt_pdata(struct device *dev)
+{
+ int ret = 0;
+
+ /* reading the gpio configurations from dtsi file */
+ if (!pinctrl_init) {
+ ret = msm_gpioset_initialize(CLIENT_WSA_BONGO_1, dev);
+ if (ret < 0) {
+ dev_err(dev,
+ "%s: error reading dtsi files%d\n", __func__, ret);
+ goto err;
+ }
+ pinctrl_init = true;
+ }
+err:
+ return ret;
+}
+
+static int wsa881x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ int wsa881x_index = 0;
+ struct wsa881x_pdata *pdata = NULL;
+
+ ret = wsa881x_i2c_get_client_index(client, &wsa881x_index);
+ if (ret != 0) {
+ dev_err(&client->dev, "%s: I2C get codec I2C\n"
+ "client failed\n", __func__);
+ return ret;
+ }
+
+ pdata = &wsa_pdata[wsa881x_index];
+
+ if ((client->addr == WSA881X_I2C_SPK0_SLAVE1_ADDR ||
+ client->addr == WSA881X_I2C_SPK1_SLAVE1_ADDR) &&
+ (pdata->status == WSA881X_STATUS_PROBING))
+ return ret;
+
+ if (pdata->status == WSA881X_STATUS_I2C) {
+ dev_dbg(&client->dev, "%s:probe for other slaves\n"
+ "devices of codec I2C slave Addr = %x\n",
+ __func__, client->addr);
+
+ dev_dbg(&client->dev, "%s:wsa_idx = %d SLAVE = %d\n",
+ __func__, wsa881x_index, WSA881X_ANALOG_SLAVE);
+ pdata->regmap[WSA881X_ANALOG_SLAVE] =
+ devm_regmap_init_i2c(
+ client,
+ &wsa881x_ana_regmap_config[WSA881X_ANALOG_SLAVE]);
+ regcache_cache_bypass(pdata->regmap[WSA881X_ANALOG_SLAVE],
+ true);
+ if (IS_ERR(pdata->regmap[WSA881X_ANALOG_SLAVE])) {
+ ret = PTR_ERR(pdata->regmap[WSA881X_ANALOG_SLAVE]);
+ dev_err(&client->dev,
+ "%s: regmap_init failed %d\n",
+ __func__, ret);
+ }
+ client->dev.platform_data = pdata;
+ i2c_set_clientdata(client, pdata);
+ pdata->client[WSA881X_ANALOG_SLAVE] = client;
+ if (pdata->version == WSA881X_2_0)
+ wsa881x_update_regmap_2_0(
+ pdata->regmap[WSA881X_ANALOG_SLAVE],
+ WSA881X_ANALOG_SLAVE);
+
+ return ret;
+ } else if (pdata->status == WSA881X_STATUS_PROBING) {
+ pdata->index = wsa881x_index;
+ if (client->dev.of_node) {
+ dev_dbg(&client->dev, "%s:Platform data\n"
+ "from device tree\n", __func__);
+ ret = wsa881x_populate_dt_pdata(&client->dev);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: Fail to obtain pdata from device tree\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ client->dev.platform_data = pdata;
+ } else {
+ dev_dbg(&client->dev, "%s:Platform data from\n"
+ "board file\n", __func__);
+ pdata = client->dev.platform_data;
+ }
+ if (!pdata) {
+ dev_dbg(&client->dev, "no platform data?\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ dev_set_drvdata(&client->dev, client);
+
+ pdata->regmap[WSA881X_DIGITAL_SLAVE] =
+ devm_regmap_init_i2c(
+ client,
+ &wsa881x_ana_regmap_config[WSA881X_DIGITAL_SLAVE]);
+ regcache_cache_bypass(pdata->regmap[WSA881X_DIGITAL_SLAVE],
+ true);
+ if (IS_ERR(pdata->regmap[WSA881X_DIGITAL_SLAVE])) {
+ ret = PTR_ERR(pdata->regmap[WSA881X_DIGITAL_SLAVE]);
+ dev_err(&client->dev, "%s: regmap_init failed %d\n",
+ __func__, ret);
+ goto err;
+ }
+ /* bus reset sequence */
+ ret = wsa881x_reset(pdata, true);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: WSA enable Failed %d\n",
+ __func__, ret);
+ goto err;
+ }
+ pdata->client[WSA881X_DIGITAL_SLAVE] = client;
+ pdata->regmap_flag = true;
+ ret = check_wsa881x_presence(client);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "failed to ping wsa with addr:%x, ret = %d\n",
+ client->addr, ret);
+ wsa881x_probing_count++;
+ goto err1;
+ }
+ pdata->version = wsa881x_i2c_read_device(pdata,
+ WSA881X_CHIP_ID1);
+ pr_debug("%s: wsa881x version: %d\n", __func__, pdata->version);
+ if (pdata->version == WSA881X_2_0) {
+ wsa881x_update_reg_defaults_2_0();
+ wsa881x_update_regmap_2_0(
+ pdata->regmap[WSA881X_DIGITAL_SLAVE],
+ WSA881X_DIGITAL_SLAVE);
+ }
+ wsa881x_presence_count++;
+ wsa881x_probing_count++;
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_dev_wsa881x,
+ NULL, 0);
+ if (ret < 0)
+ goto err1;
+ pdata->status = WSA881X_STATUS_I2C;
+ }
+err1:
+ wsa881x_reset(pdata, false);
+err:
+ return 0;
+}
+
+static int wsa881x_i2c_remove(struct i2c_client *client)
+{
+ struct wsa881x_pdata *wsa881x = client->dev.platform_data;
+
+ snd_soc_unregister_codec(&client->dev);
+ i2c_set_clientdata(client, NULL);
+ kfree(wsa881x);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int wsa881x_i2c_suspend(struct device *dev)
+{
+ pr_debug("%s: system suspend\n", __func__);
+ return 0;
+}
+
+static int wsa881x_i2c_resume(struct device *dev)
+{
+ pr_debug("%s: system resume\n", __func__);
+ return 0;
+}
+
+static const struct dev_pm_ops wsa881x_i2c_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(wsa881x_i2c_suspend, wsa881x_i2c_resume)
+};
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct i2c_device_id wsa881x_i2c_id[] = {
+ {"wsa881x-i2c-dev", WSA881X_I2C_SPK0_SLAVE0_ADDR},
+ {"wsa881x-i2c-dev", WSA881X_I2C_SPK0_SLAVE1_ADDR},
+ {"wsa881x-i2c-dev", WSA881X_I2C_SPK1_SLAVE0_ADDR},
+ {"wsa881x-i2c-dev", WSA881X_I2C_SPK1_SLAVE1_ADDR},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, wsa881x_i2c_id);
+
+
+static struct of_device_id msm_match_table[] = {
+ {.compatible = "qcom,wsa881x-i2c-codec"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_match_table);
+
+static struct i2c_driver wsa881x_codec_driver = {
+ .driver = {
+ .name = "wsa881x-i2c-codec",
+ .owner = THIS_MODULE,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+#ifdef CONFIG_PM_SLEEP
+ .pm = &wsa881x_i2c_pm_ops,
+#endif
+ .of_match_table = msm_match_table,
+ },
+ .id_table = wsa881x_i2c_id,
+ .probe = wsa881x_i2c_probe,
+ .remove = wsa881x_i2c_remove,
+};
+
+static int __init wsa881x_codec_init(void)
+{
+ int i = 0;
+
+ for (i = 0; i < MAX_WSA881X_DEVICE; i++)
+ wsa_pdata[i].status = WSA881X_STATUS_PROBING;
+ return i2c_add_driver(&wsa881x_codec_driver);
+}
+module_init(wsa881x_codec_init);
+
+static void __exit wsa881x_codec_exit(void)
+{
+ i2c_del_driver(&wsa881x_codec_driver);
+}
+
+module_exit(wsa881x_codec_exit);
+
+MODULE_DESCRIPTION("WSA881x Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wsa881x-analog.h b/sound/soc/codecs/wsa881x-analog.h
new file mode 100644
index 000000000000..a2ef2a284c23
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-analog.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WSA881X_H
+#define _WSA881X_H
+
+#include <linux/regmap.h>
+#include "wsa881x-registers-analog.h"
+#include <sound/soc.h>
+
+#define WSA881X_I2C_SPK0_SLAVE0_ADDR 0x0E
+#define WSA881X_I2C_SPK0_SLAVE1_ADDR 0x44
+#define WSA881X_I2C_SPK1_SLAVE0_ADDR 0x0F
+#define WSA881X_I2C_SPK1_SLAVE1_ADDR 0x45
+
+#define WSA881X_I2C_SPK0_SLAVE0 0
+#define WSA881X_I2C_SPK1_SLAVE0 1
+#define MAX_WSA881X_DEVICE 2
+#define WSA881X_DIGITAL_SLAVE 0
+#define WSA881X_ANALOG_SLAVE 1
+
+enum {
+ WSA881X_1_X = 0,
+ WSA881X_2_0,
+};
+
+#define WSA881X_IS_2_0(ver) \
+ ((ver == WSA881X_2_0) ? 1 : 0)
+
+extern const u8 wsa881x_ana_reg_readable[WSA881X_CACHE_SIZE];
+extern struct reg_default wsa881x_ana_reg_defaults[WSA881X_CACHE_SIZE];
+extern struct regmap_config wsa881x_ana_regmap_config[2];
+int wsa881x_get_client_index(void);
+int wsa881x_get_probing_count(void);
+int wsa881x_get_presence_count(void);
+int wsa881x_set_mclk_callback(
+ int (*enable_mclk_callback)(struct snd_soc_card *, bool));
+void wsa881x_update_reg_defaults_2_0(void);
+void wsa881x_update_regmap_2_0(struct regmap *regmap, int flag);
+
+#endif /* _WSA881X_H */
diff --git a/sound/soc/codecs/wsa881x-irq.c b/sound/soc/codecs/wsa881x-irq.c
new file mode 100644
index 000000000000..9afbd92b8f72
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-irq.c
@@ -0,0 +1,610 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/ratelimit.h>
+#include <linux/pm_qos.h>
+#include <soc/qcom/pm.h>
+#include "wsa881x-irq.h"
+#include "wsa881x-registers-analog.h"
+
+#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE))
+#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
+
+
+#define WSA_MAX_NUM_IRQS 8
+
+#ifndef NO_IRQ
+#define NO_IRQ (-1)
+#endif
+
+static int virq_to_phyirq(
+ struct wsa_resource *wsa_res, int virq);
+static int phyirq_to_virq(
+ struct wsa_resource *wsa_res, int irq);
+static unsigned int wsa_irq_get_upstream_irq(
+ struct wsa_resource *wsa_res);
+static void wsa_irq_put_upstream_irq(
+ struct wsa_resource *wsa_res);
+static int wsa_map_irq(
+ struct wsa_resource *wsa_res, int irq);
+
+static struct snd_soc_codec *ptr_codec;
+
+/**
+ * wsa_set_codec() - to update codec pointer
+ * @codec: codec pointer.
+ *
+ * To update the codec pointer, which is used to read/write
+ * wsa register.
+ *
+ * Return: void.
+ */
+void wsa_set_codec(struct snd_soc_codec *codec)
+{
+ if (codec == NULL) {
+ pr_err("%s: codec pointer is NULL\n", __func__);
+ ptr_codec = NULL;
+ return;
+ }
+ ptr_codec = codec;
+ /* Initialize interrupt mask and level registers */
+ snd_soc_write(codec, WSA881X_INTR_LEVEL, 0x8F);
+ snd_soc_write(codec, WSA881X_INTR_MASK, 0x8F);
+}
+
+static void wsa_irq_lock(struct irq_data *data)
+{
+ struct wsa_resource *wsa_res =
+ irq_data_get_irq_chip_data(data);
+
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res pointer is NULL\n", __func__);
+ return;
+ }
+ mutex_lock(&wsa_res->irq_lock);
+}
+
+static void wsa_irq_sync_unlock(struct irq_data *data)
+{
+ struct wsa_resource *wsa_res =
+ irq_data_get_irq_chip_data(data);
+
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res pointer is NULL\n", __func__);
+ return;
+ }
+ if (wsa_res->codec == NULL) {
+ pr_err("%s: codec pointer not registered\n", __func__);
+ if (ptr_codec == NULL) {
+ pr_err("%s: did not receive valid codec pointer\n",
+ __func__);
+ goto unlock;
+ } else {
+ wsa_res->codec = ptr_codec;
+ }
+ }
+
+ /*
+ * If there's been a change in the mask write it back
+ * to the hardware.
+ */
+ if (wsa_res->irq_masks_cur !=
+ wsa_res->irq_masks_cache) {
+
+ wsa_res->irq_masks_cache =
+ wsa_res->irq_masks_cur;
+ snd_soc_write(wsa_res->codec,
+ WSA881X_INTR_MASK,
+ wsa_res->irq_masks_cur);
+ }
+unlock:
+ mutex_unlock(&wsa_res->irq_lock);
+}
+
+static void wsa_irq_enable(struct irq_data *data)
+{
+ struct wsa_resource *wsa_res =
+ irq_data_get_irq_chip_data(data);
+ int wsa_irq;
+
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res pointer is NULL\n", __func__);
+ return;
+ }
+ wsa_irq = virq_to_phyirq(wsa_res, data->irq);
+ pr_debug("%s: wsa_irq = %d\n", __func__, wsa_irq);
+ wsa_res->irq_masks_cur &=
+ ~(BYTE_BIT_MASK(wsa_irq));
+}
+
+static void wsa_irq_disable(struct irq_data *data)
+{
+ struct wsa_resource *wsa_res =
+ irq_data_get_irq_chip_data(data);
+ int wsa_irq;
+
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res pointer is NULL\n", __func__);
+ return;
+ }
+ wsa_irq = virq_to_phyirq(wsa_res, data->irq);
+ pr_debug("%s: wsa_irq = %d\n", __func__, wsa_irq);
+ wsa_res->irq_masks_cur
+ |= BYTE_BIT_MASK(wsa_irq);
+}
+
+static void wsa_irq_ack(struct irq_data *data)
+{
+ int wsa_irq = 0;
+ struct wsa_resource *wsa_res =
+ irq_data_get_irq_chip_data(data);
+
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return;
+ }
+ wsa_irq = virq_to_phyirq(wsa_res, data->irq);
+ pr_debug("%s: IRQ_ACK called for WCD9XXX IRQ: %d\n",
+ __func__, wsa_irq);
+}
+
+static void wsa_irq_mask(struct irq_data *d)
+{
+ /* do nothing but required as linux calls irq_mask without NULL check */
+}
+
+static struct irq_chip wsa_irq_chip = {
+ .name = "wsa",
+ .irq_bus_lock = wsa_irq_lock,
+ .irq_bus_sync_unlock = wsa_irq_sync_unlock,
+ .irq_disable = wsa_irq_disable,
+ .irq_enable = wsa_irq_enable,
+ .irq_mask = wsa_irq_mask,
+ .irq_ack = wsa_irq_ack,
+};
+
+static irqreturn_t wsa_irq_thread(int irq, void *data)
+{
+ struct wsa_resource *wsa_res = data;
+ int i;
+ u8 status;
+
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return IRQ_HANDLED;
+ }
+ if (wsa_res->codec == NULL) {
+ pr_err("%s: codec pointer not registered\n", __func__);
+ if (ptr_codec == NULL) {
+ pr_err("%s: did not receive valid codec pointer\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+ wsa_res->codec = ptr_codec;
+ }
+ status = snd_soc_read(wsa_res->codec, WSA881X_INTR_STATUS);
+ /* Apply masking */
+ status &= ~wsa_res->irq_masks_cur;
+
+ for (i = 0; i < wsa_res->num_irqs; i++) {
+ if (status & BYTE_BIT_MASK(i)) {
+ mutex_lock(&wsa_res->nested_irq_lock);
+ handle_nested_irq(phyirq_to_virq(wsa_res, i));
+ mutex_unlock(&wsa_res->nested_irq_lock);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * wsa_free_irq() - to free an interrupt
+ * @irq: interrupt number.
+ * @data: pointer to wsa resource.
+ *
+ * To free already requested interrupt.
+ *
+ * Return: void.
+ */
+void wsa_free_irq(int irq, void *data)
+{
+ struct wsa_resource *wsa_res = data;
+
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return;
+ }
+ free_irq(phyirq_to_virq(wsa_res, irq), data);
+}
+
+/**
+ * wsa_enable_irq() - to enable an interrupt
+ * @wsa_res: pointer to wsa resource.
+ * @irq: interrupt number.
+ *
+ * This function is to enable an interrupt.
+ *
+ * Return: void.
+ */
+void wsa_enable_irq(struct wsa_resource *wsa_res, int irq)
+{
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return;
+ }
+ enable_irq(phyirq_to_virq(wsa_res, irq));
+}
+
+/**
+ * wsa_disable_irq() - to disable an interrupt
+ * @wsa_res: pointer to wsa resource.
+ * @irq: interrupt number.
+ *
+ * To disable an interrupt without waiting for executing
+ * handler to complete.
+ *
+ * Return: void.
+ */
+void wsa_disable_irq(struct wsa_resource *wsa_res, int irq)
+{
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return;
+ }
+ disable_irq_nosync(phyirq_to_virq(wsa_res, irq));
+}
+
+/**
+ * wsa_disable_irq_sync() - to disable an interrupt
+ * @wsa_res: pointer to wsa resource.
+ * @irq: interrupt number.
+ *
+ * To disable an interrupt, wait for executing IRQ
+ * handler to complete.
+ *
+ * Return: void.
+ */
+void wsa_disable_irq_sync(
+ struct wsa_resource *wsa_res, int irq)
+{
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return;
+ }
+ disable_irq(phyirq_to_virq(wsa_res, irq));
+}
+
+static int wsa_irq_setup_downstream_irq(struct wsa_resource *wsa_res)
+{
+ int irq, virq, ret;
+
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: enter\n", __func__);
+
+ for (irq = 0; irq < wsa_res->num_irqs; irq++) {
+ /* Map OF irq */
+ virq = wsa_map_irq(wsa_res, irq);
+ pr_debug("%s: irq %d -> %d\n", __func__, irq, virq);
+ if (virq == NO_IRQ) {
+ pr_err("%s, No interrupt specifier for irq %d\n",
+ __func__, irq);
+ return NO_IRQ;
+ }
+
+ ret = irq_set_chip_data(virq, wsa_res);
+ if (ret) {
+ pr_err("%s: Failed to configure irq %d (%d)\n",
+ __func__, irq, ret);
+ return ret;
+ }
+
+ if (wsa_res->irq_level_high[irq])
+ irq_set_chip_and_handler(virq, &wsa_irq_chip,
+ handle_level_irq);
+ else
+ irq_set_chip_and_handler(virq, &wsa_irq_chip,
+ handle_edge_irq);
+
+ irq_set_nested_thread(virq, 1);
+ }
+
+ pr_debug("%s: leave\n", __func__);
+
+ return 0;
+}
+
+static int wsa_irq_init(struct wsa_resource *wsa_res)
+{
+ int i, ret;
+
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return -EINVAL;
+ }
+ mutex_init(&wsa_res->irq_lock);
+ mutex_init(&wsa_res->nested_irq_lock);
+
+ wsa_res->irq = wsa_irq_get_upstream_irq(wsa_res);
+ if (!wsa_res->irq) {
+ pr_warn("%s: irq driver is not yet initialized\n", __func__);
+ mutex_destroy(&wsa_res->irq_lock);
+ mutex_destroy(&wsa_res->nested_irq_lock);
+ return -EPROBE_DEFER;
+ }
+ pr_debug("%s: probed irq %d\n", __func__, wsa_res->irq);
+
+ /* Setup downstream IRQs */
+ ret = wsa_irq_setup_downstream_irq(wsa_res);
+ if (ret) {
+ pr_err("%s: Failed to setup downstream IRQ\n", __func__);
+ goto fail_irq_init;
+ }
+
+ /* mask all the interrupts */
+ for (i = 0; i < wsa_res->num_irqs; i++) {
+ wsa_res->irq_masks_cur |= BYTE_BIT_MASK(i);
+ wsa_res->irq_masks_cache |= BYTE_BIT_MASK(i);
+ }
+
+ ret = request_threaded_irq(wsa_res->irq, NULL, wsa_irq_thread,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "wsa", wsa_res);
+ if (ret != 0) {
+ dev_err(wsa_res->dev, "Failed to request IRQ %d: %d\n",
+ wsa_res->irq, ret);
+ } else {
+ ret = enable_irq_wake(wsa_res->irq);
+ if (ret) {
+ dev_err(wsa_res->dev,
+ "Failed to set wake interrupt on IRQ %d: %d\n",
+ wsa_res->irq, ret);
+ free_irq(wsa_res->irq, wsa_res);
+ }
+ }
+
+ if (ret)
+ goto fail_irq_init;
+
+ return ret;
+
+fail_irq_init:
+ dev_err(wsa_res->dev,
+ "%s: Failed to init wsa irq\n", __func__);
+ wsa_irq_put_upstream_irq(wsa_res);
+ mutex_destroy(&wsa_res->irq_lock);
+ mutex_destroy(&wsa_res->nested_irq_lock);
+ return ret;
+}
+
+/**
+ * wsa_request_irq() - to request/register an interrupt
+ * @wsa_res: pointer to wsa_resource.
+ * @irq: interrupt number.
+ * @handler: interrupt handler function pointer.
+ * @name: interrupt name.
+ * @data: device info.
+ *
+ * Convert physical irq to virtual irq and then
+ * reguest for threaded handler.
+ *
+ * Return: Retuns success/failure.
+ */
+int wsa_request_irq(struct wsa_resource *wsa_res,
+ int irq, irq_handler_t handler,
+ const char *name, void *data)
+{
+ int virq;
+
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return -EINVAL;
+ }
+ virq = phyirq_to_virq(wsa_res, irq);
+
+ /*
+ * ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so.
+ */
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+ set_irq_flags(virq, IRQF_VALID);
+#else
+ set_irq_noprobe(virq);
+#endif
+
+ return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING,
+ name, data);
+}
+
+/**
+ * wsa_irq_exit() - to disable/clear interrupt/resources
+ * @wsa_res: pointer to wsa_resource
+ *
+ * Disable and free the interrupts and then release resources.
+ *
+ * Return: void.
+ */
+void wsa_irq_exit(struct wsa_resource *wsa_res)
+{
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return;
+ }
+ dev_dbg(wsa_res->dev, "%s: Cleaning up irq %d\n", __func__,
+ wsa_res->irq);
+
+ if (wsa_res->irq) {
+ disable_irq_wake(wsa_res->irq);
+ free_irq(wsa_res->irq, wsa_res);
+ /* Release parent's of node */
+ wsa_irq_put_upstream_irq(wsa_res);
+ }
+ mutex_destroy(&wsa_res->irq_lock);
+ mutex_destroy(&wsa_res->nested_irq_lock);
+}
+
+static int phyirq_to_virq(struct wsa_resource *wsa_res, int offset)
+{
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return -EINVAL;
+ }
+ return irq_linear_revmap(wsa_res->domain, offset);
+}
+
+static int virq_to_phyirq(struct wsa_resource *wsa_res, int virq)
+{
+ struct irq_data *irq_data = irq_get_irq_data(virq);
+
+ if (unlikely(!irq_data)) {
+ pr_err("%s: irq_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+ return irq_data->hwirq;
+}
+
+static unsigned int wsa_irq_get_upstream_irq(struct wsa_resource *wsa_res)
+{
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return -EINVAL;
+ }
+ return wsa_res->irq;
+}
+
+static void wsa_irq_put_upstream_irq(struct wsa_resource *wsa_res)
+{
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return;
+ }
+ /* Hold parent's of node */
+ of_node_put(wsa_res->dev->of_node);
+}
+
+static int wsa_map_irq(struct wsa_resource *wsa_res, int irq)
+{
+ if (wsa_res == NULL) {
+ pr_err("%s: wsa_res is NULL\n", __func__);
+ return -EINVAL;
+ }
+ return of_irq_to_resource(wsa_res->dev->of_node, irq, NULL);
+}
+
+static int wsa_irq_probe(struct platform_device *pdev)
+{
+ int irq;
+ struct wsa_resource *wsa_res = NULL;
+ int ret = -EINVAL;
+
+ irq = platform_get_irq_byname(pdev, "wsa-int");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "%s: Couldn't find wsa-int node(%d)\n",
+ __func__, irq);
+ return -EINVAL;
+ }
+ pr_debug("%s: node %s\n", __func__, pdev->name);
+ wsa_res = kzalloc(sizeof(*wsa_res), GFP_KERNEL);
+ if (!wsa_res) {
+ pr_err("%s: could not allocate memory\n", __func__);
+ return -ENOMEM;
+ }
+ /*
+ * wsa interrupt controller supports N to N irq mapping with
+ * single cell binding with irq numbers(offsets) only.
+ * Use irq_domain_simple_ops that has irq_domain_simple_map and
+ * irq_domain_xlate_onetwocell.
+ */
+ wsa_res->dev = &pdev->dev;
+ wsa_res->domain = irq_domain_add_linear(wsa_res->dev->of_node,
+ WSA_MAX_NUM_IRQS, &irq_domain_simple_ops,
+ wsa_res);
+ if (!wsa_res->domain) {
+ dev_err(&pdev->dev, "%s: domain is NULL\n", __func__);
+ ret = -ENOMEM;
+ goto err;
+ }
+ wsa_res->dev = &pdev->dev;
+
+ dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq);
+ wsa_res->irq = irq;
+ wsa_res->num_irq_regs = 1;
+ wsa_res->num_irqs = WSA_NUM_IRQS;
+ ret = wsa_irq_init(wsa_res);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s: failed to do irq init %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ return ret;
+err:
+ kfree(wsa_res);
+ return ret;
+}
+
+static int wsa_irq_remove(struct platform_device *pdev)
+{
+ struct irq_domain *domain;
+ struct wsa_resource *data;
+
+ domain = irq_find_host(pdev->dev.of_node);
+ if (unlikely(!domain)) {
+ pr_err("%s: domain is NULL\n", __func__);
+ return -EINVAL;
+ }
+ data = (struct wsa_resource *)domain->host_data;
+ data->irq = 0;
+
+ return 0;
+}
+
+static const struct of_device_id of_match[] = {
+ { .compatible = "qcom,wsa-irq" },
+ { }
+};
+
+static struct platform_driver wsa_irq_driver = {
+ .probe = wsa_irq_probe,
+ .remove = wsa_irq_remove,
+ .driver = {
+ .name = "wsa_intc",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_match),
+ },
+};
+
+static int wsa_irq_drv_init(void)
+{
+ return platform_driver_register(&wsa_irq_driver);
+}
+subsys_initcall(wsa_irq_drv_init);
+
+static void wsa_irq_drv_exit(void)
+{
+ platform_driver_unregister(&wsa_irq_driver);
+}
+module_exit(wsa_irq_drv_exit);
+
+MODULE_DESCRIPTION("WSA881x IRQ driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wsa881x-irq.h b/sound/soc/codecs/wsa881x-irq.h
new file mode 100644
index 000000000000..270eb917a666
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-irq.h
@@ -0,0 +1,82 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WSA881X_IRQ_H__
+#define __WSA881X_IRQ_H__
+
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <sound/soc.h>
+
+/**
+ * enum wsa_interrupts - wsa interrupt number
+ * @WSA_INT_SAF2WAR: Temp irq interrupt, from safe state to warning state.
+ * @WSA_INT_WAR2SAF: Temp irq interrupt, from warning state to safe state.
+ * @WSA_INT_DISABLE: Disable Temp sensor interrupts.
+ * @WSA_INT_OCP: OCP interrupt.
+ * @WSA_INT_CLIP: CLIP detect interrupt.
+ * @WSA_NUM_IRQS: MAX Interrupt number.
+ *
+ * WSA IRQ Interrupt numbers.
+ */
+enum wsa_interrupts {
+ WSA_INT_SAF2WAR = 0,
+ WSA_INT_WAR2SAF,
+ WSA_INT_DISABLE,
+ WSA_INT_OCP,
+ WSA_INT_CLIP,
+ WSA_NUM_IRQS,
+};
+
+/**
+ * struct wsa_resource - the basic wsa_resource structure
+ * @irq_lock: lock used by irq_chip functions.
+ * @nested_irq_lock: lock used while handling nested interrupts.
+ * @irq: interrupt number.
+ * @irq_masks_cur: current mask value to be written to mask registers.
+ * @irq_masks_cache: cached mask value.
+ * @num_irqs: number of supported interrupts.
+ * @num_irq_regs: number of irq registers.
+ * @parent: parent pointer.
+ * @dev: device pointer.
+ * @domain: irq domain pointer.
+ * codec: codec pointer.
+ *
+ * Contains required members used in wsa irq driver.
+ */
+
+struct wsa_resource {
+ struct mutex irq_lock;
+ struct mutex nested_irq_lock;
+ unsigned int irq;
+ u8 irq_masks_cur;
+ u8 irq_masks_cache;
+ bool irq_level_high[8];
+ int num_irqs;
+ int num_irq_regs;
+ void *parent;
+ struct device *dev;
+ struct irq_domain *domain;
+ struct snd_soc_codec *codec;
+};
+
+void wsa_set_codec(struct snd_soc_codec *codec);
+void wsa_free_irq(int irq, void *data);
+void wsa_enable_irq(struct wsa_resource *wsa_res, int irq);
+void wsa_disable_irq(struct wsa_resource *wsa_res, int irq);
+void wsa_disable_irq_sync(struct wsa_resource *wsa_res, int irq);
+int wsa_request_irq(struct wsa_resource *wsa_res,
+ int irq, irq_handler_t handler,
+ const char *name, void *data);
+
+void wsa_irq_exit(struct wsa_resource *wsa_res);
+
+#endif /* __WSA881X_IRQ_H__ */
diff --git a/sound/soc/codecs/wsa881x-registers-analog.h b/sound/soc/codecs/wsa881x-registers-analog.h
new file mode 100644
index 000000000000..a5ebf8e1aab4
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-registers-analog.h
@@ -0,0 +1,206 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WSA881X_REGISTERS_H
+#define WSA881X_REGISTERS_H
+
+#define WSA881X_DIGITAL_BASE 0x0000
+#define WSA881X_ANALOG_BASE 0x0100
+
+#define WSA881X_CHIP_ID0 (WSA881X_DIGITAL_BASE+0x0000)
+#define WSA881X_CHIP_ID1 (WSA881X_DIGITAL_BASE+0x0001)
+#define WSA881X_CHIP_ID2 (WSA881X_DIGITAL_BASE+0x0002)
+#define WSA881X_CHIP_ID3 (WSA881X_DIGITAL_BASE+0x0003)
+#define WSA881X_BUS_ID (WSA881X_DIGITAL_BASE+0x0004)
+#define WSA881X_CDC_RST_CTL (WSA881X_DIGITAL_BASE+0x0005)
+#define WSA881X_CDC_TOP_CLK_CTL (WSA881X_DIGITAL_BASE+0x0006)
+#define WSA881X_CDC_ANA_CLK_CTL (WSA881X_DIGITAL_BASE+0x0007)
+#define WSA881X_CDC_DIG_CLK_CTL (WSA881X_DIGITAL_BASE+0x0008)
+#define WSA881X_CLOCK_CONFIG (WSA881X_DIGITAL_BASE+0x0009)
+#define WSA881X_ANA_CTL (WSA881X_DIGITAL_BASE+0x000A)
+#define WSA881X_SWR_RESET_EN (WSA881X_DIGITAL_BASE+0x000B)
+#define WSA881X_RESET_CTL (WSA881X_DIGITAL_BASE+0x000C)
+#define WSA881X_TADC_VALUE_CTL (WSA881X_DIGITAL_BASE+0x000F)
+#define WSA881X_TEMP_DETECT_CTL (WSA881X_DIGITAL_BASE+0x0010)
+#define WSA881X_TEMP_MSB (WSA881X_DIGITAL_BASE+0x0011)
+#define WSA881X_TEMP_LSB (WSA881X_DIGITAL_BASE+0x0012)
+#define WSA881X_TEMP_CONFIG0 (WSA881X_DIGITAL_BASE+0x0013)
+#define WSA881X_TEMP_CONFIG1 (WSA881X_DIGITAL_BASE+0x0014)
+#define WSA881X_CDC_CLIP_CTL (WSA881X_DIGITAL_BASE+0x0015)
+#define WSA881X_SDM_PDM9_LSB (WSA881X_DIGITAL_BASE+0x0016)
+#define WSA881X_SDM_PDM9_MSB (WSA881X_DIGITAL_BASE+0x0017)
+#define WSA881X_CDC_RX_CTL (WSA881X_DIGITAL_BASE+0x0018)
+#define WSA881X_DEM_BYPASS_DATA0 (WSA881X_DIGITAL_BASE+0x0019)
+#define WSA881X_DEM_BYPASS_DATA1 (WSA881X_DIGITAL_BASE+0x001A)
+#define WSA881X_DEM_BYPASS_DATA2 (WSA881X_DIGITAL_BASE+0x001B)
+#define WSA881X_DEM_BYPASS_DATA3 (WSA881X_DIGITAL_BASE+0x001C)
+#define WSA881X_OTP_CTRL0 (WSA881X_DIGITAL_BASE+0x001D)
+#define WSA881X_OTP_CTRL1 (WSA881X_DIGITAL_BASE+0x001E)
+#define WSA881X_HDRIVE_CTL_GROUP1 (WSA881X_DIGITAL_BASE+0x001F)
+#define WSA881X_INTR_MODE (WSA881X_DIGITAL_BASE+0x0020)
+#define WSA881X_INTR_MASK (WSA881X_DIGITAL_BASE+0x0021)
+#define WSA881X_INTR_STATUS (WSA881X_DIGITAL_BASE+0x0022)
+#define WSA881X_INTR_CLEAR (WSA881X_DIGITAL_BASE+0x0023)
+#define WSA881X_INTR_LEVEL (WSA881X_DIGITAL_BASE+0x0024)
+#define WSA881X_INTR_SET (WSA881X_DIGITAL_BASE+0x0025)
+#define WSA881X_INTR_TEST (WSA881X_DIGITAL_BASE+0x0026)
+#define WSA881X_PDM_TEST_MODE (WSA881X_DIGITAL_BASE+0x0030)
+#define WSA881X_ATE_TEST_MODE (WSA881X_DIGITAL_BASE+0x0031)
+#define WSA881X_PIN_CTL_MODE (WSA881X_DIGITAL_BASE+0x0032)
+#define WSA881X_PIN_CTL_OE (WSA881X_DIGITAL_BASE+0x0033)
+#define WSA881X_PIN_WDATA_IOPAD (WSA881X_DIGITAL_BASE+0x0034)
+#define WSA881X_PIN_STATUS (WSA881X_DIGITAL_BASE+0x0035)
+#define WSA881X_DIG_DEBUG_MODE (WSA881X_DIGITAL_BASE+0x0037)
+#define WSA881X_DIG_DEBUG_SEL (WSA881X_DIGITAL_BASE+0x0038)
+#define WSA881X_DIG_DEBUG_EN (WSA881X_DIGITAL_BASE+0x0039)
+#define WSA881X_SWR_HM_TEST1 (WSA881X_DIGITAL_BASE+0x003B)
+#define WSA881X_SWR_HM_TEST2 (WSA881X_DIGITAL_BASE+0x003C)
+#define WSA881X_TEMP_DETECT_DBG_CTL (WSA881X_DIGITAL_BASE+0x003D)
+#define WSA881X_TEMP_DEBUG_MSB (WSA881X_DIGITAL_BASE+0x003E)
+#define WSA881X_TEMP_DEBUG_LSB (WSA881X_DIGITAL_BASE+0x003F)
+#define WSA881X_SAMPLE_EDGE_SEL (WSA881X_DIGITAL_BASE+0x0044)
+#define WSA881X_IOPAD_CTL (WSA881X_DIGITAL_BASE+0x0045)
+#define WSA881X_SPARE_0 (WSA881X_DIGITAL_BASE+0x0050)
+#define WSA881X_SPARE_1 (WSA881X_DIGITAL_BASE+0x0051)
+#define WSA881X_SPARE_2 (WSA881X_DIGITAL_BASE+0x0052)
+#define WSA881X_OTP_REG_0 (WSA881X_DIGITAL_BASE+0x0080)
+#define WSA881X_OTP_REG_1 (WSA881X_DIGITAL_BASE+0x0081)
+#define WSA881X_OTP_REG_2 (WSA881X_DIGITAL_BASE+0x0082)
+#define WSA881X_OTP_REG_3 (WSA881X_DIGITAL_BASE+0x0083)
+#define WSA881X_OTP_REG_4 (WSA881X_DIGITAL_BASE+0x0084)
+#define WSA881X_OTP_REG_5 (WSA881X_DIGITAL_BASE+0x0085)
+#define WSA881X_OTP_REG_6 (WSA881X_DIGITAL_BASE+0x0086)
+#define WSA881X_OTP_REG_7 (WSA881X_DIGITAL_BASE+0x0087)
+#define WSA881X_OTP_REG_8 (WSA881X_DIGITAL_BASE+0x0088)
+#define WSA881X_OTP_REG_9 (WSA881X_DIGITAL_BASE+0x0089)
+#define WSA881X_OTP_REG_10 (WSA881X_DIGITAL_BASE+0x008A)
+#define WSA881X_OTP_REG_11 (WSA881X_DIGITAL_BASE+0x008B)
+#define WSA881X_OTP_REG_12 (WSA881X_DIGITAL_BASE+0x008C)
+#define WSA881X_OTP_REG_13 (WSA881X_DIGITAL_BASE+0x008D)
+#define WSA881X_OTP_REG_14 (WSA881X_DIGITAL_BASE+0x008E)
+#define WSA881X_OTP_REG_15 (WSA881X_DIGITAL_BASE+0x008F)
+#define WSA881X_OTP_REG_16 (WSA881X_DIGITAL_BASE+0x0090)
+#define WSA881X_OTP_REG_17 (WSA881X_DIGITAL_BASE+0x0091)
+#define WSA881X_OTP_REG_18 (WSA881X_DIGITAL_BASE+0x0092)
+#define WSA881X_OTP_REG_19 (WSA881X_DIGITAL_BASE+0x0093)
+#define WSA881X_OTP_REG_20 (WSA881X_DIGITAL_BASE+0x0094)
+#define WSA881X_OTP_REG_21 (WSA881X_DIGITAL_BASE+0x0095)
+#define WSA881X_OTP_REG_22 (WSA881X_DIGITAL_BASE+0x0096)
+#define WSA881X_OTP_REG_23 (WSA881X_DIGITAL_BASE+0x0097)
+#define WSA881X_OTP_REG_24 (WSA881X_DIGITAL_BASE+0x0098)
+#define WSA881X_OTP_REG_25 (WSA881X_DIGITAL_BASE+0x0099)
+#define WSA881X_OTP_REG_26 (WSA881X_DIGITAL_BASE+0x009A)
+#define WSA881X_OTP_REG_27 (WSA881X_DIGITAL_BASE+0x009B)
+#define WSA881X_OTP_REG_28 (WSA881X_DIGITAL_BASE+0x009C)
+#define WSA881X_OTP_REG_29 (WSA881X_DIGITAL_BASE+0x009D)
+#define WSA881X_OTP_REG_30 (WSA881X_DIGITAL_BASE+0x009E)
+#define WSA881X_OTP_REG_31 (WSA881X_DIGITAL_BASE+0x009F)
+#define WSA881X_OTP_REG_32 (WSA881X_DIGITAL_BASE+0x00A0)
+#define WSA881X_OTP_REG_33 (WSA881X_DIGITAL_BASE+0x00A1)
+#define WSA881X_OTP_REG_34 (WSA881X_DIGITAL_BASE+0x00A2)
+#define WSA881X_OTP_REG_35 (WSA881X_DIGITAL_BASE+0x00A3)
+#define WSA881X_OTP_REG_36 (WSA881X_DIGITAL_BASE+0x00A4)
+#define WSA881X_OTP_REG_37 (WSA881X_DIGITAL_BASE+0x00A5)
+#define WSA881X_OTP_REG_38 (WSA881X_DIGITAL_BASE+0x00A6)
+#define WSA881X_OTP_REG_39 (WSA881X_DIGITAL_BASE+0x00A7)
+#define WSA881X_OTP_REG_40 (WSA881X_DIGITAL_BASE+0x00A8)
+#define WSA881X_OTP_REG_41 (WSA881X_DIGITAL_BASE+0x00A9)
+#define WSA881X_OTP_REG_42 (WSA881X_DIGITAL_BASE+0x00AA)
+#define WSA881X_OTP_REG_43 (WSA881X_DIGITAL_BASE+0x00AB)
+#define WSA881X_OTP_REG_44 (WSA881X_DIGITAL_BASE+0x00AC)
+#define WSA881X_OTP_REG_45 (WSA881X_DIGITAL_BASE+0x00AD)
+#define WSA881X_OTP_REG_46 (WSA881X_DIGITAL_BASE+0x00AE)
+#define WSA881X_OTP_REG_47 (WSA881X_DIGITAL_BASE+0x00AF)
+#define WSA881X_OTP_REG_48 (WSA881X_DIGITAL_BASE+0x00B0)
+#define WSA881X_OTP_REG_49 (WSA881X_DIGITAL_BASE+0x00B1)
+#define WSA881X_OTP_REG_50 (WSA881X_DIGITAL_BASE+0x00B2)
+#define WSA881X_OTP_REG_51 (WSA881X_DIGITAL_BASE+0x00B3)
+#define WSA881X_OTP_REG_52 (WSA881X_DIGITAL_BASE+0x00B4)
+#define WSA881X_OTP_REG_53 (WSA881X_DIGITAL_BASE+0x00B5)
+#define WSA881X_OTP_REG_54 (WSA881X_DIGITAL_BASE+0x00B6)
+#define WSA881X_OTP_REG_55 (WSA881X_DIGITAL_BASE+0x00B7)
+#define WSA881X_OTP_REG_56 (WSA881X_DIGITAL_BASE+0x00B8)
+#define WSA881X_OTP_REG_57 (WSA881X_DIGITAL_BASE+0x00B9)
+#define WSA881X_OTP_REG_58 (WSA881X_DIGITAL_BASE+0x00BA)
+#define WSA881X_OTP_REG_59 (WSA881X_DIGITAL_BASE+0x00BB)
+#define WSA881X_OTP_REG_60 (WSA881X_DIGITAL_BASE+0x00BC)
+#define WSA881X_OTP_REG_61 (WSA881X_DIGITAL_BASE+0x00BD)
+#define WSA881X_OTP_REG_62 (WSA881X_DIGITAL_BASE+0x00BE)
+#define WSA881X_OTP_REG_63 (WSA881X_DIGITAL_BASE+0x00BF)
+/* Analog Register address space */
+#define WSA881X_BIAS_REF_CTRL (WSA881X_ANALOG_BASE+0x0000)
+#define WSA881X_BIAS_TEST (WSA881X_ANALOG_BASE+0x0001)
+#define WSA881X_BIAS_BIAS (WSA881X_ANALOG_BASE+0x0002)
+#define WSA881X_TEMP_OP (WSA881X_ANALOG_BASE+0x0003)
+#define WSA881X_TEMP_IREF_CTRL (WSA881X_ANALOG_BASE+0x0004)
+#define WSA881X_TEMP_ISENS_CTRL (WSA881X_ANALOG_BASE+0x0005)
+#define WSA881X_TEMP_CLK_CTRL (WSA881X_ANALOG_BASE+0x0006)
+#define WSA881X_TEMP_TEST (WSA881X_ANALOG_BASE+0x0007)
+#define WSA881X_TEMP_BIAS (WSA881X_ANALOG_BASE+0x0008)
+#define WSA881X_TEMP_ADC_CTRL (WSA881X_ANALOG_BASE+0x0009)
+#define WSA881X_TEMP_DOUT_MSB (WSA881X_ANALOG_BASE+0x000A)
+#define WSA881X_TEMP_DOUT_LSB (WSA881X_ANALOG_BASE+0x000B)
+#define WSA881X_ADC_EN_MODU_V (WSA881X_ANALOG_BASE+0x0010)
+#define WSA881X_ADC_EN_MODU_I (WSA881X_ANALOG_BASE+0x0011)
+#define WSA881X_ADC_EN_DET_TEST_V (WSA881X_ANALOG_BASE+0x0012)
+#define WSA881X_ADC_EN_DET_TEST_I (WSA881X_ANALOG_BASE+0x0013)
+#define WSA881X_ADC_SEL_IBIAS (WSA881X_ANALOG_BASE+0x0014)
+#define WSA881X_ADC_EN_SEL_IBIAS (WSA881X_ANALOG_BASE+0x0015)
+#define WSA881X_SPKR_DRV_EN (WSA881X_ANALOG_BASE+0x001A)
+#define WSA881X_SPKR_DRV_GAIN (WSA881X_ANALOG_BASE+0x001B)
+#define WSA881X_SPKR_DAC_CTL (WSA881X_ANALOG_BASE+0x001C)
+#define WSA881X_SPKR_DRV_DBG (WSA881X_ANALOG_BASE+0x001D)
+#define WSA881X_SPKR_PWRSTG_DBG (WSA881X_ANALOG_BASE+0x001E)
+#define WSA881X_SPKR_OCP_CTL (WSA881X_ANALOG_BASE+0x001F)
+#define WSA881X_SPKR_CLIP_CTL (WSA881X_ANALOG_BASE+0x0020)
+#define WSA881X_SPKR_BBM_CTL (WSA881X_ANALOG_BASE+0x0021)
+#define WSA881X_SPKR_MISC_CTL1 (WSA881X_ANALOG_BASE+0x0022)
+#define WSA881X_SPKR_MISC_CTL2 (WSA881X_ANALOG_BASE+0x0023)
+#define WSA881X_SPKR_BIAS_INT (WSA881X_ANALOG_BASE+0x0024)
+#define WSA881X_SPKR_PA_INT (WSA881X_ANALOG_BASE+0x0025)
+#define WSA881X_SPKR_BIAS_CAL (WSA881X_ANALOG_BASE+0x0026)
+#define WSA881X_SPKR_BIAS_PSRR (WSA881X_ANALOG_BASE+0x0027)
+#define WSA881X_SPKR_STATUS1 (WSA881X_ANALOG_BASE+0x0028)
+#define WSA881X_SPKR_STATUS2 (WSA881X_ANALOG_BASE+0x0029)
+#define WSA881X_BOOST_EN_CTL (WSA881X_ANALOG_BASE+0x002A)
+#define WSA881X_BOOST_CURRENT_LIMIT (WSA881X_ANALOG_BASE+0x002B)
+#define WSA881X_BOOST_PS_CTL (WSA881X_ANALOG_BASE+0x002C)
+#define WSA881X_BOOST_PRESET_OUT1 (WSA881X_ANALOG_BASE+0x002D)
+#define WSA881X_BOOST_PRESET_OUT2 (WSA881X_ANALOG_BASE+0x002E)
+#define WSA881X_BOOST_FORCE_OUT (WSA881X_ANALOG_BASE+0x002F)
+#define WSA881X_BOOST_LDO_PROG (WSA881X_ANALOG_BASE+0x0030)
+#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB (WSA881X_ANALOG_BASE+0x0031)
+#define WSA881X_BOOST_RON_CTL (WSA881X_ANALOG_BASE+0x0032)
+#define WSA881X_BOOST_LOOP_STABILITY (WSA881X_ANALOG_BASE+0x0033)
+#define WSA881X_BOOST_ZX_CTL (WSA881X_ANALOG_BASE+0x0034)
+#define WSA881X_BOOST_START_CTL (WSA881X_ANALOG_BASE+0x0035)
+#define WSA881X_BOOST_MISC1_CTL (WSA881X_ANALOG_BASE+0x0036)
+#define WSA881X_BOOST_MISC2_CTL (WSA881X_ANALOG_BASE+0x0037)
+#define WSA881X_BOOST_MISC3_CTL (WSA881X_ANALOG_BASE+0x0038)
+#define WSA881X_BOOST_ATEST_CTL (WSA881X_ANALOG_BASE+0x0039)
+#define WSA881X_SPKR_PROT_FE_GAIN (WSA881X_ANALOG_BASE+0x003A)
+#define WSA881X_SPKR_PROT_FE_CM_LDO_SET (WSA881X_ANALOG_BASE+0x003B)
+#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x003C)
+#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 (WSA881X_ANALOG_BASE+0x003D)
+#define WSA881X_SPKR_PROT_ATEST1 (WSA881X_ANALOG_BASE+0x003E)
+#define WSA881X_SPKR_PROT_ATEST2 (WSA881X_ANALOG_BASE+0x003F)
+#define WSA881X_SPKR_PROT_FE_VSENSE_VCM (WSA881X_ANALOG_BASE+0x0040)
+#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x0041)
+#define WSA881X_BONGO_RESRV_REG1 (WSA881X_ANALOG_BASE+0x0042)
+#define WSA881X_BONGO_RESRV_REG2 (WSA881X_ANALOG_BASE+0x0043)
+#define WSA881X_SPKR_PROT_SAR (WSA881X_ANALOG_BASE+0x0044)
+#define WSA881X_SPKR_STATUS3 (WSA881X_ANALOG_BASE+0x0045)
+
+#define WSA881X_NUM_REGISTERS (WSA881X_SPKR_STATUS3+1)
+#define WSA881X_MAX_REGISTER (WSA881X_NUM_REGISTERS-1)
+#define WSA881X_CACHE_SIZE WSA881X_NUM_REGISTERS
+#endif /* WSA881X_REGISTERS_H */
diff --git a/sound/soc/codecs/wsa881x-registers.h b/sound/soc/codecs/wsa881x-registers.h
new file mode 100644
index 000000000000..825a5f034da8
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-registers.h
@@ -0,0 +1,178 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WSA881X_REGISTERS_H
+#define WSA881X_REGISTERS_H
+
+#define WSA881X_DIGITAL_BASE 0x3000
+#define WSA881X_ANALOG_BASE 0x3100
+
+/* Digital register address space */
+#define WSA881X_CHIP_ID0 (WSA881X_DIGITAL_BASE+0x0000)
+#define WSA881X_CHIP_ID1 (WSA881X_DIGITAL_BASE+0x0001)
+#define WSA881X_CHIP_ID2 (WSA881X_DIGITAL_BASE+0x0002)
+#define WSA881X_CHIP_ID3 (WSA881X_DIGITAL_BASE+0x0003)
+#define WSA881X_BUS_ID (WSA881X_DIGITAL_BASE+0x0004)
+#define WSA881X_CDC_RST_CTL (WSA881X_DIGITAL_BASE+0x0005)
+#define WSA881X_CDC_TOP_CLK_CTL (WSA881X_DIGITAL_BASE+0x0006)
+#define WSA881X_CDC_ANA_CLK_CTL (WSA881X_DIGITAL_BASE+0x0007)
+#define WSA881X_CDC_DIG_CLK_CTL (WSA881X_DIGITAL_BASE+0x0008)
+#define WSA881X_CLOCK_CONFIG (WSA881X_DIGITAL_BASE+0x0009)
+#define WSA881X_ANA_CTL (WSA881X_DIGITAL_BASE+0x000A)
+#define WSA881X_SWR_RESET_EN (WSA881X_DIGITAL_BASE+0x000B)
+#define WSA881X_RESET_CTL (WSA881X_DIGITAL_BASE+0x000C)
+#define WSA881X_TADC_VALUE_CTL (WSA881X_DIGITAL_BASE+0x000F)
+#define WSA881X_TEMP_DETECT_CTL (WSA881X_DIGITAL_BASE+0x0010)
+#define WSA881X_TEMP_MSB (WSA881X_DIGITAL_BASE+0x0011)
+#define WSA881X_TEMP_LSB (WSA881X_DIGITAL_BASE+0x0012)
+#define WSA881X_TEMP_CONFIG0 (WSA881X_DIGITAL_BASE+0x0013)
+#define WSA881X_TEMP_CONFIG1 (WSA881X_DIGITAL_BASE+0x0014)
+#define WSA881X_CDC_CLIP_CTL (WSA881X_DIGITAL_BASE+0x0015)
+#define WSA881X_SDM_PDM9_LSB (WSA881X_DIGITAL_BASE+0x0016)
+#define WSA881X_SDM_PDM9_MSB (WSA881X_DIGITAL_BASE+0x0017)
+#define WSA881X_CDC_RX_CTL (WSA881X_DIGITAL_BASE+0x0018)
+#define WSA881X_DEM_BYPASS_DATA0 (WSA881X_DIGITAL_BASE+0x0019)
+#define WSA881X_DEM_BYPASS_DATA1 (WSA881X_DIGITAL_BASE+0x001A)
+#define WSA881X_DEM_BYPASS_DATA2 (WSA881X_DIGITAL_BASE+0x001B)
+#define WSA881X_DEM_BYPASS_DATA3 (WSA881X_DIGITAL_BASE+0x001C)
+#define WSA881X_OTP_CTRL0 (WSA881X_DIGITAL_BASE+0x001D)
+#define WSA881X_OTP_CTRL1 (WSA881X_DIGITAL_BASE+0x001E)
+#define WSA881X_HDRIVE_CTL_GROUP1 (WSA881X_DIGITAL_BASE+0x001F)
+#define WSA881X_INTR_MODE (WSA881X_DIGITAL_BASE+0x0020)
+#define WSA881X_INTR_MASK (WSA881X_DIGITAL_BASE+0x0021)
+#define WSA881X_INTR_STATUS (WSA881X_DIGITAL_BASE+0x0022)
+#define WSA881X_INTR_CLEAR (WSA881X_DIGITAL_BASE+0x0023)
+#define WSA881X_INTR_LEVEL (WSA881X_DIGITAL_BASE+0x0024)
+#define WSA881X_INTR_SET (WSA881X_DIGITAL_BASE+0x0025)
+#define WSA881X_INTR_TEST (WSA881X_DIGITAL_BASE+0x0026)
+#define WSA881X_PDM_TEST_MODE (WSA881X_DIGITAL_BASE+0x0030)
+#define WSA881X_ATE_TEST_MODE (WSA881X_DIGITAL_BASE+0x0031)
+#define WSA881X_PIN_CTL_MODE (WSA881X_DIGITAL_BASE+0x0032)
+#define WSA881X_PIN_CTL_OE (WSA881X_DIGITAL_BASE+0x0033)
+#define WSA881X_PIN_WDATA_IOPAD (WSA881X_DIGITAL_BASE+0x0034)
+#define WSA881X_PIN_STATUS (WSA881X_DIGITAL_BASE+0x0035)
+#define WSA881X_DIG_DEBUG_MODE (WSA881X_DIGITAL_BASE+0x0037)
+#define WSA881X_DIG_DEBUG_SEL (WSA881X_DIGITAL_BASE+0x0038)
+#define WSA881X_DIG_DEBUG_EN (WSA881X_DIGITAL_BASE+0x0039)
+#define WSA881X_SWR_HM_TEST1 (WSA881X_DIGITAL_BASE+0x003B)
+#define WSA881X_SWR_HM_TEST2 (WSA881X_DIGITAL_BASE+0x003C)
+#define WSA881X_TEMP_DETECT_DBG_CTL (WSA881X_DIGITAL_BASE+0x003D)
+#define WSA881X_TEMP_DEBUG_MSB (WSA881X_DIGITAL_BASE+0x003E)
+#define WSA881X_TEMP_DEBUG_LSB (WSA881X_DIGITAL_BASE+0x003F)
+#define WSA881X_SAMPLE_EDGE_SEL (WSA881X_DIGITAL_BASE+0x0044)
+#define WSA881X_IOPAD_CTL (WSA881X_DIGITAL_BASE+0x0045)
+#define WSA881X_SPARE_0 (WSA881X_DIGITAL_BASE+0x0050)
+#define WSA881X_SPARE_1 (WSA881X_DIGITAL_BASE+0x0051)
+#define WSA881X_SPARE_2 (WSA881X_DIGITAL_BASE+0x0052)
+#define WSA881X_OTP_REG_0 (WSA881X_DIGITAL_BASE+0x0080)
+#define WSA881X_OTP_REG_1 (WSA881X_DIGITAL_BASE+0x0081)
+#define WSA881X_OTP_REG_2 (WSA881X_DIGITAL_BASE+0x0082)
+#define WSA881X_OTP_REG_3 (WSA881X_DIGITAL_BASE+0x0083)
+#define WSA881X_OTP_REG_4 (WSA881X_DIGITAL_BASE+0x0084)
+#define WSA881X_OTP_REG_5 (WSA881X_DIGITAL_BASE+0x0085)
+#define WSA881X_OTP_REG_6 (WSA881X_DIGITAL_BASE+0x0086)
+#define WSA881X_OTP_REG_7 (WSA881X_DIGITAL_BASE+0x0087)
+#define WSA881X_OTP_REG_8 (WSA881X_DIGITAL_BASE+0x0088)
+#define WSA881X_OTP_REG_9 (WSA881X_DIGITAL_BASE+0x0089)
+#define WSA881X_OTP_REG_10 (WSA881X_DIGITAL_BASE+0x008A)
+#define WSA881X_OTP_REG_11 (WSA881X_DIGITAL_BASE+0x008B)
+#define WSA881X_OTP_REG_12 (WSA881X_DIGITAL_BASE+0x008C)
+#define WSA881X_OTP_REG_13 (WSA881X_DIGITAL_BASE+0x008D)
+#define WSA881X_OTP_REG_14 (WSA881X_DIGITAL_BASE+0x008E)
+#define WSA881X_OTP_REG_15 (WSA881X_DIGITAL_BASE+0x008F)
+#define WSA881X_OTP_REG_16 (WSA881X_DIGITAL_BASE+0x0090)
+#define WSA881X_OTP_REG_17 (WSA881X_DIGITAL_BASE+0x0091)
+#define WSA881X_OTP_REG_18 (WSA881X_DIGITAL_BASE+0x0092)
+#define WSA881X_OTP_REG_19 (WSA881X_DIGITAL_BASE+0x0093)
+#define WSA881X_OTP_REG_20 (WSA881X_DIGITAL_BASE+0x0094)
+#define WSA881X_OTP_REG_21 (WSA881X_DIGITAL_BASE+0x0095)
+#define WSA881X_OTP_REG_22 (WSA881X_DIGITAL_BASE+0x0096)
+#define WSA881X_OTP_REG_23 (WSA881X_DIGITAL_BASE+0x0097)
+#define WSA881X_OTP_REG_24 (WSA881X_DIGITAL_BASE+0x0098)
+#define WSA881X_OTP_REG_25 (WSA881X_DIGITAL_BASE+0x0099)
+#define WSA881X_OTP_REG_26 (WSA881X_DIGITAL_BASE+0x009A)
+#define WSA881X_OTP_REG_27 (WSA881X_DIGITAL_BASE+0x009B)
+#define WSA881X_OTP_REG_28 (WSA881X_DIGITAL_BASE+0x009C)
+#define WSA881X_OTP_REG_29 (WSA881X_DIGITAL_BASE+0x009D)
+#define WSA881X_OTP_REG_30 (WSA881X_DIGITAL_BASE+0x009E)
+#define WSA881X_OTP_REG_31 (WSA881X_DIGITAL_BASE+0x009F)
+#define WSA881X_OTP_REG_63 (WSA881X_DIGITAL_BASE+0x00BF)
+
+/* Analog Register address space */
+#define WSA881X_BIAS_REF_CTRL (WSA881X_ANALOG_BASE+0x0000)
+#define WSA881X_BIAS_TEST (WSA881X_ANALOG_BASE+0x0001)
+#define WSA881X_BIAS_BIAS (WSA881X_ANALOG_BASE+0x0002)
+#define WSA881X_TEMP_OP (WSA881X_ANALOG_BASE+0x0003)
+#define WSA881X_TEMP_IREF_CTRL (WSA881X_ANALOG_BASE+0x0004)
+#define WSA881X_TEMP_ISENS_CTRL (WSA881X_ANALOG_BASE+0x0005)
+#define WSA881X_TEMP_CLK_CTRL (WSA881X_ANALOG_BASE+0x0006)
+#define WSA881X_TEMP_TEST (WSA881X_ANALOG_BASE+0x0007)
+#define WSA881X_TEMP_BIAS (WSA881X_ANALOG_BASE+0x0008)
+#define WSA881X_TEMP_ADC_CTRL (WSA881X_ANALOG_BASE+0x0009)
+#define WSA881X_TEMP_DOUT_MSB (WSA881X_ANALOG_BASE+0x000A)
+#define WSA881X_TEMP_DOUT_LSB (WSA881X_ANALOG_BASE+0x000B)
+#define WSA881X_ADC_EN_MODU_V (WSA881X_ANALOG_BASE+0x0010)
+#define WSA881X_ADC_EN_MODU_I (WSA881X_ANALOG_BASE+0x0011)
+#define WSA881X_ADC_EN_DET_TEST_V (WSA881X_ANALOG_BASE+0x0012)
+#define WSA881X_ADC_EN_DET_TEST_I (WSA881X_ANALOG_BASE+0x0013)
+#define WSA881X_ADC_SEL_IBIAS (WSA881X_ANALOG_BASE+0x0014)
+#define WSA881X_ADC_EN_SEL_IBAIS (WSA881X_ANALOG_BASE+0x0015)
+#define WSA881X_SPKR_DRV_EN (WSA881X_ANALOG_BASE+0x001A)
+#define WSA881X_SPKR_DRV_GAIN (WSA881X_ANALOG_BASE+0x001B)
+#define WSA881X_SPKR_DAC_CTL (WSA881X_ANALOG_BASE+0x001C)
+#define WSA881X_SPKR_DRV_DBG (WSA881X_ANALOG_BASE+0x001D)
+#define WSA881X_SPKR_PWRSTG_DBG (WSA881X_ANALOG_BASE+0x001E)
+#define WSA881X_SPKR_OCP_CTL (WSA881X_ANALOG_BASE+0x001F)
+#define WSA881X_SPKR_CLIP_CTL (WSA881X_ANALOG_BASE+0x0020)
+#define WSA881X_SPKR_BBM_CTL (WSA881X_ANALOG_BASE+0x0021)
+#define WSA881X_SPKR_MISC_CTL1 (WSA881X_ANALOG_BASE+0x0022)
+#define WSA881X_SPKR_MISC_CTL2 (WSA881X_ANALOG_BASE+0x0023)
+#define WSA881X_SPKR_BIAS_INT (WSA881X_ANALOG_BASE+0x0024)
+#define WSA881X_SPKR_PA_INT (WSA881X_ANALOG_BASE+0x0025)
+#define WSA881X_SPKR_BIAS_CAL (WSA881X_ANALOG_BASE+0x0026)
+#define WSA881X_SPKR_BIAS_PSRR (WSA881X_ANALOG_BASE+0x0027)
+#define WSA881X_SPKR_STATUS1 (WSA881X_ANALOG_BASE+0x0028)
+#define WSA881X_SPKR_STATUS2 (WSA881X_ANALOG_BASE+0x0029)
+#define WSA881X_BOOST_EN_CTL (WSA881X_ANALOG_BASE+0x002A)
+#define WSA881X_BOOST_CURRENT_LIMIT (WSA881X_ANALOG_BASE+0x002B)
+#define WSA881X_BOOST_PS_CTL (WSA881X_ANALOG_BASE+0x002C)
+#define WSA881X_BOOST_PRESET_OUT1 (WSA881X_ANALOG_BASE+0x002D)
+#define WSA881X_BOOST_PRESET_OUT2 (WSA881X_ANALOG_BASE+0x002E)
+#define WSA881X_BOOST_FORCE_OUT (WSA881X_ANALOG_BASE+0x002F)
+#define WSA881X_BOOST_LDO_PROG (WSA881X_ANALOG_BASE+0x0030)
+#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB (WSA881X_ANALOG_BASE+0x0031)
+#define WSA881X_BOOST_RON_CTL (WSA881X_ANALOG_BASE+0x0032)
+#define WSA881X_BOOST_LOOP_STABILITY (WSA881X_ANALOG_BASE+0x0033)
+#define WSA881X_BOOST_ZX_CTL (WSA881X_ANALOG_BASE+0x0034)
+#define WSA881X_BOOST_START_CTL (WSA881X_ANALOG_BASE+0x0035)
+#define WSA881X_BOOST_MISC1_CTL (WSA881X_ANALOG_BASE+0x0036)
+#define WSA881X_BOOST_MISC2_CTL (WSA881X_ANALOG_BASE+0x0037)
+#define WSA881X_BOOST_MISC3_CTL (WSA881X_ANALOG_BASE+0x0038)
+#define WSA881X_BOOST_ATEST_CTL (WSA881X_ANALOG_BASE+0x0039)
+#define WSA881X_SPKR_PROT_FE_GAIN (WSA881X_ANALOG_BASE+0x003A)
+#define WSA881X_SPKR_PROT_FE_CM_LDO_SET (WSA881X_ANALOG_BASE+0x003B)
+#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x003C)
+#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 (WSA881X_ANALOG_BASE+0x003D)
+#define WSA881X_SPKR_PROT_ATEST1 (WSA881X_ANALOG_BASE+0x003E)
+#define WSA881X_SPKR_PROT_ATEST2 (WSA881X_ANALOG_BASE+0x003F)
+#define WSA881X_SPKR_PROT_FE_VSENSE_VCM (WSA881X_ANALOG_BASE+0x0040)
+#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x0041)
+#define WSA881X_BONGO_RESRV_REG1 (WSA881X_ANALOG_BASE+0x0042)
+#define WSA881X_BONGO_RESRV_REG2 (WSA881X_ANALOG_BASE+0x0043)
+#define WSA881X_SPKR_PROT_SAR (WSA881X_ANALOG_BASE+0x0044)
+#define WSA881X_SPKR_STATUS3 (WSA881X_ANALOG_BASE+0x0045)
+
+#define WSA881X_NUM_REGISTERS (WSA881X_SPKR_STATUS3+1)
+#define WSA881X_MAX_REGISTER (WSA881X_NUM_REGISTERS-1)
+#define WSA881X_CACHE_SIZE WSA881X_NUM_REGISTERS
+
+#endif /* WSA881X_REGISTERS_H */
diff --git a/sound/soc/codecs/wsa881x-regmap-analog.c b/sound/soc/codecs/wsa881x-regmap-analog.c
new file mode 100644
index 000000000000..96621b14e1fe
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-regmap-analog.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "wsa881x-registers-analog.h"
+#include "wsa881x-analog.h"
+
+struct reg_default wsa881x_ana_reg_defaults[] = {
+ {WSA881X_CHIP_ID0, 0x00},
+ {WSA881X_CHIP_ID1, 0x00},
+ {WSA881X_CHIP_ID2, 0x00},
+ {WSA881X_CHIP_ID3, 0x02},
+ {WSA881X_BUS_ID, 0x00},
+ {WSA881X_CDC_RST_CTL, 0x00},
+ {WSA881X_CDC_TOP_CLK_CTL, 0x03},
+ {WSA881X_CDC_ANA_CLK_CTL, 0x00},
+ {WSA881X_CDC_DIG_CLK_CTL, 0x00},
+ {WSA881X_CLOCK_CONFIG, 0x00},
+ {WSA881X_ANA_CTL, 0x08},
+ {WSA881X_SWR_RESET_EN, 0x00},
+ {WSA881X_TEMP_DETECT_CTL, 0x01},
+ {WSA881X_TEMP_MSB, 0x00},
+ {WSA881X_TEMP_LSB, 0x00},
+ {WSA881X_TEMP_CONFIG0, 0x00},
+ {WSA881X_TEMP_CONFIG1, 0x00},
+ {WSA881X_CDC_CLIP_CTL, 0x03},
+ {WSA881X_SDM_PDM9_LSB, 0x00},
+ {WSA881X_SDM_PDM9_MSB, 0x00},
+ {WSA881X_CDC_RX_CTL, 0x7E},
+ {WSA881X_DEM_BYPASS_DATA0, 0x00},
+ {WSA881X_DEM_BYPASS_DATA1, 0x00},
+ {WSA881X_DEM_BYPASS_DATA2, 0x00},
+ {WSA881X_DEM_BYPASS_DATA3, 0x00},
+ {WSA881X_OTP_CTRL0, 0x00},
+ {WSA881X_OTP_CTRL1, 0x00},
+ {WSA881X_HDRIVE_CTL_GROUP1, 0x00},
+ {WSA881X_INTR_MODE, 0x00},
+ {WSA881X_INTR_MASK, 0x1F},
+ {WSA881X_INTR_STATUS, 0x00},
+ {WSA881X_INTR_CLEAR, 0x00},
+ {WSA881X_INTR_LEVEL, 0x00},
+ {WSA881X_INTR_SET, 0x00},
+ {WSA881X_INTR_TEST, 0x00},
+ {WSA881X_PDM_TEST_MODE, 0x00},
+ {WSA881X_ATE_TEST_MODE, 0x00},
+ {WSA881X_PIN_CTL_MODE, 0x00},
+ {WSA881X_PIN_CTL_OE, 0x00},
+ {WSA881X_PIN_WDATA_IOPAD, 0x00},
+ {WSA881X_PIN_STATUS, 0x00},
+ {WSA881X_DIG_DEBUG_MODE, 0x00},
+ {WSA881X_DIG_DEBUG_SEL, 0x00},
+ {WSA881X_DIG_DEBUG_EN, 0x00},
+ {WSA881X_SWR_HM_TEST1, 0x08},
+ {WSA881X_SWR_HM_TEST2, 0x00},
+ {WSA881X_TEMP_DETECT_DBG_CTL, 0x00},
+ {WSA881X_TEMP_DEBUG_MSB, 0x00},
+ {WSA881X_TEMP_DEBUG_LSB, 0x00},
+ {WSA881X_SAMPLE_EDGE_SEL, 0x0C},
+ {WSA881X_SPARE_0, 0x00},
+ {WSA881X_SPARE_1, 0x00},
+ {WSA881X_SPARE_2, 0x00},
+ {WSA881X_OTP_REG_0, 0x01},
+ {WSA881X_OTP_REG_1, 0xFF},
+ {WSA881X_OTP_REG_2, 0xC0},
+ {WSA881X_OTP_REG_3, 0xFF},
+ {WSA881X_OTP_REG_4, 0xC0},
+ {WSA881X_OTP_REG_5, 0xFF},
+ {WSA881X_OTP_REG_6, 0xFF},
+ {WSA881X_OTP_REG_7, 0xFF},
+ {WSA881X_OTP_REG_8, 0xFF},
+ {WSA881X_OTP_REG_9, 0xFF},
+ {WSA881X_OTP_REG_10, 0xFF},
+ {WSA881X_OTP_REG_11, 0xFF},
+ {WSA881X_OTP_REG_12, 0xFF},
+ {WSA881X_OTP_REG_13, 0xFF},
+ {WSA881X_OTP_REG_14, 0xFF},
+ {WSA881X_OTP_REG_15, 0xFF},
+ {WSA881X_OTP_REG_16, 0xFF},
+ {WSA881X_OTP_REG_17, 0xFF},
+ {WSA881X_OTP_REG_18, 0xFF},
+ {WSA881X_OTP_REG_19, 0xFF},
+ {WSA881X_OTP_REG_20, 0xFF},
+ {WSA881X_OTP_REG_21, 0xFF},
+ {WSA881X_OTP_REG_22, 0xFF},
+ {WSA881X_OTP_REG_23, 0xFF},
+ {WSA881X_OTP_REG_24, 0x03},
+ {WSA881X_OTP_REG_25, 0x01},
+ {WSA881X_OTP_REG_26, 0x03},
+ {WSA881X_OTP_REG_27, 0x11},
+ {WSA881X_OTP_REG_28, 0xFF},
+ {WSA881X_OTP_REG_29, 0xFF},
+ {WSA881X_OTP_REG_30, 0xFF},
+ {WSA881X_OTP_REG_31, 0xFF},
+ {WSA881X_OTP_REG_63, 0x40},
+ /* WSA881x Analog registers */
+ {WSA881X_BIAS_REF_CTRL, 0x6C},
+ {WSA881X_BIAS_TEST, 0x16},
+ {WSA881X_BIAS_BIAS, 0xF0},
+ {WSA881X_TEMP_OP, 0x00},
+ {WSA881X_TEMP_IREF_CTRL, 0x56},
+ {WSA881X_TEMP_ISENS_CTRL, 0x47},
+ {WSA881X_TEMP_CLK_CTRL, 0x87},
+ {WSA881X_TEMP_TEST, 0x00},
+ {WSA881X_TEMP_BIAS, 0x51},
+ {WSA881X_TEMP_ADC_CTRL, 0x00},
+ {WSA881X_TEMP_DOUT_MSB, 0x00},
+ {WSA881X_TEMP_DOUT_LSB, 0x00},
+ {WSA881X_ADC_EN_MODU_V, 0x00},
+ {WSA881X_ADC_EN_MODU_I, 0x00},
+ {WSA881X_ADC_EN_DET_TEST_V, 0x00},
+ {WSA881X_ADC_EN_DET_TEST_I, 0x00},
+ {WSA881X_ADC_SEL_IBIAS, 0x25},
+ {WSA881X_ADC_EN_SEL_IBIAS, 0x10},
+ {WSA881X_SPKR_DRV_EN, 0x74},
+ {WSA881X_SPKR_DRV_GAIN, 0x01},
+ {WSA881X_SPKR_DAC_CTL, 0x40},
+ {WSA881X_SPKR_DRV_DBG, 0x15},
+ {WSA881X_SPKR_PWRSTG_DBG, 0x00},
+ {WSA881X_SPKR_OCP_CTL, 0xD4},
+ {WSA881X_SPKR_CLIP_CTL, 0x90},
+ {WSA881X_SPKR_BBM_CTL, 0x00},
+ {WSA881X_SPKR_MISC_CTL1, 0x80},
+ {WSA881X_SPKR_MISC_CTL2, 0x00},
+ {WSA881X_SPKR_BIAS_INT, 0x56},
+ {WSA881X_SPKR_PA_INT, 0x54},
+ {WSA881X_SPKR_BIAS_CAL, 0xAC},
+ {WSA881X_SPKR_BIAS_PSRR, 0x54},
+ {WSA881X_SPKR_STATUS1, 0x00},
+ {WSA881X_SPKR_STATUS2, 0x00},
+ {WSA881X_BOOST_EN_CTL, 0x18},
+ {WSA881X_BOOST_CURRENT_LIMIT, 0x7A},
+ {WSA881X_BOOST_PS_CTL, 0xC0},
+ {WSA881X_BOOST_PRESET_OUT1, 0x77},
+ {WSA881X_BOOST_PRESET_OUT2, 0x70},
+ {WSA881X_BOOST_FORCE_OUT, 0x0E},
+ {WSA881X_BOOST_LDO_PROG, 0x16},
+ {WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71},
+ {WSA881X_BOOST_RON_CTL, 0x0F},
+ {WSA881X_BOOST_LOOP_STABILITY, 0xAD},
+ {WSA881X_BOOST_ZX_CTL, 0x34},
+ {WSA881X_BOOST_START_CTL, 0x23},
+ {WSA881X_BOOST_MISC1_CTL, 0x80},
+ {WSA881X_BOOST_MISC2_CTL, 0x00},
+ {WSA881X_BOOST_MISC3_CTL, 0x00},
+ {WSA881X_BOOST_ATEST_CTL, 0x00},
+ {WSA881X_SPKR_PROT_FE_GAIN, 0x46},
+ {WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B},
+ {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D},
+ {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D},
+ {WSA881X_SPKR_PROT_ATEST1, 0x01},
+ {WSA881X_SPKR_PROT_ATEST2, 0x00},
+ {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D},
+ {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D},
+ {WSA881X_BONGO_RESRV_REG1, 0x00},
+ {WSA881X_BONGO_RESRV_REG2, 0x00},
+ {WSA881X_SPKR_PROT_SAR, 0x00},
+ {WSA881X_SPKR_STATUS3, 0x00},
+};
+
+struct reg_default wsa881x_ana_reg_defaults_0[] = {
+ {WSA881X_CHIP_ID0, 0x00},
+ {WSA881X_CHIP_ID1, 0x00},
+ {WSA881X_CHIP_ID2, 0x00},
+ {WSA881X_CHIP_ID3, 0x02},
+ {WSA881X_BUS_ID, 0x00},
+ {WSA881X_CDC_RST_CTL, 0x00},
+ {WSA881X_CDC_TOP_CLK_CTL, 0x03},
+ {WSA881X_CDC_ANA_CLK_CTL, 0x00},
+ {WSA881X_CDC_DIG_CLK_CTL, 0x00},
+ {WSA881X_CLOCK_CONFIG, 0x00},
+ {WSA881X_ANA_CTL, 0x08},
+ {WSA881X_SWR_RESET_EN, 0x00},
+ {WSA881X_TEMP_DETECT_CTL, 0x01},
+ {WSA881X_TEMP_MSB, 0x00},
+ {WSA881X_TEMP_LSB, 0x00},
+ {WSA881X_TEMP_CONFIG0, 0x00},
+ {WSA881X_TEMP_CONFIG1, 0x00},
+ {WSA881X_CDC_CLIP_CTL, 0x03},
+ {WSA881X_SDM_PDM9_LSB, 0x00},
+ {WSA881X_SDM_PDM9_MSB, 0x00},
+ {WSA881X_CDC_RX_CTL, 0x7E},
+ {WSA881X_DEM_BYPASS_DATA0, 0x00},
+ {WSA881X_DEM_BYPASS_DATA1, 0x00},
+ {WSA881X_DEM_BYPASS_DATA2, 0x00},
+ {WSA881X_DEM_BYPASS_DATA3, 0x00},
+ {WSA881X_OTP_CTRL0, 0x00},
+ {WSA881X_OTP_CTRL1, 0x00},
+ {WSA881X_HDRIVE_CTL_GROUP1, 0x00},
+ {WSA881X_INTR_MODE, 0x00},
+ {WSA881X_INTR_MASK, 0x1F},
+ {WSA881X_INTR_STATUS, 0x00},
+ {WSA881X_INTR_CLEAR, 0x00},
+ {WSA881X_INTR_LEVEL, 0x00},
+ {WSA881X_INTR_SET, 0x00},
+ {WSA881X_INTR_TEST, 0x00},
+ {WSA881X_PDM_TEST_MODE, 0x00},
+ {WSA881X_ATE_TEST_MODE, 0x00},
+ {WSA881X_PIN_CTL_MODE, 0x00},
+ {WSA881X_PIN_CTL_OE, 0x00},
+ {WSA881X_PIN_WDATA_IOPAD, 0x00},
+ {WSA881X_PIN_STATUS, 0x00},
+ {WSA881X_DIG_DEBUG_MODE, 0x00},
+ {WSA881X_DIG_DEBUG_SEL, 0x00},
+ {WSA881X_DIG_DEBUG_EN, 0x00},
+ {WSA881X_SWR_HM_TEST1, 0x08},
+ {WSA881X_SWR_HM_TEST2, 0x00},
+ {WSA881X_TEMP_DETECT_DBG_CTL, 0x00},
+ {WSA881X_TEMP_DEBUG_MSB, 0x00},
+ {WSA881X_TEMP_DEBUG_LSB, 0x00},
+ {WSA881X_SAMPLE_EDGE_SEL, 0x0C},
+ {WSA881X_SPARE_0, 0x00},
+ {WSA881X_SPARE_1, 0x00},
+ {WSA881X_SPARE_2, 0x00},
+ {WSA881X_OTP_REG_0, 0x01},
+ {WSA881X_OTP_REG_1, 0xFF},
+ {WSA881X_OTP_REG_2, 0xC0},
+ {WSA881X_OTP_REG_3, 0xFF},
+ {WSA881X_OTP_REG_4, 0xC0},
+ {WSA881X_OTP_REG_5, 0xFF},
+ {WSA881X_OTP_REG_6, 0xFF},
+ {WSA881X_OTP_REG_7, 0xFF},
+ {WSA881X_OTP_REG_8, 0xFF},
+ {WSA881X_OTP_REG_9, 0xFF},
+ {WSA881X_OTP_REG_10, 0xFF},
+ {WSA881X_OTP_REG_11, 0xFF},
+ {WSA881X_OTP_REG_12, 0xFF},
+ {WSA881X_OTP_REG_13, 0xFF},
+ {WSA881X_OTP_REG_14, 0xFF},
+ {WSA881X_OTP_REG_15, 0xFF},
+ {WSA881X_OTP_REG_16, 0xFF},
+ {WSA881X_OTP_REG_17, 0xFF},
+ {WSA881X_OTP_REG_18, 0xFF},
+ {WSA881X_OTP_REG_19, 0xFF},
+ {WSA881X_OTP_REG_20, 0xFF},
+ {WSA881X_OTP_REG_21, 0xFF},
+ {WSA881X_OTP_REG_22, 0xFF},
+ {WSA881X_OTP_REG_23, 0xFF},
+ {WSA881X_OTP_REG_24, 0x03},
+ {WSA881X_OTP_REG_25, 0x01},
+ {WSA881X_OTP_REG_26, 0x03},
+ {WSA881X_OTP_REG_27, 0x11},
+ {WSA881X_OTP_REG_28, 0xFF},
+ {WSA881X_OTP_REG_29, 0xFF},
+ {WSA881X_OTP_REG_30, 0xFF},
+ {WSA881X_OTP_REG_31, 0xFF},
+ {WSA881X_OTP_REG_63, 0x40},
+};
+
+struct reg_default wsa881x_ana_reg_defaults_1[] = {
+ {WSA881X_BIAS_REF_CTRL - WSA881X_ANALOG_BASE, 0x6C},
+ {WSA881X_BIAS_TEST - WSA881X_ANALOG_BASE, 0x16},
+ {WSA881X_BIAS_BIAS - WSA881X_ANALOG_BASE, 0xF0},
+ {WSA881X_TEMP_OP - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_TEMP_IREF_CTRL - WSA881X_ANALOG_BASE, 0x56},
+ {WSA881X_TEMP_ISENS_CTRL - WSA881X_ANALOG_BASE, 0x47},
+ {WSA881X_TEMP_CLK_CTRL - WSA881X_ANALOG_BASE, 0x87},
+ {WSA881X_TEMP_TEST - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_TEMP_BIAS - WSA881X_ANALOG_BASE, 0x51},
+ {WSA881X_TEMP_ADC_CTRL - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_TEMP_DOUT_MSB - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_TEMP_DOUT_LSB - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_ADC_EN_MODU_V - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_ADC_EN_MODU_I - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_ADC_EN_DET_TEST_V - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_ADC_EN_DET_TEST_I - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_ADC_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x25},
+ {WSA881X_ADC_EN_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x10},
+ {WSA881X_SPKR_DRV_EN - WSA881X_ANALOG_BASE, 0x74},
+ {WSA881X_SPKR_DRV_GAIN - WSA881X_ANALOG_BASE, 0x01},
+ {WSA881X_SPKR_DAC_CTL - WSA881X_ANALOG_BASE, 0x40},
+ {WSA881X_SPKR_DRV_DBG - WSA881X_ANALOG_BASE, 0x15},
+ {WSA881X_SPKR_PWRSTG_DBG - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_SPKR_OCP_CTL - WSA881X_ANALOG_BASE, 0xD4},
+ {WSA881X_SPKR_CLIP_CTL - WSA881X_ANALOG_BASE, 0x90},
+ {WSA881X_SPKR_BBM_CTL - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_SPKR_MISC_CTL1 - WSA881X_ANALOG_BASE, 0x80},
+ {WSA881X_SPKR_MISC_CTL2 - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_SPKR_BIAS_INT - WSA881X_ANALOG_BASE, 0x56},
+ {WSA881X_SPKR_PA_INT - WSA881X_ANALOG_BASE, 0x54},
+ {WSA881X_SPKR_BIAS_CAL - WSA881X_ANALOG_BASE, 0xAC},
+ {WSA881X_SPKR_BIAS_PSRR - WSA881X_ANALOG_BASE, 0x54},
+ {WSA881X_SPKR_STATUS1 - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_SPKR_STATUS2 - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_BOOST_EN_CTL - WSA881X_ANALOG_BASE, 0x18},
+ {WSA881X_BOOST_CURRENT_LIMIT - WSA881X_ANALOG_BASE, 0x7A},
+ {WSA881X_BOOST_PS_CTL - WSA881X_ANALOG_BASE, 0xC0},
+ {WSA881X_BOOST_PRESET_OUT1 - WSA881X_ANALOG_BASE, 0x77},
+ {WSA881X_BOOST_PRESET_OUT2 - WSA881X_ANALOG_BASE, 0x70},
+ {WSA881X_BOOST_FORCE_OUT - WSA881X_ANALOG_BASE, 0x0E},
+ {WSA881X_BOOST_LDO_PROG - WSA881X_ANALOG_BASE, 0x16},
+ {WSA881X_BOOST_SLOPE_COMP_ISENSE_FB - WSA881X_ANALOG_BASE, 0x71},
+ {WSA881X_BOOST_RON_CTL - WSA881X_ANALOG_BASE, 0x0F},
+ {WSA881X_BOOST_LOOP_STABILITY - WSA881X_ANALOG_BASE, 0xAD},
+ {WSA881X_BOOST_ZX_CTL - WSA881X_ANALOG_BASE, 0x34},
+ {WSA881X_BOOST_START_CTL - WSA881X_ANALOG_BASE, 0x23},
+ {WSA881X_BOOST_MISC1_CTL - WSA881X_ANALOG_BASE, 0x80},
+ {WSA881X_BOOST_MISC2_CTL - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_BOOST_MISC3_CTL - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_BOOST_ATEST_CTL - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_SPKR_PROT_FE_GAIN - WSA881X_ANALOG_BASE, 0x46},
+ {WSA881X_SPKR_PROT_FE_CM_LDO_SET - WSA881X_ANALOG_BASE, 0x3B},
+ {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 - WSA881X_ANALOG_BASE, 0x8D},
+ {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 - WSA881X_ANALOG_BASE, 0x8D},
+ {WSA881X_SPKR_PROT_ATEST1 - WSA881X_ANALOG_BASE, 0x01},
+ {WSA881X_SPKR_PROT_ATEST2 - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_SPKR_PROT_FE_VSENSE_VCM - WSA881X_ANALOG_BASE, 0x8D},
+ {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 - WSA881X_ANALOG_BASE, 0x4D},
+ {WSA881X_BONGO_RESRV_REG1 - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_BONGO_RESRV_REG2 - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_SPKR_PROT_SAR - WSA881X_ANALOG_BASE, 0x00},
+ {WSA881X_SPKR_STATUS3 - WSA881X_ANALOG_BASE, 0x00},
+};
+
+struct reg_default wsa881x_rev_2_0_dig[] = {
+ {WSA881X_RESET_CTL, 0x00},
+ {WSA881X_TADC_VALUE_CTL, 0x01},
+ {WSA881X_INTR_MASK, 0x1B},
+ {WSA881X_IOPAD_CTL, 0x00},
+ {WSA881X_OTP_REG_28, 0x3F},
+ {WSA881X_OTP_REG_29, 0x3F},
+ {WSA881X_OTP_REG_30, 0x01},
+ {WSA881X_OTP_REG_31, 0x01},
+};
+
+struct reg_default wsa881x_rev_2_0_ana[] = {
+ {WSA881X_TEMP_ADC_CTRL, 0x03},
+ {WSA881X_ADC_SEL_IBIAS, 0x45},
+ {WSA881X_SPKR_DRV_GAIN, 0xC1},
+ {WSA881X_SPKR_DAC_CTL, 0x42},
+ {WSA881X_SPKR_BBM_CTL, 0x02},
+ {WSA881X_SPKR_MISC_CTL1, 0x40},
+ {WSA881X_SPKR_MISC_CTL2, 0x07},
+ {WSA881X_SPKR_BIAS_INT, 0x5F},
+ {WSA881X_SPKR_BIAS_PSRR, 0x44},
+ {WSA881X_BOOST_PS_CTL, 0xA0},
+ {WSA881X_BOOST_PRESET_OUT1, 0xB7},
+ {WSA881X_BOOST_LOOP_STABILITY, 0x8D},
+ {WSA881X_SPKR_PROT_ATEST2, 0x02},
+ {WSA881X_BONGO_RESRV_REG1, 0x5E},
+ {WSA881X_BONGO_RESRV_REG2, 0x07},
+};
+
+struct reg_default wsa881x_rev_2_0_regmap_ana[] = {
+ {WSA881X_TEMP_ADC_CTRL - WSA881X_ANALOG_BASE, 0x03},
+ {WSA881X_ADC_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x45},
+ {WSA881X_SPKR_DRV_GAIN - WSA881X_ANALOG_BASE, 0xC1},
+ {WSA881X_SPKR_DAC_CTL - WSA881X_ANALOG_BASE, 0x42},
+ {WSA881X_SPKR_BBM_CTL - WSA881X_ANALOG_BASE, 0x02},
+ {WSA881X_SPKR_MISC_CTL1 - WSA881X_ANALOG_BASE, 0x40},
+ {WSA881X_SPKR_MISC_CTL2 - WSA881X_ANALOG_BASE, 0x07},
+ {WSA881X_SPKR_BIAS_INT - WSA881X_ANALOG_BASE, 0x5F},
+ {WSA881X_SPKR_BIAS_PSRR - WSA881X_ANALOG_BASE, 0x44},
+ {WSA881X_BOOST_PS_CTL - WSA881X_ANALOG_BASE, 0xA0},
+ {WSA881X_BOOST_PRESET_OUT1 - WSA881X_ANALOG_BASE, 0xB7},
+ {WSA881X_BOOST_LOOP_STABILITY - WSA881X_ANALOG_BASE, 0x8D},
+ {WSA881X_SPKR_PROT_ATEST2 - WSA881X_ANALOG_BASE, 0x02},
+ {WSA881X_BONGO_RESRV_REG1 - WSA881X_ANALOG_BASE, 0x5E},
+ {WSA881X_BONGO_RESRV_REG2 - WSA881X_ANALOG_BASE, 0x07},
+};
+
+/**
+ * wsa881x_update_reg_defaults_2_0 - update default values of regs for v2.0
+ *
+ * Bongo v2.0 has different default values for certain analog and digital
+ * registers compared to v1.x. Therefore, update the values of these registers
+ * with the values from tables defined above for v2.0.
+ */
+void wsa881x_update_reg_defaults_2_0(void)
+{
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(wsa881x_rev_2_0_dig); i++) {
+ for (j = 0; j < ARRAY_SIZE(wsa881x_ana_reg_defaults); j++)
+ if (wsa881x_ana_reg_defaults[j].reg ==
+ wsa881x_rev_2_0_dig[i].reg)
+ wsa881x_ana_reg_defaults[j].def =
+ wsa881x_rev_2_0_dig[i].def;
+ }
+ for (i = 0; i < ARRAY_SIZE(wsa881x_rev_2_0_ana); i++) {
+ for (j = 0; j < ARRAY_SIZE(wsa881x_ana_reg_defaults); j++)
+ if (wsa881x_ana_reg_defaults[j].reg ==
+ wsa881x_rev_2_0_ana[i].reg)
+ wsa881x_ana_reg_defaults[j].def =
+ wsa881x_rev_2_0_ana[i].def;
+ }
+}
+EXPORT_SYMBOL(wsa881x_update_reg_defaults_2_0);
+
+/**
+ * wsa881x_update_regmap_2_0 - update regmap framework with new tables
+ * @regmap: pointer to bongo regmap structure
+ * @flag: indicates digital or analog bongo slave
+ *
+ * Bongo v2.0 has some new registers for both analog and digital slaves.
+ * Update the regmap framework with all the new registers.
+ */
+void wsa881x_update_regmap_2_0(struct regmap *regmap, int flag)
+{
+ u16 ret = 0;
+
+ switch (flag) {
+ case WSA881X_DIGITAL_SLAVE:
+ ret = regmap_register_patch(regmap, wsa881x_rev_2_0_dig,
+ ARRAY_SIZE(wsa881x_rev_2_0_dig));
+ break;
+ case WSA881X_ANALOG_SLAVE:
+ ret = regmap_register_patch(regmap, wsa881x_rev_2_0_ana,
+ ARRAY_SIZE(wsa881x_rev_2_0_ana));
+ break;
+ default:
+ pr_debug("%s: unknown version", __func__);
+ ret = -EINVAL;
+ break;
+ }
+ if (ret)
+ pr_err("%s: Failed to update regmap defaults ret= %d\n",
+ __func__, ret);
+}
+EXPORT_SYMBOL(wsa881x_update_regmap_2_0);
+
+static bool wsa881x_readable_register(struct device *dev, unsigned int reg)
+{
+ return wsa881x_ana_reg_readable[reg];
+}
+
+static bool wsa881x_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WSA881X_CHIP_ID0:
+ case WSA881X_CHIP_ID1:
+ case WSA881X_CHIP_ID2:
+ case WSA881X_CHIP_ID3:
+ case WSA881X_BUS_ID:
+ case WSA881X_TEMP_MSB:
+ case WSA881X_TEMP_LSB:
+ case WSA881X_SDM_PDM9_LSB:
+ case WSA881X_SDM_PDM9_MSB:
+ case WSA881X_OTP_REG_0:
+ case WSA881X_OTP_REG_1:
+ case WSA881X_OTP_REG_2:
+ case WSA881X_OTP_REG_3:
+ case WSA881X_OTP_REG_4:
+ case WSA881X_OTP_REG_5:
+ case WSA881X_OTP_REG_31:
+ case WSA881X_TEMP_DOUT_MSB:
+ case WSA881X_TEMP_DOUT_LSB:
+ case WSA881X_TEMP_OP:
+ case WSA881X_OTP_CTRL1:
+ case WSA881X_INTR_STATUS:
+ case WSA881X_ATE_TEST_MODE:
+ case WSA881X_PIN_STATUS:
+ case WSA881X_SWR_HM_TEST2:
+ case WSA881X_SPKR_STATUS1:
+ case WSA881X_SPKR_STATUS2:
+ case WSA881X_SPKR_STATUS3:
+ case WSA881X_SPKR_PROT_SAR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+struct regmap_config wsa881x_ana_regmap_config[] = {
+{
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_NONE,
+ .reg_defaults = wsa881x_ana_reg_defaults_0,
+ .num_reg_defaults = ARRAY_SIZE(wsa881x_ana_reg_defaults_0),
+ .max_register = WSA881X_MAX_REGISTER,
+ .volatile_reg = wsa881x_volatile_register,
+ .readable_reg = wsa881x_readable_register,
+ .reg_format_endian = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+},
+{
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_NONE,
+ .reg_defaults = wsa881x_ana_reg_defaults_1,
+ .num_reg_defaults = ARRAY_SIZE(wsa881x_ana_reg_defaults_1),
+ .max_register = WSA881X_MAX_REGISTER,
+ .volatile_reg = wsa881x_volatile_register,
+ .readable_reg = wsa881x_readable_register,
+ .reg_format_endian = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+}
+};
diff --git a/sound/soc/codecs/wsa881x-regmap.c b/sound/soc/codecs/wsa881x-regmap.c
new file mode 100644
index 000000000000..20dc3508a5af
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-regmap.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "wsa881x-registers.h"
+#include "wsa881x.h"
+
+/*
+ * Default register reset values that are common across different versions
+ * are defined here. If a register reset value is changed based on version
+ * then remove it from this structure and add it in version specific
+ * structures.
+ */
+static struct reg_default wsa881x_defaults[] = {
+ {WSA881X_CHIP_ID0, 0x00},
+ {WSA881X_CHIP_ID1, 0x00},
+ {WSA881X_CHIP_ID2, 0x00},
+ {WSA881X_CHIP_ID3, 0x02},
+ {WSA881X_BUS_ID, 0x00},
+ {WSA881X_CDC_RST_CTL, 0x00},
+ {WSA881X_CDC_TOP_CLK_CTL, 0x03},
+ {WSA881X_CDC_ANA_CLK_CTL, 0x00},
+ {WSA881X_CDC_DIG_CLK_CTL, 0x00},
+ {WSA881X_CLOCK_CONFIG, 0x00},
+ {WSA881X_ANA_CTL, 0x08},
+ {WSA881X_SWR_RESET_EN, 0x00},
+ {WSA881X_TEMP_DETECT_CTL, 0x01},
+ {WSA881X_TEMP_MSB, 0x00},
+ {WSA881X_TEMP_LSB, 0x00},
+ {WSA881X_TEMP_CONFIG0, 0x00},
+ {WSA881X_TEMP_CONFIG1, 0x00},
+ {WSA881X_CDC_CLIP_CTL, 0x03},
+ {WSA881X_SDM_PDM9_LSB, 0x00},
+ {WSA881X_SDM_PDM9_MSB, 0x00},
+ {WSA881X_CDC_RX_CTL, 0x7E},
+ {WSA881X_DEM_BYPASS_DATA0, 0x00},
+ {WSA881X_DEM_BYPASS_DATA1, 0x00},
+ {WSA881X_DEM_BYPASS_DATA2, 0x00},
+ {WSA881X_DEM_BYPASS_DATA3, 0x00},
+ {WSA881X_OTP_CTRL0, 0x00},
+ {WSA881X_OTP_CTRL1, 0x00},
+ {WSA881X_HDRIVE_CTL_GROUP1, 0x00},
+ {WSA881X_INTR_MODE, 0x00},
+ {WSA881X_INTR_STATUS, 0x00},
+ {WSA881X_INTR_CLEAR, 0x00},
+ {WSA881X_INTR_LEVEL, 0x00},
+ {WSA881X_INTR_SET, 0x00},
+ {WSA881X_INTR_TEST, 0x00},
+ {WSA881X_PDM_TEST_MODE, 0x00},
+ {WSA881X_ATE_TEST_MODE, 0x00},
+ {WSA881X_PIN_CTL_MODE, 0x00},
+ {WSA881X_PIN_CTL_OE, 0x00},
+ {WSA881X_PIN_WDATA_IOPAD, 0x00},
+ {WSA881X_PIN_STATUS, 0x00},
+ {WSA881X_DIG_DEBUG_MODE, 0x00},
+ {WSA881X_DIG_DEBUG_SEL, 0x00},
+ {WSA881X_DIG_DEBUG_EN, 0x00},
+ {WSA881X_SWR_HM_TEST1, 0x08},
+ {WSA881X_SWR_HM_TEST2, 0x00},
+ {WSA881X_TEMP_DETECT_DBG_CTL, 0x00},
+ {WSA881X_TEMP_DEBUG_MSB, 0x00},
+ {WSA881X_TEMP_DEBUG_LSB, 0x00},
+ {WSA881X_SAMPLE_EDGE_SEL, 0x0C},
+ {WSA881X_SPARE_0, 0x00},
+ {WSA881X_SPARE_1, 0x00},
+ {WSA881X_SPARE_2, 0x00},
+ {WSA881X_OTP_REG_0, 0x01},
+ {WSA881X_OTP_REG_1, 0xFF},
+ {WSA881X_OTP_REG_2, 0xC0},
+ {WSA881X_OTP_REG_3, 0xFF},
+ {WSA881X_OTP_REG_4, 0xC0},
+ {WSA881X_OTP_REG_5, 0xFF},
+ {WSA881X_OTP_REG_6, 0xFF},
+ {WSA881X_OTP_REG_7, 0xFF},
+ {WSA881X_OTP_REG_8, 0xFF},
+ {WSA881X_OTP_REG_9, 0xFF},
+ {WSA881X_OTP_REG_10, 0xFF},
+ {WSA881X_OTP_REG_11, 0xFF},
+ {WSA881X_OTP_REG_12, 0xFF},
+ {WSA881X_OTP_REG_13, 0xFF},
+ {WSA881X_OTP_REG_14, 0xFF},
+ {WSA881X_OTP_REG_15, 0xFF},
+ {WSA881X_OTP_REG_16, 0xFF},
+ {WSA881X_OTP_REG_17, 0xFF},
+ {WSA881X_OTP_REG_18, 0xFF},
+ {WSA881X_OTP_REG_19, 0xFF},
+ {WSA881X_OTP_REG_20, 0xFF},
+ {WSA881X_OTP_REG_21, 0xFF},
+ {WSA881X_OTP_REG_22, 0xFF},
+ {WSA881X_OTP_REG_23, 0xFF},
+ {WSA881X_OTP_REG_24, 0x03},
+ {WSA881X_OTP_REG_25, 0x01},
+ {WSA881X_OTP_REG_26, 0x03},
+ {WSA881X_OTP_REG_27, 0x11},
+ {WSA881X_OTP_REG_63, 0x40},
+ /* WSA881x Analog registers */
+ {WSA881X_BIAS_REF_CTRL, 0x6C},
+ {WSA881X_BIAS_TEST, 0x16},
+ {WSA881X_BIAS_BIAS, 0xF0},
+ {WSA881X_TEMP_OP, 0x00},
+ {WSA881X_TEMP_IREF_CTRL, 0x56},
+ {WSA881X_TEMP_ISENS_CTRL, 0x47},
+ {WSA881X_TEMP_CLK_CTRL, 0x87},
+ {WSA881X_TEMP_TEST, 0x00},
+ {WSA881X_TEMP_BIAS, 0x51},
+ {WSA881X_TEMP_DOUT_MSB, 0x00},
+ {WSA881X_TEMP_DOUT_LSB, 0x00},
+ {WSA881X_ADC_EN_MODU_V, 0x00},
+ {WSA881X_ADC_EN_MODU_I, 0x00},
+ {WSA881X_ADC_EN_DET_TEST_V, 0x00},
+ {WSA881X_ADC_EN_DET_TEST_I, 0x00},
+ {WSA881X_ADC_EN_SEL_IBAIS, 0x10},
+ {WSA881X_SPKR_DRV_EN, 0x74},
+ {WSA881X_SPKR_DRV_DBG, 0x15},
+ {WSA881X_SPKR_PWRSTG_DBG, 0x00},
+ {WSA881X_SPKR_OCP_CTL, 0xD4},
+ {WSA881X_SPKR_CLIP_CTL, 0x90},
+ {WSA881X_SPKR_PA_INT, 0x54},
+ {WSA881X_SPKR_BIAS_CAL, 0xAC},
+ {WSA881X_SPKR_STATUS1, 0x00},
+ {WSA881X_SPKR_STATUS2, 0x00},
+ {WSA881X_BOOST_EN_CTL, 0x18},
+ {WSA881X_BOOST_CURRENT_LIMIT, 0x7A},
+ {WSA881X_BOOST_PRESET_OUT2, 0x70},
+ {WSA881X_BOOST_FORCE_OUT, 0x0E},
+ {WSA881X_BOOST_LDO_PROG, 0x16},
+ {WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71},
+ {WSA881X_BOOST_RON_CTL, 0x0F},
+ {WSA881X_BOOST_ZX_CTL, 0x34},
+ {WSA881X_BOOST_START_CTL, 0x23},
+ {WSA881X_BOOST_MISC1_CTL, 0x80},
+ {WSA881X_BOOST_MISC2_CTL, 0x00},
+ {WSA881X_BOOST_MISC3_CTL, 0x00},
+ {WSA881X_BOOST_ATEST_CTL, 0x00},
+ {WSA881X_SPKR_PROT_FE_GAIN, 0x46},
+ {WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B},
+ {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D},
+ {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D},
+ {WSA881X_SPKR_PROT_ATEST1, 0x01},
+ {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D},
+ {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D},
+ {WSA881X_SPKR_PROT_SAR, 0x00},
+ {WSA881X_SPKR_STATUS3, 0x00},
+};
+
+/* Default register reset values for WSA881x rev 2.0 */
+static struct reg_sequence wsa881x_rev_2_0[] = {
+ {WSA881X_RESET_CTL, 0x00, 0x00},
+ {WSA881X_TADC_VALUE_CTL, 0x01, 0x00},
+ {WSA881X_INTR_MASK, 0x1B, 0x00},
+ {WSA881X_IOPAD_CTL, 0x00, 0x00},
+ {WSA881X_OTP_REG_28, 0x3F, 0x00},
+ {WSA881X_OTP_REG_29, 0x3F, 0x00},
+ {WSA881X_OTP_REG_30, 0x01, 0x00},
+ {WSA881X_OTP_REG_31, 0x01, 0x00},
+ {WSA881X_TEMP_ADC_CTRL, 0x03, 0x00},
+ {WSA881X_ADC_SEL_IBIAS, 0x45, 0x00},
+ {WSA881X_SPKR_DRV_GAIN, 0xC1, 0x00},
+ {WSA881X_SPKR_DAC_CTL, 0x42, 0x00},
+ {WSA881X_SPKR_BBM_CTL, 0x02, 0x00},
+ {WSA881X_SPKR_MISC_CTL1, 0x40, 0x00},
+ {WSA881X_SPKR_MISC_CTL2, 0x07, 0x00},
+ {WSA881X_SPKR_BIAS_INT, 0x5F, 0x00},
+ {WSA881X_SPKR_BIAS_PSRR, 0x44, 0x00},
+ {WSA881X_BOOST_PS_CTL, 0xA0, 0x00},
+ {WSA881X_BOOST_PRESET_OUT1, 0xB7, 0x00},
+ {WSA881X_BOOST_LOOP_STABILITY, 0x8D, 0x00},
+ {WSA881X_SPKR_PROT_ATEST2, 0x02, 0x00},
+ {WSA881X_BONGO_RESRV_REG1, 0x5E, 0x00},
+ {WSA881X_BONGO_RESRV_REG2, 0x07, 0x00},
+};
+
+/*
+ * wsa881x_regmap_defaults - update regmap default register values
+ * @regmap: pointer to regmap structure
+ * @version: wsa881x version id
+ *
+ * Update regmap default register values based on version id
+ *
+ */
+void wsa881x_regmap_defaults(struct regmap *regmap, u8 version)
+{
+ u16 ret = 0;
+
+ if (!regmap) {
+ pr_debug("%s: regmap structure is NULL\n", __func__);
+ return;
+ }
+
+ regcache_cache_only(regmap, true);
+ ret = regmap_multi_reg_write(regmap, wsa881x_rev_2_0,
+ ARRAY_SIZE(wsa881x_rev_2_0));
+ regcache_cache_only(regmap, false);
+
+ if (ret)
+ pr_debug("%s: Failed to update regmap defaults ret= %d\n",
+ __func__, ret);
+}
+EXPORT_SYMBOL(wsa881x_regmap_defaults);
+
+static bool wsa881x_readable_register(struct device *dev, unsigned int reg)
+{
+ return wsa881x_reg_readable[reg];
+}
+
+static bool wsa881x_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WSA881X_CHIP_ID0:
+ case WSA881X_CHIP_ID1:
+ case WSA881X_CHIP_ID2:
+ case WSA881X_CHIP_ID3:
+ case WSA881X_BUS_ID:
+ case WSA881X_TEMP_MSB:
+ case WSA881X_TEMP_LSB:
+ case WSA881X_SDM_PDM9_LSB:
+ case WSA881X_SDM_PDM9_MSB:
+ case WSA881X_OTP_CTRL1:
+ case WSA881X_INTR_STATUS:
+ case WSA881X_ATE_TEST_MODE:
+ case WSA881X_PIN_STATUS:
+ case WSA881X_SWR_HM_TEST2:
+ case WSA881X_SPKR_STATUS1:
+ case WSA881X_SPKR_STATUS2:
+ case WSA881X_SPKR_STATUS3:
+ case WSA881X_OTP_REG_0:
+ case WSA881X_OTP_REG_1:
+ case WSA881X_OTP_REG_2:
+ case WSA881X_OTP_REG_3:
+ case WSA881X_OTP_REG_4:
+ case WSA881X_OTP_REG_5:
+ case WSA881X_OTP_REG_31:
+ case WSA881X_TEMP_DOUT_MSB:
+ case WSA881X_TEMP_DOUT_LSB:
+ case WSA881X_TEMP_OP:
+ case WSA881X_SPKR_PROT_SAR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+struct regmap_config wsa881x_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wsa881x_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wsa881x_defaults),
+ .max_register = WSA881X_MAX_REGISTER,
+ .volatile_reg = wsa881x_volatile_register,
+ .readable_reg = wsa881x_readable_register,
+ .reg_format_endian = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+ .can_multi_write = true,
+};
diff --git a/sound/soc/codecs/wsa881x-tables-analog.c b/sound/soc/codecs/wsa881x-tables-analog.c
new file mode 100644
index 000000000000..061ed6fff798
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-tables-analog.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "wsa881x-registers-analog.h"
+
+const u8 wsa881x_ana_reg_readable[WSA881X_CACHE_SIZE] = {
+ [WSA881X_CHIP_ID0] = 1,
+ [WSA881X_CHIP_ID1] = 1,
+ [WSA881X_CHIP_ID2] = 1,
+ [WSA881X_CHIP_ID3] = 1,
+ [WSA881X_BUS_ID] = 1,
+ [WSA881X_CDC_RST_CTL] = 1,
+ [WSA881X_CDC_TOP_CLK_CTL] = 1,
+ [WSA881X_CDC_ANA_CLK_CTL] = 1,
+ [WSA881X_CDC_DIG_CLK_CTL] = 1,
+ [WSA881X_CLOCK_CONFIG] = 1,
+ [WSA881X_ANA_CTL] = 1,
+ [WSA881X_SWR_RESET_EN] = 1,
+ [WSA881X_RESET_CTL] = 1,
+ [WSA881X_TADC_VALUE_CTL] = 1,
+ [WSA881X_TEMP_DETECT_CTL] = 1,
+ [WSA881X_TEMP_MSB] = 1,
+ [WSA881X_TEMP_LSB] = 1,
+ [WSA881X_TEMP_CONFIG0] = 1,
+ [WSA881X_TEMP_CONFIG1] = 1,
+ [WSA881X_CDC_CLIP_CTL] = 1,
+ [WSA881X_SDM_PDM9_LSB] = 1,
+ [WSA881X_SDM_PDM9_MSB] = 1,
+ [WSA881X_CDC_RX_CTL] = 1,
+ [WSA881X_DEM_BYPASS_DATA0] = 1,
+ [WSA881X_DEM_BYPASS_DATA1] = 1,
+ [WSA881X_DEM_BYPASS_DATA2] = 1,
+ [WSA881X_DEM_BYPASS_DATA3] = 1,
+ [WSA881X_OTP_CTRL0] = 1,
+ [WSA881X_OTP_CTRL1] = 1,
+ [WSA881X_HDRIVE_CTL_GROUP1] = 1,
+ [WSA881X_INTR_MODE] = 1,
+ [WSA881X_INTR_MASK] = 1,
+ [WSA881X_INTR_STATUS] = 1,
+ [WSA881X_INTR_CLEAR] = 1,
+ [WSA881X_INTR_LEVEL] = 1,
+ [WSA881X_INTR_SET] = 1,
+ [WSA881X_INTR_TEST] = 1,
+ [WSA881X_PDM_TEST_MODE] = 1,
+ [WSA881X_ATE_TEST_MODE] = 1,
+ [WSA881X_PIN_CTL_MODE] = 1,
+ [WSA881X_PIN_CTL_OE] = 1,
+ [WSA881X_PIN_WDATA_IOPAD] = 1,
+ [WSA881X_PIN_STATUS] = 1,
+ [WSA881X_DIG_DEBUG_MODE] = 1,
+ [WSA881X_DIG_DEBUG_SEL] = 1,
+ [WSA881X_DIG_DEBUG_EN] = 1,
+ [WSA881X_SWR_HM_TEST1] = 1,
+ [WSA881X_SWR_HM_TEST2] = 1,
+ [WSA881X_TEMP_DETECT_DBG_CTL] = 1,
+ [WSA881X_TEMP_DEBUG_MSB] = 1,
+ [WSA881X_TEMP_DEBUG_LSB] = 1,
+ [WSA881X_SAMPLE_EDGE_SEL] = 1,
+ [WSA881X_IOPAD_CTL] = 1,
+ [WSA881X_SPARE_0] = 1,
+ [WSA881X_SPARE_1] = 1,
+ [WSA881X_SPARE_2] = 1,
+ [WSA881X_OTP_REG_0] = 1,
+ [WSA881X_OTP_REG_1] = 1,
+ [WSA881X_OTP_REG_2] = 1,
+ [WSA881X_OTP_REG_3] = 1,
+ [WSA881X_OTP_REG_4] = 1,
+ [WSA881X_OTP_REG_5] = 1,
+ [WSA881X_OTP_REG_6] = 1,
+ [WSA881X_OTP_REG_7] = 1,
+ [WSA881X_OTP_REG_8] = 1,
+ [WSA881X_OTP_REG_9] = 1,
+ [WSA881X_OTP_REG_10] = 1,
+ [WSA881X_OTP_REG_11] = 1,
+ [WSA881X_OTP_REG_12] = 1,
+ [WSA881X_OTP_REG_13] = 1,
+ [WSA881X_OTP_REG_14] = 1,
+ [WSA881X_OTP_REG_15] = 1,
+ [WSA881X_OTP_REG_16] = 1,
+ [WSA881X_OTP_REG_17] = 1,
+ [WSA881X_OTP_REG_18] = 1,
+ [WSA881X_OTP_REG_19] = 1,
+ [WSA881X_OTP_REG_20] = 1,
+ [WSA881X_OTP_REG_21] = 1,
+ [WSA881X_OTP_REG_22] = 1,
+ [WSA881X_OTP_REG_23] = 1,
+ [WSA881X_OTP_REG_24] = 1,
+ [WSA881X_OTP_REG_25] = 1,
+ [WSA881X_OTP_REG_26] = 1,
+ [WSA881X_OTP_REG_27] = 1,
+ [WSA881X_OTP_REG_28] = 1,
+ [WSA881X_OTP_REG_29] = 1,
+ [WSA881X_OTP_REG_30] = 1,
+ [WSA881X_OTP_REG_31] = 1,
+ [WSA881X_OTP_REG_63] = 1,
+ /* Analog Registers */
+ [WSA881X_BIAS_REF_CTRL] = 1,
+ [WSA881X_BIAS_TEST] = 1,
+ [WSA881X_BIAS_BIAS] = 1,
+ [WSA881X_TEMP_OP] = 1,
+ [WSA881X_TEMP_IREF_CTRL] = 1,
+ [WSA881X_TEMP_ISENS_CTRL] = 1,
+ [WSA881X_TEMP_CLK_CTRL] = 1,
+ [WSA881X_TEMP_TEST] = 1,
+ [WSA881X_TEMP_BIAS] = 1,
+ [WSA881X_TEMP_ADC_CTRL] = 1,
+ [WSA881X_TEMP_DOUT_MSB] = 1,
+ [WSA881X_TEMP_DOUT_LSB] = 1,
+ [WSA881X_ADC_EN_MODU_V] = 1,
+ [WSA881X_ADC_EN_MODU_I] = 1,
+ [WSA881X_ADC_EN_DET_TEST_V] = 1,
+ [WSA881X_ADC_EN_DET_TEST_I] = 1,
+ [WSA881X_ADC_SEL_IBIAS] = 1,
+ [WSA881X_ADC_EN_SEL_IBIAS] = 1,
+ [WSA881X_SPKR_DRV_EN] = 1,
+ [WSA881X_SPKR_DRV_GAIN] = 1,
+ [WSA881X_SPKR_DAC_CTL] = 1,
+ [WSA881X_SPKR_DRV_DBG] = 1,
+ [WSA881X_SPKR_PWRSTG_DBG] = 1,
+ [WSA881X_SPKR_OCP_CTL] = 1,
+ [WSA881X_SPKR_CLIP_CTL] = 1,
+ [WSA881X_SPKR_BBM_CTL] = 1,
+ [WSA881X_SPKR_MISC_CTL1] = 1,
+ [WSA881X_SPKR_MISC_CTL2] = 1,
+ [WSA881X_SPKR_BIAS_INT] = 1,
+ [WSA881X_SPKR_PA_INT] = 1,
+ [WSA881X_SPKR_BIAS_CAL] = 1,
+ [WSA881X_SPKR_BIAS_PSRR] = 1,
+ [WSA881X_SPKR_STATUS1] = 1,
+ [WSA881X_SPKR_STATUS2] = 1,
+ [WSA881X_BOOST_EN_CTL] = 1,
+ [WSA881X_BOOST_CURRENT_LIMIT] = 1,
+ [WSA881X_BOOST_PS_CTL] = 1,
+ [WSA881X_BOOST_PRESET_OUT1] = 1,
+ [WSA881X_BOOST_PRESET_OUT2] = 1,
+ [WSA881X_BOOST_FORCE_OUT] = 1,
+ [WSA881X_BOOST_LDO_PROG] = 1,
+ [WSA881X_BOOST_SLOPE_COMP_ISENSE_FB] = 1,
+ [WSA881X_BOOST_RON_CTL] = 1,
+ [WSA881X_BOOST_LOOP_STABILITY] = 1,
+ [WSA881X_BOOST_ZX_CTL] = 1,
+ [WSA881X_BOOST_START_CTL] = 1,
+ [WSA881X_BOOST_MISC1_CTL] = 1,
+ [WSA881X_BOOST_MISC2_CTL] = 1,
+ [WSA881X_BOOST_MISC3_CTL] = 1,
+ [WSA881X_BOOST_ATEST_CTL] = 1,
+ [WSA881X_SPKR_PROT_FE_GAIN] = 1,
+ [WSA881X_SPKR_PROT_FE_CM_LDO_SET] = 1,
+ [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1] = 1,
+ [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2] = 1,
+ [WSA881X_SPKR_PROT_ATEST1] = 1,
+ [WSA881X_SPKR_PROT_ATEST2] = 1,
+ [WSA881X_SPKR_PROT_FE_VSENSE_VCM] = 1,
+ [WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1] = 1,
+ [WSA881X_BONGO_RESRV_REG1] = 1,
+ [WSA881X_BONGO_RESRV_REG2] = 1,
+ [WSA881X_SPKR_PROT_SAR] = 1,
+ [WSA881X_SPKR_STATUS3] = 1,
+};
diff --git a/sound/soc/codecs/wsa881x-tables.c b/sound/soc/codecs/wsa881x-tables.c
new file mode 100644
index 000000000000..4f1212be9c5b
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-tables.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "wsa881x-registers.h"
+
+const u8 wsa881x_reg_readable[WSA881X_CACHE_SIZE] = {
+ [WSA881X_CHIP_ID0] = 1,
+ [WSA881X_CHIP_ID1] = 1,
+ [WSA881X_CHIP_ID2] = 1,
+ [WSA881X_CHIP_ID3] = 1,
+ [WSA881X_BUS_ID] = 1,
+ [WSA881X_CDC_RST_CTL] = 1,
+ [WSA881X_CDC_TOP_CLK_CTL] = 1,
+ [WSA881X_CDC_ANA_CLK_CTL] = 1,
+ [WSA881X_CDC_DIG_CLK_CTL] = 1,
+ [WSA881X_CLOCK_CONFIG] = 1,
+ [WSA881X_ANA_CTL] = 1,
+ [WSA881X_SWR_RESET_EN] = 1,
+ [WSA881X_RESET_CTL] = 1,
+ [WSA881X_TADC_VALUE_CTL] = 1,
+ [WSA881X_TEMP_DETECT_CTL] = 1,
+ [WSA881X_TEMP_MSB] = 1,
+ [WSA881X_TEMP_LSB] = 1,
+ [WSA881X_TEMP_CONFIG0] = 1,
+ [WSA881X_TEMP_CONFIG1] = 1,
+ [WSA881X_CDC_CLIP_CTL] = 1,
+ [WSA881X_SDM_PDM9_LSB] = 1,
+ [WSA881X_SDM_PDM9_MSB] = 1,
+ [WSA881X_CDC_RX_CTL] = 1,
+ [WSA881X_DEM_BYPASS_DATA0] = 1,
+ [WSA881X_DEM_BYPASS_DATA1] = 1,
+ [WSA881X_DEM_BYPASS_DATA2] = 1,
+ [WSA881X_DEM_BYPASS_DATA3] = 1,
+ [WSA881X_OTP_CTRL0] = 1,
+ [WSA881X_OTP_CTRL1] = 1,
+ [WSA881X_HDRIVE_CTL_GROUP1] = 1,
+ [WSA881X_INTR_MODE] = 1,
+ [WSA881X_INTR_MASK] = 1,
+ [WSA881X_INTR_STATUS] = 1,
+ [WSA881X_INTR_CLEAR] = 1,
+ [WSA881X_INTR_LEVEL] = 1,
+ [WSA881X_INTR_SET] = 1,
+ [WSA881X_INTR_TEST] = 1,
+ [WSA881X_PDM_TEST_MODE] = 1,
+ [WSA881X_ATE_TEST_MODE] = 1,
+ [WSA881X_PIN_CTL_MODE] = 1,
+ [WSA881X_PIN_CTL_OE] = 1,
+ [WSA881X_PIN_WDATA_IOPAD] = 1,
+ [WSA881X_PIN_STATUS] = 1,
+ [WSA881X_DIG_DEBUG_MODE] = 1,
+ [WSA881X_DIG_DEBUG_SEL] = 1,
+ [WSA881X_DIG_DEBUG_EN] = 1,
+ [WSA881X_SWR_HM_TEST1] = 1,
+ [WSA881X_SWR_HM_TEST2] = 1,
+ [WSA881X_TEMP_DETECT_DBG_CTL] = 1,
+ [WSA881X_TEMP_DEBUG_MSB] = 1,
+ [WSA881X_TEMP_DEBUG_LSB] = 1,
+ [WSA881X_SAMPLE_EDGE_SEL] = 1,
+ [WSA881X_IOPAD_CTL] = 1,
+ [WSA881X_SPARE_0] = 1,
+ [WSA881X_SPARE_1] = 1,
+ [WSA881X_SPARE_2] = 1,
+ [WSA881X_OTP_REG_0] = 1,
+ [WSA881X_OTP_REG_1] = 1,
+ [WSA881X_OTP_REG_2] = 1,
+ [WSA881X_OTP_REG_3] = 1,
+ [WSA881X_OTP_REG_4] = 1,
+ [WSA881X_OTP_REG_5] = 1,
+ [WSA881X_OTP_REG_6] = 1,
+ [WSA881X_OTP_REG_7] = 1,
+ [WSA881X_OTP_REG_8] = 1,
+ [WSA881X_OTP_REG_9] = 1,
+ [WSA881X_OTP_REG_10] = 1,
+ [WSA881X_OTP_REG_11] = 1,
+ [WSA881X_OTP_REG_12] = 1,
+ [WSA881X_OTP_REG_13] = 1,
+ [WSA881X_OTP_REG_14] = 1,
+ [WSA881X_OTP_REG_15] = 1,
+ [WSA881X_OTP_REG_16] = 1,
+ [WSA881X_OTP_REG_17] = 1,
+ [WSA881X_OTP_REG_18] = 1,
+ [WSA881X_OTP_REG_19] = 1,
+ [WSA881X_OTP_REG_20] = 1,
+ [WSA881X_OTP_REG_21] = 1,
+ [WSA881X_OTP_REG_22] = 1,
+ [WSA881X_OTP_REG_23] = 1,
+ [WSA881X_OTP_REG_24] = 1,
+ [WSA881X_OTP_REG_25] = 1,
+ [WSA881X_OTP_REG_26] = 1,
+ [WSA881X_OTP_REG_27] = 1,
+ [WSA881X_OTP_REG_28] = 1,
+ [WSA881X_OTP_REG_29] = 1,
+ [WSA881X_OTP_REG_30] = 1,
+ [WSA881X_OTP_REG_31] = 1,
+ [WSA881X_OTP_REG_63] = 1,
+ /* Analog Registers */
+ [WSA881X_BIAS_REF_CTRL] = 1,
+ [WSA881X_BIAS_TEST] = 1,
+ [WSA881X_BIAS_BIAS] = 1,
+ [WSA881X_TEMP_OP] = 1,
+ [WSA881X_TEMP_IREF_CTRL] = 1,
+ [WSA881X_TEMP_ISENS_CTRL] = 1,
+ [WSA881X_TEMP_CLK_CTRL] = 1,
+ [WSA881X_TEMP_TEST] = 1,
+ [WSA881X_TEMP_BIAS] = 1,
+ [WSA881X_TEMP_ADC_CTRL] = 1,
+ [WSA881X_TEMP_DOUT_MSB] = 1,
+ [WSA881X_TEMP_DOUT_LSB] = 1,
+ [WSA881X_ADC_EN_MODU_V] = 1,
+ [WSA881X_ADC_EN_MODU_I] = 1,
+ [WSA881X_ADC_EN_DET_TEST_V] = 1,
+ [WSA881X_ADC_EN_DET_TEST_I] = 1,
+ [WSA881X_ADC_SEL_IBIAS] = 1,
+ [WSA881X_ADC_EN_SEL_IBAIS] = 1,
+ [WSA881X_SPKR_DRV_EN] = 1,
+ [WSA881X_SPKR_DRV_GAIN] = 1,
+ [WSA881X_SPKR_DAC_CTL] = 1,
+ [WSA881X_SPKR_DRV_DBG] = 1,
+ [WSA881X_SPKR_PWRSTG_DBG] = 1,
+ [WSA881X_SPKR_OCP_CTL] = 1,
+ [WSA881X_SPKR_CLIP_CTL] = 1,
+ [WSA881X_SPKR_BBM_CTL] = 1,
+ [WSA881X_SPKR_MISC_CTL1] = 1,
+ [WSA881X_SPKR_MISC_CTL2] = 1,
+ [WSA881X_SPKR_BIAS_INT] = 1,
+ [WSA881X_SPKR_PA_INT] = 1,
+ [WSA881X_SPKR_BIAS_CAL] = 1,
+ [WSA881X_SPKR_BIAS_PSRR] = 1,
+ [WSA881X_SPKR_STATUS1] = 1,
+ [WSA881X_SPKR_STATUS2] = 1,
+ [WSA881X_BOOST_EN_CTL] = 1,
+ [WSA881X_BOOST_CURRENT_LIMIT] = 1,
+ [WSA881X_BOOST_PS_CTL] = 1,
+ [WSA881X_BOOST_PRESET_OUT1] = 1,
+ [WSA881X_BOOST_PRESET_OUT2] = 1,
+ [WSA881X_BOOST_FORCE_OUT] = 1,
+ [WSA881X_BOOST_LDO_PROG] = 1,
+ [WSA881X_BOOST_SLOPE_COMP_ISENSE_FB] = 1,
+ [WSA881X_BOOST_RON_CTL] = 1,
+ [WSA881X_BOOST_LOOP_STABILITY] = 1,
+ [WSA881X_BOOST_ZX_CTL] = 1,
+ [WSA881X_BOOST_START_CTL] = 1,
+ [WSA881X_BOOST_MISC1_CTL] = 1,
+ [WSA881X_BOOST_MISC2_CTL] = 1,
+ [WSA881X_BOOST_MISC3_CTL] = 1,
+ [WSA881X_BOOST_ATEST_CTL] = 1,
+ [WSA881X_SPKR_PROT_FE_GAIN] = 1,
+ [WSA881X_SPKR_PROT_FE_CM_LDO_SET] = 1,
+ [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1] = 1,
+ [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2] = 1,
+ [WSA881X_SPKR_PROT_ATEST1] = 1,
+ [WSA881X_SPKR_PROT_ATEST2] = 1,
+ [WSA881X_SPKR_PROT_FE_VSENSE_VCM] = 1,
+ [WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1] = 1,
+ [WSA881X_BONGO_RESRV_REG1] = 1,
+ [WSA881X_BONGO_RESRV_REG2] = 1,
+ [WSA881X_SPKR_PROT_SAR] = 1,
+ [WSA881X_SPKR_STATUS3] = 1,
+};
diff --git a/sound/soc/codecs/wsa881x-temp-sensor.c b/sound/soc/codecs/wsa881x-temp-sensor.c
new file mode 100644
index 000000000000..5ab0ecfdc022
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-temp-sensor.c
@@ -0,0 +1,149 @@
+/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/thermal.h>
+#include <sound/soc.h>
+#include "wsa881x-temp-sensor.h"
+
+#define T1_TEMP -10
+#define T2_TEMP 150
+#define LOW_TEMP_THRESHOLD 5
+#define HIGH_TEMP_THRESHOLD 45
+#define TEMP_INVALID 0xFFFF
+#define WSA881X_TEMP_RETRY 3
+/*
+ * wsa881x_get_temp - get wsa temperature
+ * @thermal: thermal zone device
+ * @temp: temperature value
+ *
+ * Get the temperature of wsa881x.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int wsa881x_get_temp(struct thermal_zone_device *thermal,
+ int *temp)
+{
+ struct wsa881x_tz_priv *pdata;
+ struct snd_soc_codec *codec;
+ struct wsa_temp_register reg;
+ int dmeas, d1, d2;
+ int ret = 0;
+ int temp_val;
+ int t1 = T1_TEMP;
+ int t2 = T2_TEMP;
+ u8 retry = WSA881X_TEMP_RETRY;
+
+ if (!thermal)
+ return -EINVAL;
+
+ if (thermal->devdata) {
+ pdata = thermal->devdata;
+ if (pdata->codec) {
+ codec = pdata->codec;
+ } else {
+ pr_err("%s: codec is NULL\n", __func__);
+ return -EINVAL;
+ }
+ } else {
+ pr_err("%s: pdata is NULL\n", __func__);
+ return -EINVAL;
+ }
+temp_retry:
+ if (pdata->wsa_temp_reg_read) {
+ ret = pdata->wsa_temp_reg_read(codec, &reg);
+ if (ret) {
+ pr_err("%s: temperature register read failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ } else {
+ pr_err("%s: wsa_temp_reg_read is NULL\n", __func__);
+ return -EINVAL;
+ }
+ /*
+ * Temperature register values are expected to be in the
+ * following range.
+ * d1_msb = 68 - 92 and d1_lsb = 0, 64, 128, 192
+ * d2_msb = 185 -218 and d2_lsb = 0, 64, 128, 192
+ */
+ if ((reg.d1_msb < 68 || reg.d1_msb > 92) ||
+ (!(reg.d1_lsb == 0 || reg.d1_lsb == 64 || reg.d1_lsb == 128 ||
+ reg.d1_lsb == 192)) ||
+ (reg.d2_msb < 185 || reg.d2_msb > 218) ||
+ (!(reg.d2_lsb == 0 || reg.d2_lsb == 64 || reg.d2_lsb == 128 ||
+ reg.d2_lsb == 192))) {
+ printk_ratelimited("%s: Temperature registers[%d %d %d %d] are out of range\n",
+ __func__, reg.d1_msb, reg.d1_lsb, reg.d2_msb,
+ reg.d2_lsb);
+ }
+ dmeas = ((reg.dmeas_msb << 0x8) | reg.dmeas_lsb) >> 0x6;
+ d1 = ((reg.d1_msb << 0x8) | reg.d1_lsb) >> 0x6;
+ d2 = ((reg.d2_msb << 0x8) | reg.d2_lsb) >> 0x6;
+
+ if (d1 == d2)
+ temp_val = TEMP_INVALID;
+ else
+ temp_val = t1 + (((dmeas - d1) * (t2 - t1))/(d2 - d1));
+
+ if (temp_val <= LOW_TEMP_THRESHOLD ||
+ temp_val >= HIGH_TEMP_THRESHOLD) {
+ printk_ratelimited("%s: T0: %d is out of range[%d, %d]\n",
+ __func__, temp_val, LOW_TEMP_THRESHOLD,
+ HIGH_TEMP_THRESHOLD);
+ if (retry--) {
+ msleep(20);
+ goto temp_retry;
+ }
+ }
+ if (temp)
+ *temp = temp_val;
+ pr_debug("%s: t0 measured: %d dmeas = %d, d1 = %d, d2 = %d\n",
+ __func__, temp_val, dmeas, d1, d2);
+ return ret;
+}
+EXPORT_SYMBOL(wsa881x_get_temp);
+
+static struct thermal_zone_device_ops wsa881x_thermal_ops = {
+ .get_temp = wsa881x_get_temp,
+};
+
+int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata)
+{
+ struct thermal_zone_device *tz_dev;
+
+ if (tz_pdata == NULL) {
+ pr_err("%s: thermal pdata is NULL\n", __func__);
+ return -EINVAL;
+ }
+ /* Register with the thermal zone */
+ tz_dev = thermal_zone_device_register(tz_pdata->name,
+ 0, 0, tz_pdata,
+ &wsa881x_thermal_ops, NULL, 0, 0);
+ if (IS_ERR(tz_dev)) {
+ pr_err("%s: thermal device register failed.\n", __func__);
+ return -EINVAL;
+ }
+ tz_pdata->tz_dev = tz_dev;
+ return 0;
+}
+EXPORT_SYMBOL(wsa881x_init_thermal);
+
+void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev)
+{
+ if (tz_dev)
+ thermal_zone_device_unregister(tz_dev);
+}
+EXPORT_SYMBOL(wsa881x_deinit_thermal);
diff --git a/sound/soc/codecs/wsa881x-temp-sensor.h b/sound/soc/codecs/wsa881x-temp-sensor.h
new file mode 100644
index 000000000000..d6c1eb75e940
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-temp-sensor.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef WSA881X_TEMP_SENSOR_H
+#define WSA881X_TEMP_SENSOR_H
+
+#include <linux/thermal.h>
+#include <sound/soc.h>
+
+struct wsa_temp_register {
+ u8 d1_msb;
+ u8 d1_lsb;
+ u8 d2_msb;
+ u8 d2_lsb;
+ u8 dmeas_msb;
+ u8 dmeas_lsb;
+};
+typedef int32_t (*wsa_temp_register_read)(struct snd_soc_codec *codec,
+ struct wsa_temp_register *wsa_temp_reg);
+struct wsa881x_tz_priv {
+ struct thermal_zone_device *tz_dev;
+ struct snd_soc_codec *codec;
+ struct wsa_temp_register *wsa_temp_reg;
+ char name[80];
+ wsa_temp_register_read wsa_temp_reg_read;
+};
+
+int wsa881x_get_temp(struct thermal_zone_device *tz_dev, int *temp);
+int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata);
+void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev);
+#endif
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
new file mode 100644
index 000000000000..df2b5ff24054
--- /dev/null
+++ b/sound/soc/codecs/wsa881x.c
@@ -0,0 +1,1460 @@
+/*
+ * Copyright (c) 2015-2018,2020 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/bitops.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/debugfs.h>
+#include <linux/soundwire/soundwire.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "wsa881x.h"
+#include "wsa881x-temp-sensor.h"
+
+#define WSA881X_NUM_RETRY 5
+
+enum {
+ G_18DB = 0,
+ G_16P5DB,
+ G_15DB,
+ G_13P5DB,
+ G_12DB,
+ G_10P5DB,
+ G_9DB,
+ G_7P5DB,
+ G_6DB,
+ G_4P5DB,
+ G_3DB,
+ G_1P5DB,
+ G_0DB,
+};
+
+enum {
+ DISABLE = 0,
+ ENABLE,
+};
+
+enum {
+ SWR_DAC_PORT,
+ SWR_COMP_PORT,
+ SWR_BOOST_PORT,
+ SWR_VISENSE_PORT,
+};
+
+struct swr_port {
+ u8 port_id;
+ u8 ch_mask;
+ u32 ch_rate;
+ u8 num_ch;
+};
+
+enum {
+ WSA881X_DEV_DOWN,
+ WSA881X_DEV_UP,
+ WSA881X_DEV_READY,
+};
+
+/*
+ * Private data Structure for wsa881x. All parameters related to
+ * WSA881X codec needs to be defined here.
+ */
+struct wsa881x_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ struct swr_device *swr_slave;
+ struct snd_soc_codec *codec;
+ bool comp_enable;
+ bool boost_enable;
+ bool visense_enable;
+ u8 pa_gain;
+ struct swr_port port[WSA881X_MAX_SWR_PORTS];
+ int pd_gpio;
+ struct wsa881x_tz_priv tz_pdata;
+ int bg_cnt;
+ int clk_cnt;
+ int version;
+ struct mutex bg_lock;
+ struct mutex res_lock;
+ struct mutex temp_lock;
+ struct snd_info_entry *entry;
+ struct snd_info_entry *version_entry;
+ int state;
+ struct delayed_work ocp_ctl_work;
+ struct device_node *wsa_rst_np;
+ int pa_mute;
+};
+
+#define SWR_SLV_MAX_REG_ADDR 0x390
+#define SWR_SLV_START_REG_ADDR 0x40
+#define SWR_SLV_MAX_BUF_LEN 20
+#define BYTES_PER_LINE 12
+#define SWR_SLV_RD_BUF_LEN 8
+#define SWR_SLV_WR_BUF_LEN 32
+#define SWR_SLV_MAX_DEVICES 2
+
+#define WSA881X_VERSION_ENTRY_SIZE 27
+#define WSA881X_OCP_CTL_TIMER_SEC 2
+#define WSA881X_OCP_CTL_TEMP_CELSIUS 25
+#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60
+
+static int wsa881x_ocp_poll_timer_sec = WSA881X_OCP_CTL_POLL_TIMER_SEC;
+module_param(wsa881x_ocp_poll_timer_sec, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(wsa881x_ocp_poll_timer_sec, "timer for ocp ctl polling");
+
+static struct wsa881x_priv *dbgwsa881x;
+static struct dentry *debugfs_wsa881x_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static struct dentry *debugfs_reg_dump;
+static unsigned int read_data;
+static unsigned int devnum;
+
+static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec,
+ bool enable);
+
+static const char * const wsa_pa_gain_text[] = {
+ "G_18_DB", "G_16P5_DB", "G_15_DB", "G_13P5_DB", "G_12_DB", "G_10P5_DB",
+ "G_9_DB", "G_7P5_DB", "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB",
+ "G_0_DB"
+};
+
+static const struct soc_enum wsa_pa_gain_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa_pa_gain_text), wsa_pa_gain_text);
+
+static int wsa_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wsa881x->pa_gain;
+
+ dev_dbg(codec->dev, "%s: PA gain = 0x%x\n", __func__, wsa881x->pa_gain);
+
+ return 0;
+}
+
+static int wsa_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ wsa881x->pa_gain = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int wsa881x_get_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wsa881x->pa_mute;
+
+ return 0;
+}
+
+static int wsa881x_set_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int value = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: mute current %d, new %d\n",
+ __func__, wsa881x->pa_mute, value);
+
+ if (value)
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x80, 0x00);
+ wsa881x->pa_mute = value;
+
+ return 0;
+}
+
+
+static const struct snd_kcontrol_new wsa_snd_controls[] = {
+ SOC_ENUM_EXT("WSA PA Gain", wsa_pa_gain_enum,
+ wsa_pa_gain_get, wsa_pa_gain_put),
+ SOC_SINGLE_EXT("WSA PA Mute", SND_SOC_NOPM, 0, 1, 0,
+ wsa881x_get_mute, wsa881x_set_mute),
+};
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int get_parameters(char *buf, u32 *param1, int num_of_par)
+{
+ char *token;
+ int base, cnt;
+
+ token = strsep(&buf, " ");
+ for (cnt = 0; cnt < num_of_par; cnt++) {
+ if (token) {
+ if ((token[1] == 'x') || (token[1] == 'X'))
+ base = 16;
+ else
+ base = 10;
+
+ if (kstrtou32(token, base, &param1[cnt]) != 0)
+ return -EINVAL;
+
+ token = strsep(&buf, " ");
+ } else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static ssize_t wsa881x_codec_version_read(struct snd_info_entry *entry,
+ void *file_private_data, struct file *file,
+ char __user *buf, size_t count, loff_t pos)
+{
+ struct wsa881x_priv *wsa881x;
+ char buffer[WSA881X_VERSION_ENTRY_SIZE];
+ int len;
+
+ wsa881x = (struct wsa881x_priv *) entry->private_data;
+ if (!wsa881x) {
+ pr_err("%s: wsa881x priv is null\n", __func__);
+ return -EINVAL;
+ }
+
+ len = snprintf(buffer, sizeof(buffer), "WSA881X-SOUNDWIRE_2_0\n");
+
+ return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static struct snd_info_entry_ops wsa881x_codec_info_ops = {
+ .read = wsa881x_codec_version_read,
+};
+
+/*
+ * wsa881x_codec_info_create_codec_entry - creates wsa881x module
+ * @codec_root: The parent directory
+ * @codec: Codec instance
+ *
+ * Creates wsa881x module and version entry under the given
+ * parent directory.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int wsa881x_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+ struct snd_soc_codec *codec)
+{
+ struct snd_info_entry *version_entry;
+ struct wsa881x_priv *wsa881x;
+ struct snd_soc_card *card;
+ char name[80];
+
+ if (!codec_root || !codec)
+ return -EINVAL;
+
+ wsa881x = snd_soc_codec_get_drvdata(codec);
+ card = codec->component.card;
+ snprintf(name, sizeof(name), "%s.%x", "wsa881x",
+ (u32)wsa881x->swr_slave->addr);
+
+ wsa881x->entry = snd_register_module_info(codec_root->module,
+ (const char *)name,
+ codec_root);
+ if (!wsa881x->entry) {
+ dev_dbg(codec->dev, "%s: failed to create wsa881x entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry = snd_info_create_card_entry(card->snd_card,
+ "version",
+ wsa881x->entry);
+ if (!version_entry) {
+ dev_dbg(codec->dev, "%s: failed to create wsa881x version entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry->private_data = wsa881x;
+ version_entry->size = WSA881X_VERSION_ENTRY_SIZE;
+ version_entry->content = SNDRV_INFO_CONTENT_DATA;
+ version_entry->c.ops = &wsa881x_codec_info_ops;
+
+ if (snd_info_register(version_entry) < 0) {
+ snd_info_free_entry(version_entry);
+ return -ENOMEM;
+ }
+ wsa881x->version_entry = version_entry;
+
+ return 0;
+}
+EXPORT_SYMBOL(wsa881x_codec_info_create_codec_entry);
+
+static bool is_swr_slv_reg_readable(int reg)
+{
+ bool ret = true;
+
+ if (((reg > 0x46) && (reg < 0x4A)) ||
+ ((reg > 0x4A) && (reg < 0x50)) ||
+ ((reg > 0x55) && (reg < 0xE0)) ||
+ ((reg > 0xE0) && (reg < 0xF0)) ||
+ ((reg > 0xF0) && (reg < 0x100)) ||
+ ((reg > 0x105) && (reg < 0x120)) ||
+ ((reg > 0x128) && (reg < 0x130)) ||
+ ((reg > 0x138) && (reg < 0x200)) ||
+ ((reg > 0x205) && (reg < 0x220)) ||
+ ((reg > 0x228) && (reg < 0x230)) ||
+ ((reg > 0x238) && (reg < 0x300)) ||
+ ((reg > 0x305) && (reg < 0x320)) ||
+ ((reg > 0x328) && (reg < 0x330)) ||
+ ((reg > 0x338) && (reg < 0x400)) ||
+ ((reg > 0x405) && (reg < 0x420)))
+ ret = false;
+
+ return ret;
+}
+
+static ssize_t wsa881x_swrslave_reg_show(char __user *ubuf, size_t count,
+ loff_t *ppos)
+{
+ int i, reg_val, len;
+ ssize_t total = 0;
+ char tmp_buf[SWR_SLV_MAX_BUF_LEN];
+
+ if (!ubuf || !ppos || (devnum == 0))
+ return 0;
+
+ for (i = (((int) *ppos / BYTES_PER_LINE) + SWR_SLV_START_REG_ADDR);
+ i <= SWR_SLV_MAX_REG_ADDR; i++) {
+ if (!is_swr_slv_reg_readable(i))
+ continue;
+ swr_read(dbgwsa881x->swr_slave, devnum,
+ i, &reg_val, 1);
+ len = snprintf(tmp_buf, sizeof(tmp_buf), "0x%.3x: 0x%.2x\n",
+ i, (reg_val & 0xFF));
+ if ((total + len) >= count - 1)
+ break;
+ if (copy_to_user((ubuf + total), tmp_buf, len)) {
+ pr_err("%s: fail to copy reg dump\n", __func__);
+ total = -EFAULT;
+ goto copy_err;
+ }
+ *ppos += len;
+ total += len;
+ }
+
+copy_err:
+ return total;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char lbuf[SWR_SLV_RD_BUF_LEN];
+ char *access_str;
+ ssize_t ret_cnt;
+
+ if (!count || !file || !ppos || !ubuf)
+ return -EINVAL;
+
+ access_str = file->private_data;
+ if (*ppos < 0)
+ return -EINVAL;
+
+ if (!strcmp(access_str, "swrslave_peek")) {
+ snprintf(lbuf, sizeof(lbuf), "0x%x\n", (read_data & 0xFF));
+ ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf,
+ strnlen(lbuf, 7));
+ } else if (!strcmp(access_str, "swrslave_reg_dump")) {
+ ret_cnt = wsa881x_swrslave_reg_show(ubuf, count, ppos);
+ } else {
+ pr_err("%s: %s not permitted to read\n", __func__, access_str);
+ ret_cnt = -EPERM;
+ }
+ return ret_cnt;
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char lbuf[SWR_SLV_WR_BUF_LEN];
+ int rc;
+ u32 param[5];
+ char *access_str;
+
+ if (!filp || !ppos || !ubuf)
+ return -EINVAL;
+
+ access_str = filp->private_data;
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+ if (!strcmp(access_str, "swrslave_poke")) {
+ /* write */
+ rc = get_parameters(lbuf, param, 3);
+ if ((param[0] <= SWR_SLV_MAX_REG_ADDR) && (param[1] <= 0xFF) &&
+ (rc == 0))
+ swr_write(dbgwsa881x->swr_slave, param[2],
+ param[0], &param[1]);
+ else
+ rc = -EINVAL;
+ } else if (!strcmp(access_str, "swrslave_peek")) {
+ /* read */
+ rc = get_parameters(lbuf, param, 2);
+ if ((param[0] <= SWR_SLV_MAX_REG_ADDR) && (rc == 0))
+ swr_read(dbgwsa881x->swr_slave, param[1],
+ param[0], &read_data, 1);
+ else
+ rc = -EINVAL;
+ } else if (!strcmp(access_str, "swrslave_reg_dump")) {
+ /* reg dump */
+ rc = get_parameters(lbuf, param, 1);
+ if ((rc == 0) && (param[0] > 0) &&
+ (param[0] <= SWR_SLV_MAX_DEVICES))
+ devnum = param[0];
+ else
+ rc = -EINVAL;
+ }
+ if (rc == 0)
+ rc = cnt;
+ else
+ pr_err("%s: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+ .open = codec_debug_open,
+ .write = codec_debug_write,
+ .read = codec_debug_read,
+};
+
+static void wsa881x_regcache_sync(struct wsa881x_priv *wsa881x)
+{
+ mutex_lock(&wsa881x->res_lock);
+ if (wsa881x->state != WSA881X_DEV_READY) {
+ regcache_mark_dirty(wsa881x->regmap);
+ regcache_sync(wsa881x->regmap);
+ wsa881x->state = WSA881X_DEV_READY;
+ }
+ mutex_unlock(&wsa881x->res_lock);
+}
+
+static const struct reg_sequence wsa881x_pre_pmu_pa[] = {
+ {WSA881X_SPKR_DRV_GAIN, 0x41, 0},
+ {WSA881X_SPKR_MISC_CTL1, 0x01, 0},
+ {WSA881X_ADC_EN_DET_TEST_I, 0x01, 0},
+ {WSA881X_ADC_EN_MODU_V, 0x02, 0},
+ {WSA881X_ADC_EN_DET_TEST_V, 0x10, 0},
+ {WSA881X_SPKR_PWRSTG_DBG, 0xA0, 0},
+};
+
+static const struct reg_sequence wsa881x_pre_pmu_pa_2_0[] = {
+ {WSA881X_SPKR_DRV_GAIN, 0x41, 0},
+ {WSA881X_SPKR_MISC_CTL1, 0x87, 0},
+};
+
+static const struct reg_sequence wsa881x_post_pmu_pa[] = {
+ {WSA881X_SPKR_PWRSTG_DBG, 0x00, 0},
+ {WSA881X_ADC_EN_DET_TEST_V, 0x00, 0},
+ {WSA881X_ADC_EN_MODU_V, 0x00, 0},
+ {WSA881X_ADC_EN_DET_TEST_I, 0x00, 0},
+};
+
+static const struct reg_sequence wsa881x_vi_txfe_en[] = {
+ {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0},
+ {WSA881X_SPKR_PROT_ATEST2, 0x0A, 0},
+ {WSA881X_SPKR_PROT_FE_GAIN, 0xCF, 0},
+};
+
+static const struct reg_sequence wsa881x_vi_txfe_en_2_0[] = {
+ {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0},
+ {WSA881X_SPKR_PROT_ATEST2, 0x0A, 0},
+ {WSA881X_SPKR_PROT_FE_GAIN, 0x47, 0},
+};
+
+static int wsa881x_boost_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+ dev_dbg(codec->dev, "%s: enable:%d\n", __func__, enable);
+ if (enable)
+ snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x80);
+ else
+ snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x00);
+ /*
+ * 1.5ms sleep is needed after boost enable/disable as per
+ * HW requirement
+ */
+ usleep_range(1500, 1510);
+ return 0;
+}
+
+static int wsa881x_visense_txfe_ctrl(struct snd_soc_codec *codec, bool enable,
+ u8 isense1_gain, u8 isense2_gain,
+ u8 vsense_gain)
+{
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev,
+ "%s: enable:%d, isense1 gain: %d, isense2 gain: %d, vsense_gain %d\n",
+ __func__, enable, isense1_gain, isense2_gain, vsense_gain);
+
+ if (enable) {
+ regmap_multi_reg_write(wsa881x->regmap,
+ wsa881x_vi_txfe_en_2_0,
+ ARRAY_SIZE(wsa881x_vi_txfe_en_2_0));
+ } else {
+ snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_VSENSE_VCM,
+ 0x08, 0x08);
+ /*
+ * 200us sleep is needed after visense txfe disable as per
+ * HW requirement.
+ */
+ usleep_range(200, 210);
+ snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN,
+ 0x01, 0x00);
+ }
+ return 0;
+}
+
+static int wsa881x_visense_adc_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+
+ dev_dbg(codec->dev, "%s: enable:%d\n", __func__, enable);
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, (0x01 << 7),
+ (enable << 7));
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, (0x01 << 7),
+ (enable << 7));
+ return 0;
+}
+
+static void wsa881x_bandgap_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: enable:%d, bg_count:%d\n", __func__,
+ enable, wsa881x->bg_cnt);
+ mutex_lock(&wsa881x->bg_lock);
+ if (enable) {
+ ++wsa881x->bg_cnt;
+ if (wsa881x->bg_cnt == 1) {
+ snd_soc_update_bits(codec, WSA881X_TEMP_OP,
+ 0x08, 0x08);
+ /* 400usec sleep is needed as per HW requirement */
+ usleep_range(400, 410);
+ snd_soc_update_bits(codec, WSA881X_TEMP_OP,
+ 0x04, 0x04);
+ }
+ } else {
+ --wsa881x->bg_cnt;
+ if (wsa881x->bg_cnt <= 0) {
+ WARN_ON(wsa881x->bg_cnt < 0);
+ wsa881x->bg_cnt = 0;
+ snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x00);
+ snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x08, 0x00);
+ }
+ }
+ mutex_unlock(&wsa881x->bg_lock);
+}
+
+static void wsa881x_clk_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: enable:%d, clk_count:%d\n", __func__,
+ enable, wsa881x->clk_cnt);
+ mutex_lock(&wsa881x->res_lock);
+ if (enable) {
+ ++wsa881x->clk_cnt;
+ if (wsa881x->clk_cnt == 1) {
+ snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x01);
+ snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x01);
+ }
+ } else {
+ --wsa881x->clk_cnt;
+ if (wsa881x->clk_cnt <= 0) {
+ WARN_ON(wsa881x->clk_cnt < 0);
+ wsa881x->clk_cnt = 0;
+ snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x00);
+ snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x00);
+ }
+ }
+ mutex_unlock(&wsa881x->res_lock);
+}
+
+static int wsa881x_get_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wsa881x->comp_enable;
+ return 0;
+}
+
+static int wsa881x_set_compander(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int value = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: Compander enable current %d, new %d\n",
+ __func__, wsa881x->comp_enable, value);
+ wsa881x->comp_enable = value;
+ return 0;
+}
+
+static int wsa881x_get_boost(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wsa881x->boost_enable;
+ return 0;
+}
+
+static int wsa881x_set_boost(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int value = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: Boost enable current %d, new %d\n",
+ __func__, wsa881x->boost_enable, value);
+ wsa881x->boost_enable = value;
+ return 0;
+}
+
+static int wsa881x_get_visense(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wsa881x->visense_enable;
+ return 0;
+}
+
+static int wsa881x_set_visense(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int value = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: VIsense enable current %d, new %d\n",
+ __func__, wsa881x->visense_enable, value);
+ wsa881x->visense_enable = value;
+ return 0;
+}
+
+static const struct snd_kcontrol_new wsa881x_snd_controls[] = {
+ SOC_SINGLE_EXT("COMP Switch", SND_SOC_NOPM, 0, 1, 0,
+ wsa881x_get_compander, wsa881x_set_compander),
+
+ SOC_SINGLE_EXT("BOOST Switch", SND_SOC_NOPM, 0, 1, 0,
+ wsa881x_get_boost, wsa881x_set_boost),
+
+ SOC_SINGLE_EXT("VISENSE Switch", SND_SOC_NOPM, 0, 1, 0,
+ wsa881x_get_visense, wsa881x_set_visense),
+};
+
+static const struct snd_kcontrol_new swr_dac_port[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static int wsa881x_set_port(struct snd_soc_codec *codec, int port_idx,
+ u8 *port_id, u8 *num_ch, u8 *ch_mask, u32 *ch_rate)
+{
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ *port_id = wsa881x->port[port_idx].port_id;
+ *num_ch = wsa881x->port[port_idx].num_ch;
+ *ch_mask = wsa881x->port[port_idx].ch_mask;
+ *ch_rate = wsa881x->port[port_idx].ch_rate;
+ return 0;
+}
+
+static int wsa881x_enable_swr_dac_port(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+ u8 port_id[WSA881X_MAX_SWR_PORTS];
+ u8 num_ch[WSA881X_MAX_SWR_PORTS];
+ u8 ch_mask[WSA881X_MAX_SWR_PORTS];
+ u32 ch_rate[WSA881X_MAX_SWR_PORTS];
+ u8 num_port = 0;
+
+ dev_dbg(codec->dev, "%s: event %d name %s\n", __func__,
+ event, w->name);
+ if (wsa881x == NULL)
+ return -EINVAL;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wsa881x_set_port(codec, SWR_DAC_PORT,
+ &port_id[num_port], &num_ch[num_port],
+ &ch_mask[num_port], &ch_rate[num_port]);
+ ++num_port;
+
+ if (wsa881x->comp_enable) {
+ wsa881x_set_port(codec, SWR_COMP_PORT,
+ &port_id[num_port], &num_ch[num_port],
+ &ch_mask[num_port], &ch_rate[num_port]);
+ ++num_port;
+ }
+ if (wsa881x->boost_enable) {
+ wsa881x_set_port(codec, SWR_BOOST_PORT,
+ &port_id[num_port], &num_ch[num_port],
+ &ch_mask[num_port], &ch_rate[num_port]);
+ ++num_port;
+ }
+ if (wsa881x->visense_enable) {
+ wsa881x_set_port(codec, SWR_VISENSE_PORT,
+ &port_id[num_port], &num_ch[num_port],
+ &ch_mask[num_port], &ch_rate[num_port]);
+ ++num_port;
+ }
+ swr_connect_port(wsa881x->swr_slave, &port_id[0], num_port,
+ &ch_mask[0], &ch_rate[0], &num_ch[0]);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ port_id[num_port] = wsa881x->port[SWR_DAC_PORT].port_id;
+ ++num_port;
+ if (wsa881x->comp_enable) {
+ port_id[num_port] =
+ wsa881x->port[SWR_COMP_PORT].port_id;
+ ++num_port;
+ }
+ if (wsa881x->boost_enable) {
+ port_id[num_port] =
+ wsa881x->port[SWR_BOOST_PORT].port_id;
+ ++num_port;
+ }
+ if (wsa881x->visense_enable) {
+ port_id[num_port] =
+ wsa881x->port[SWR_VISENSE_PORT].port_id;
+ ++num_port;
+ }
+ swr_disconnect_port(wsa881x->swr_slave, &port_id[0], num_port);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: %s %d boost %d visense %d\n", __func__,
+ w->name, event, wsa881x->boost_enable,
+ wsa881x->visense_enable);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mutex_lock(&wsa881x->temp_lock);
+ wsa881x_resource_acquire(codec, ENABLE);
+ mutex_unlock(&wsa881x->temp_lock);
+ wsa881x_boost_ctrl(codec, ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ swr_slvdev_datapath_control(wsa881x->swr_slave,
+ wsa881x->swr_slave->dev_num,
+ false);
+ wsa881x_boost_ctrl(codec, DISABLE);
+ mutex_lock(&wsa881x->temp_lock);
+ wsa881x_resource_acquire(codec, DISABLE);
+ mutex_unlock(&wsa881x->temp_lock);
+ break;
+ }
+ return 0;
+}
+
+static int wsa881x_ramp_pa_gain(struct snd_soc_codec *codec,
+ int min_gain, int max_gain, int udelay)
+{
+ int val;
+ for (val = min_gain; max_gain <= val; val--) {
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN,
+ 0xF0, val << 4);
+ /*
+ * 1ms delay is needed for every step change in gain as per
+ * HW requirement.
+ */
+ usleep_range(udelay, udelay+10);
+ }
+ return 0;
+}
+
+static void wsa881x_ocp_ctl_work(struct work_struct *work)
+{
+ struct wsa881x_priv *wsa881x;
+ struct delayed_work *dwork;
+ struct snd_soc_codec *codec;
+ int temp_val;
+
+ dwork = to_delayed_work(work);
+ wsa881x = container_of(dwork, struct wsa881x_priv, ocp_ctl_work);
+
+ codec = wsa881x->codec;
+ wsa881x_get_temp(wsa881x->tz_pdata.tz_dev, &temp_val);
+ dev_dbg(codec->dev, " temp = %d\n", temp_val);
+
+ if (temp_val <= WSA881X_OCP_CTL_TEMP_CELSIUS)
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x00);
+ else
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0);
+
+ schedule_delayed_work(&wsa881x->ocp_ctl_work,
+ msecs_to_jiffies(wsa881x_ocp_poll_timer_sec * 1000));
+}
+
+static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int min_gain, max_gain;
+
+ dev_dbg(codec->dev, "%s: %s %d\n", __func__, w->name, event);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x80);
+ regmap_multi_reg_write(wsa881x->regmap,
+ wsa881x_pre_pmu_pa_2_0,
+ ARRAY_SIZE(wsa881x_pre_pmu_pa_2_0));
+ swr_slvdev_datapath_control(wsa881x->swr_slave,
+ wsa881x->swr_slave->dev_num,
+ true);
+ /* Set register mode if compander is not enabled */
+ if (!wsa881x->comp_enable)
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN,
+ 0x08, 0x08);
+ else
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN,
+ 0x08, 0x00);
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (!wsa881x->comp_enable) {
+ max_gain = wsa881x->pa_gain;
+ /*
+ * Gain has to set incrementally in 4 steps
+ * as per HW sequence
+ */
+ if (max_gain > G_4P5DB)
+ min_gain = G_0DB;
+ else
+ min_gain = max_gain + 3;
+ /*
+ * 1ms delay is needed before change in gain
+ * as per HW requirement.
+ */
+ usleep_range(1000, 1010);
+ wsa881x_ramp_pa_gain(codec, min_gain, max_gain, 1000);
+ }
+ if (wsa881x->visense_enable) {
+ wsa881x_visense_txfe_ctrl(codec, ENABLE,
+ 0x00, 0x03, 0x01);
+ snd_soc_update_bits(codec, WSA881X_ADC_EN_SEL_IBAIS,
+ 0x07, 0x01);
+ wsa881x_visense_adc_ctrl(codec, ENABLE);
+ }
+ schedule_delayed_work(&wsa881x->ocp_ctl_work,
+ msecs_to_jiffies(WSA881X_OCP_CTL_TIMER_SEC * 1000));
+ /* Force remove group */
+ swr_remove_from_group(wsa881x->swr_slave,
+ wsa881x->swr_slave->dev_num);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (wsa881x->visense_enable) {
+ wsa881x_visense_adc_ctrl(codec, DISABLE);
+ wsa881x_visense_txfe_ctrl(codec, DISABLE,
+ 0x00, 0x01, 0x01);
+ }
+ cancel_delayed_work_sync(&wsa881x->ocp_ctl_work);
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+
+ SND_SOC_DAPM_MIXER_E("SWR DAC_Port", SND_SOC_NOPM, 0, 0, swr_dac_port,
+ ARRAY_SIZE(swr_dac_port), wsa881x_enable_swr_dac_port,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("RDAC", NULL, WSA881X_SPKR_DAC_CTL, 7, 0,
+ wsa881x_rdac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("SPKR PGA", WSA881X_SPKR_DRV_EN, 7, 0, NULL, 0,
+ wsa881x_spkr_pa_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+};
+
+static const struct snd_soc_dapm_route wsa881x_audio_map[] = {
+ {"SWR DAC_Port", "Switch", "IN"},
+ {"RDAC", NULL, "SWR DAC_Port"},
+ {"SPKR PGA", NULL, "RDAC"},
+ {"SPKR", NULL, "SPKR PGA"},
+};
+
+int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, u8 num_port,
+ unsigned int *ch_mask, unsigned int *ch_rate)
+{
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ if (!port || !ch_mask || !ch_rate ||
+ (num_port > WSA881X_MAX_SWR_PORTS)) {
+ dev_err(codec->dev,
+ "%s: Invalid port=%pK, ch_mask=%pK, ch_rate=%pK\n",
+ __func__, port, ch_mask, ch_rate);
+ return -EINVAL;
+ }
+ for (i = 0; i < num_port; i++) {
+ wsa881x->port[i].port_id = port[i];
+ wsa881x->port[i].ch_mask = ch_mask[i];
+ wsa881x->port[i].ch_rate = ch_rate[i];
+ wsa881x->port[i].num_ch = __sw_hweight8(ch_mask[i]);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(wsa881x_set_channel_map);
+
+static void wsa881x_init(struct snd_soc_codec *codec)
+{
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ wsa881x->version = snd_soc_read(codec, WSA881X_CHIP_ID1);
+ wsa881x_regmap_defaults(wsa881x->regmap, wsa881x->version);
+ /* Enable software reset output from soundwire slave */
+ snd_soc_update_bits(codec, WSA881X_SWR_RESET_EN, 0x07, 0x07);
+ /* Bring out of analog reset */
+ snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x02, 0x02);
+ /* Bring out of digital reset */
+ snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x01, 0x01);
+
+ snd_soc_update_bits(codec, WSA881X_CLOCK_CONFIG, 0x10, 0x10);
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x02, 0x02);
+ snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80);
+ snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06);
+ snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_INT, 0xFF, 0x00);
+ snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0xF0, 0x40);
+ snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0x0E, 0x0E);
+ snd_soc_update_bits(codec, WSA881X_BOOST_LOOP_STABILITY,
+ 0x03, 0x03);
+ snd_soc_update_bits(codec, WSA881X_BOOST_MISC2_CTL, 0xFF, 0x14);
+ snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, 0x03, 0x00);
+ snd_soc_update_bits(codec, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB,
+ 0x0C, 0x04);
+ snd_soc_update_bits(codec, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB,
+ 0x03, 0x00);
+ if (snd_soc_read(codec, WSA881X_OTP_REG_0))
+ snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1,
+ 0xF0, 0x70);
+ snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT2,
+ 0xF0, 0x30);
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x08, 0x08);
+ snd_soc_update_bits(codec, WSA881X_BOOST_CURRENT_LIMIT,
+ 0x0F, 0x08);
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x30, 0x30);
+ snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x0C, 0x00);
+ snd_soc_update_bits(codec, WSA881X_OTP_REG_28, 0x3F, 0x3A);
+ snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG1,
+ 0xFF, 0xB2);
+ snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG2,
+ 0xFF, 0x05);
+}
+
+static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec,
+ bool enable)
+{
+ wsa881x_clk_ctrl(codec, enable);
+ wsa881x_bandgap_ctrl(codec, enable);
+ return 0;
+}
+
+static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec,
+ struct wsa_temp_register *wsa_temp_reg)
+{
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+ struct swr_device *dev;
+ u8 retry = WSA881X_NUM_RETRY;
+ u8 devnum = 0;
+
+ if (!wsa881x) {
+ dev_err(codec->dev, "%s: wsa881x is NULL\n", __func__);
+ return -EINVAL;
+ }
+ dev = wsa881x->swr_slave;
+ if (dev && (wsa881x->state == WSA881X_DEV_DOWN)) {
+ while (swr_get_logical_dev_num(dev, dev->addr, &devnum) &&
+ retry--) {
+ /* Retry after 1 msec delay */
+ usleep_range(1000, 1100);
+ }
+ if (retry == 0) {
+ dev_err(codec->dev,
+ "%s get devnum %d for dev addr %lx failed\n",
+ __func__, devnum, dev->addr);
+ return -EINVAL;
+ }
+ }
+ wsa881x_regcache_sync(wsa881x);
+ mutex_lock(&wsa881x->temp_lock);
+ wsa881x_resource_acquire(codec, ENABLE);
+
+ snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00);
+ wsa_temp_reg->dmeas_msb = snd_soc_read(codec, WSA881X_TEMP_MSB);
+ wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, WSA881X_TEMP_LSB);
+ snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x01);
+ wsa_temp_reg->d1_msb = snd_soc_read(codec, WSA881X_OTP_REG_1);
+ wsa_temp_reg->d1_lsb = snd_soc_read(codec, WSA881X_OTP_REG_2);
+ wsa_temp_reg->d2_msb = snd_soc_read(codec, WSA881X_OTP_REG_3);
+ wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4);
+
+ wsa881x_resource_acquire(codec, DISABLE);
+ mutex_unlock(&wsa881x->temp_lock);
+
+ return 0;
+}
+
+static int wsa881x_probe(struct snd_soc_codec *codec)
+{
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+ struct swr_device *dev;
+
+ if (!wsa881x)
+ return -EINVAL;
+
+ dev = wsa881x->swr_slave;
+ wsa881x->codec = codec;
+ mutex_init(&wsa881x->bg_lock);
+ wsa881x_init(codec);
+ snprintf(wsa881x->tz_pdata.name, sizeof(wsa881x->tz_pdata.name),
+ "%s.%x", "wsatz", (u8)dev->addr);
+ wsa881x->bg_cnt = 0;
+ wsa881x->clk_cnt = 0;
+ wsa881x->tz_pdata.codec = codec;
+ wsa881x->tz_pdata.wsa_temp_reg_read = wsa881x_temp_reg_read;
+ wsa881x_init_thermal(&wsa881x->tz_pdata);
+ snd_soc_add_codec_controls(codec, wsa_snd_controls,
+ ARRAY_SIZE(wsa_snd_controls));
+ INIT_DELAYED_WORK(&wsa881x->ocp_ctl_work, wsa881x_ocp_ctl_work);
+ return 0;
+}
+
+static int wsa881x_remove(struct snd_soc_codec *codec)
+{
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ if (wsa881x->tz_pdata.tz_dev)
+ wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev);
+ mutex_destroy(&wsa881x->bg_lock);
+
+ return 0;
+}
+
+static struct regmap *wsa881x_get_regmap(struct device *dev)
+{
+ struct wsa881x_priv *control = swr_get_dev_data(to_swr_device(dev));
+
+ if (!control)
+ return NULL;
+
+ return control->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wsa881x = {
+ .probe = wsa881x_probe,
+ .remove = wsa881x_remove,
+ .controls = wsa881x_snd_controls,
+ .num_controls = ARRAY_SIZE(wsa881x_snd_controls),
+ .dapm_widgets = wsa881x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets),
+ .dapm_routes = wsa881x_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map),
+ .get_regmap = wsa881x_get_regmap,
+};
+
+static int wsa881x_gpio_ctrl(struct wsa881x_priv *wsa881x, bool enable)
+{
+ int ret = 0;
+
+ if (wsa881x->pd_gpio < 0) {
+ dev_err(wsa881x->dev, "%s: gpio is not valid %d\n",
+ __func__, wsa881x->pd_gpio);
+ return -EINVAL;
+ }
+
+ if (wsa881x->wsa_rst_np) {
+ if (enable)
+ ret = msm_cdc_pinctrl_select_active_state(
+ wsa881x->wsa_rst_np);
+ else
+ ret = msm_cdc_pinctrl_select_sleep_state(
+ wsa881x->wsa_rst_np);
+ if (ret != 0)
+ dev_err(wsa881x->dev,
+ "%s: Failed to turn state %d; ret=%d\n",
+ __func__, enable, ret);
+ } else {
+ if (gpio_is_valid(wsa881x->pd_gpio))
+ gpio_direction_output(wsa881x->pd_gpio, enable);
+ }
+
+ return ret;
+}
+
+static int wsa881x_gpio_init(struct swr_device *pdev)
+{
+ int ret = 0;
+ struct wsa881x_priv *wsa881x;
+
+ wsa881x = swr_get_dev_data(pdev);
+ if (!wsa881x) {
+ dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
+ return -EINVAL;
+ }
+ dev_dbg(&pdev->dev, "%s: gpio %d request with name %s\n",
+ __func__, wsa881x->pd_gpio, dev_name(&pdev->dev));
+ ret = gpio_request(wsa881x->pd_gpio, dev_name(&pdev->dev));
+ if (ret) {
+ if (ret == -EBUSY) {
+ /* GPIO was already requested */
+ dev_dbg(&pdev->dev,
+ "%s: gpio %d is already set to high\n",
+ __func__, wsa881x->pd_gpio);
+ ret = 0;
+ } else {
+ dev_err(&pdev->dev, "%s: Failed to request gpio %d, err: %d\n",
+ __func__, wsa881x->pd_gpio, ret);
+ }
+ }
+ return ret;
+}
+
+static int wsa881x_swr_probe(struct swr_device *pdev)
+{
+ int ret = 0;
+ struct wsa881x_priv *wsa881x;
+ u8 devnum = 0;
+ bool pin_state_current = false;
+
+ wsa881x = devm_kzalloc(&pdev->dev, sizeof(struct wsa881x_priv),
+ GFP_KERNEL);
+ if (!wsa881x) {
+ dev_err(&pdev->dev, "%s: cannot create memory for wsa881x\n",
+ __func__);
+ return -ENOMEM;
+ }
+ wsa881x->wsa_rst_np = of_parse_phandle(pdev->dev.of_node,
+ "qcom,spkr-sd-n-node", 0);
+ if (!wsa881x->wsa_rst_np) {
+ dev_dbg(&pdev->dev, "%s: Not using pinctrl, fallback to gpio\n",
+ __func__);
+ wsa881x->pd_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,spkr-sd-n-gpio", 0);
+ if (wsa881x->pd_gpio < 0) {
+ dev_err(&pdev->dev, "%s: %s property is not found %d\n",
+ __func__, "qcom,spkr-sd-n-gpio",
+ wsa881x->pd_gpio);
+ goto err;
+ }
+ dev_dbg(&pdev->dev, "%s: reset gpio %d\n", __func__,
+ wsa881x->pd_gpio);
+ }
+ swr_set_dev_data(pdev, wsa881x);
+
+ wsa881x->swr_slave = pdev;
+
+ if (!wsa881x->wsa_rst_np) {
+ ret = wsa881x_gpio_init(pdev);
+ if (ret)
+ goto err;
+ }
+ if (wsa881x->wsa_rst_np)
+ pin_state_current = msm_cdc_pinctrl_get_state(
+ wsa881x->wsa_rst_np);
+ mutex_init(&wsa881x->res_lock);
+ mutex_init(&wsa881x->temp_lock);
+ wsa881x_gpio_ctrl(wsa881x, true);
+ wsa881x->state = WSA881X_DEV_UP;
+
+ if (!debugfs_wsa881x_dent) {
+ dbgwsa881x = wsa881x;
+ debugfs_wsa881x_dent = debugfs_create_dir(
+ "wsa881x_swr_slave", 0);
+ if (!IS_ERR(debugfs_wsa881x_dent)) {
+ debugfs_peek = debugfs_create_file("swrslave_peek",
+ S_IFREG | S_IRUGO, debugfs_wsa881x_dent,
+ (void *) "swrslave_peek",
+ &codec_debug_ops);
+
+ debugfs_poke = debugfs_create_file("swrslave_poke",
+ S_IFREG | S_IRUGO, debugfs_wsa881x_dent,
+ (void *) "swrslave_poke",
+ &codec_debug_ops);
+
+ debugfs_reg_dump = debugfs_create_file(
+ "swrslave_reg_dump",
+ S_IFREG | S_IRUGO,
+ debugfs_wsa881x_dent,
+ (void *) "swrslave_reg_dump",
+ &codec_debug_ops);
+ }
+ }
+
+ /*
+ * Add 5msec delay to provide sufficient time for
+ * soundwire auto enumeration of slave devices as
+ * as per HW requirement.
+ */
+ usleep_range(5000, 5010);
+ ret = swr_get_logical_dev_num(pdev, pdev->addr, &devnum);
+ if (ret) {
+ dev_dbg(&pdev->dev,
+ "%s get devnum %d for dev addr %lx failed\n",
+ __func__, devnum, pdev->addr);
+ goto dev_err;
+ }
+ pdev->dev_num = devnum;
+
+ wsa881x->regmap = devm_regmap_init_swr(pdev,
+ &wsa881x_regmap_config);
+ if (IS_ERR(wsa881x->regmap)) {
+ ret = PTR_ERR(wsa881x->regmap);
+ dev_err(&pdev->dev, "%s: regmap_init failed %d\n",
+ __func__, ret);
+ goto dev_err;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wsa881x,
+ NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: Codec registration failed\n",
+ __func__);
+ goto dev_err;
+ }
+
+ return 0;
+
+dev_err:
+ if (pin_state_current == false)
+ wsa881x_gpio_ctrl(wsa881x, false);
+ swr_remove_device(pdev);
+err:
+ return ret;
+}
+
+static int wsa881x_swr_remove(struct swr_device *pdev)
+{
+ struct wsa881x_priv *wsa881x;
+
+ wsa881x = swr_get_dev_data(pdev);
+ if (!wsa881x) {
+ dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
+ return -EINVAL;
+ }
+ debugfs_remove_recursive(debugfs_wsa881x_dent);
+ mutex_destroy(&wsa881x->res_lock);
+ mutex_destroy(&wsa881x->temp_lock);
+ snd_soc_unregister_codec(&pdev->dev);
+ if (wsa881x->pd_gpio)
+ gpio_free(wsa881x->pd_gpio);
+ swr_set_dev_data(pdev, NULL);
+ return 0;
+}
+
+static int wsa881x_swr_up(struct swr_device *pdev)
+{
+ int ret;
+ struct wsa881x_priv *wsa881x;
+
+ wsa881x = swr_get_dev_data(pdev);
+ if (!wsa881x) {
+ dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
+ return -EINVAL;
+ }
+ ret = wsa881x_gpio_ctrl(wsa881x, true);
+ if (ret)
+ dev_err(&pdev->dev, "%s: Failed to enable gpio\n", __func__);
+ else
+ wsa881x->state = WSA881X_DEV_UP;
+
+ return ret;
+}
+
+static int wsa881x_swr_down(struct swr_device *pdev)
+{
+ struct wsa881x_priv *wsa881x;
+ int ret;
+
+ wsa881x = swr_get_dev_data(pdev);
+ if (!wsa881x) {
+ dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (delayed_work_pending(&wsa881x->ocp_ctl_work))
+ cancel_delayed_work_sync(&wsa881x->ocp_ctl_work);
+ ret = wsa881x_gpio_ctrl(wsa881x, false);
+ if (ret)
+ dev_err(&pdev->dev, "%s: Failed to disable gpio\n", __func__);
+ else
+ wsa881x->state = WSA881X_DEV_DOWN;
+
+ return ret;
+}
+
+static int wsa881x_swr_reset(struct swr_device *pdev)
+{
+ struct wsa881x_priv *wsa881x;
+ u8 retry = WSA881X_NUM_RETRY;
+ u8 devnum = 0;
+
+ wsa881x = swr_get_dev_data(pdev);
+ if (!wsa881x) {
+ dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (wsa881x->state == WSA881X_DEV_READY) {
+ dev_dbg(&pdev->dev, "%s: device already active\n", __func__);
+ return 0;
+ }
+
+ wsa881x->bg_cnt = 0;
+ wsa881x->clk_cnt = 0;
+ while (swr_get_logical_dev_num(pdev, pdev->addr, &devnum) && retry--) {
+ /* Retry after 1 msec delay */
+ usleep_range(1000, 1100);
+ }
+ pdev->dev_num = devnum;
+ wsa881x_regcache_sync(wsa881x);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int wsa881x_swr_suspend(struct device *dev)
+{
+ dev_dbg(dev, "%s: system suspend\n", __func__);
+ return 0;
+}
+
+static int wsa881x_swr_resume(struct device *dev)
+{
+ struct wsa881x_priv *wsa881x = swr_get_dev_data(to_swr_device(dev));
+
+ if (!wsa881x) {
+ dev_err(dev, "%s: wsa881x private data is NULL\n", __func__);
+ return -EINVAL;
+ }
+ dev_dbg(dev, "%s: system resume\n", __func__);
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops wsa881x_swr_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(wsa881x_swr_suspend, wsa881x_swr_resume)
+};
+
+static const struct swr_device_id wsa881x_swr_id[] = {
+ {"wsa881x", 0},
+ {}
+};
+
+static struct of_device_id wsa881x_swr_dt_match[] = {
+ {
+ .compatible = "qcom,wsa881x",
+ },
+ {}
+};
+
+static struct swr_driver wsa881x_codec_driver = {
+ .driver = {
+ .name = "wsa881x",
+ .owner = THIS_MODULE,
+ .pm = &wsa881x_swr_pm_ops,
+ .of_match_table = wsa881x_swr_dt_match,
+ },
+ .probe = wsa881x_swr_probe,
+ .remove = wsa881x_swr_remove,
+ .id_table = wsa881x_swr_id,
+ .device_up = wsa881x_swr_up,
+ .device_down = wsa881x_swr_down,
+ .reset_device = wsa881x_swr_reset,
+};
+
+static int __init wsa881x_codec_init(void)
+{
+ return swr_driver_register(&wsa881x_codec_driver);
+}
+
+static void __exit wsa881x_codec_exit(void)
+{
+ swr_driver_unregister(&wsa881x_codec_driver);
+}
+
+module_init(wsa881x_codec_init);
+module_exit(wsa881x_codec_exit);
+
+MODULE_DESCRIPTION("WSA881x Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wsa881x.h b/sound/soc/codecs/wsa881x.h
new file mode 100644
index 000000000000..9bd9f9587a2e
--- /dev/null
+++ b/sound/soc/codecs/wsa881x.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WSA881X_H
+#define _WSA881X_H
+
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/info.h>
+#include "wsa881x-registers.h"
+
+#define WSA881X_MAX_SWR_PORTS 4
+
+extern int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port,
+ u8 num_port, unsigned int *ch_mask,
+ unsigned int *ch_rate);
+
+extern const u8 wsa881x_reg_readable[WSA881X_CACHE_SIZE];
+extern struct regmap_config wsa881x_regmap_config;
+extern int wsa881x_codec_info_create_codec_entry(
+ struct snd_info_entry *codec_root,
+ struct snd_soc_codec *codec);
+void wsa881x_regmap_defaults(struct regmap *regmap, u8 version);
+
+#endif /* _WSA881X_H */
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
new file mode 100644
index 000000000000..05ff48ff033a
--- /dev/null
+++ b/sound/soc/msm/Kconfig
@@ -0,0 +1,263 @@
+menu "MSM SoC Audio support"
+
+config SND_SOC_MSM_HOSTLESS_PCM
+ tristate
+
+config SND_SOC_MSM_QDSP6V2_INTF
+ bool "SoC Q6 audio driver for MSM/APQ"
+ depends on MSM_QDSP6_APRV2_GLINK
+ help
+ To add support for SoC audio on MSM/APQ.
+ This will enable all the platform specific
+ interactions towards DSP. It includes asm,
+ adm and afe interfaces on the DSP.
+
+config SND_SOC_QDSP6V2
+ tristate "SoC ALSA audio driver for QDSP6V2"
+ select SND_SOC_MSM_QDSP6V2_INTF
+ select SND_SOC_COMPRESS
+ help
+ To add support for MSM QDSP6V2 Soc Audio.
+ This will enable sound soc platform specific
+ audio drivers. This includes q6asm, q6adm,
+ q6afe interfaces to DSP using apr.
+
+config SND_SOC_QDSP6V2_VM
+ tristate "SoC ALSA audio driver for QDSP6V2 virtualization"
+ depends on MSM_QDSP6_APRV2_VM
+ select SND_SOC_COMPRESS
+ help
+ To add support for MSM QDSP6V2 virtualization
+ Soc Audio.
+ This will enable sound soc platform specific
+ audio drivers. This includes q6asm, q6adm,
+ q6afe interfaces to DSP using virtualized apr.
+
+config SND_SOC_QDSP_DEBUG
+ bool "QDSP Audio Driver Debug Feature"
+ help
+ Configuration to enable debugging utilities for
+ QDSP6 based audio drivers. One debugging utility
+ is inducing kernel panic upon encountering critical
+ errors from DSP audio modules
+
+config DOLBY_DS2
+ bool "Enable Dolby DS2"
+ depends on SND_SOC_MSM_QDSP6V2_INTF
+ help
+ To add support for dolby DAP post processing.
+ This support is to configure the post processing parameters
+ to DSP. The configuration includes sending the end point
+ device, end point dependent post processing parameters and
+ the various posrt processing parameters
+
+config DOLBY_LICENSE
+ bool "Enable Dolby LICENSE"
+ depends on SND_SOC_MSM_QDSP6V2_INTF
+ help
+ To add support for dolby DAP post processing,
+ and retain DAP set license functionality only.
+ This is required by Dolby GEF implementation which needs
+ nothing but dolby license validation functionality in driver.
+
+config DTS_EAGLE
+ bool "Enable DTS Eagle Support"
+ depends on SND_SOC_MSM_QDSP6V2_INTF
+ select SND_HWDEP
+ help
+ To add DTS Eagle support on QDSP6 targets.
+ Eagle is a DTS pre/post processing
+ package that includes HeadphoneX. The configuration
+ includes sending tuning parameters of various modules.
+
+config DTS_SRS_TM
+ bool "Enable DTS SRS"
+ depends on SND_SOC_MSM_QDSP6V2_INTF
+ help
+ To add support for DTS SRS post processing.
+ This support is to configure the post processing
+ parameters to DSP. The configuration includes sending
+ tuning parameters of various modules.
+
+config QTI_PP
+ bool "Enable QTI PP"
+ depends on SND_SOC_MSM_QDSP6V2_INTF || SND_SOC_QDSP6V2_VM
+ help
+ To add support for default QTI post processing.
+ This support is to configure the post processing
+ parameters to DSP. The configuration includes sending
+ tuning parameters of various modules such as equalizer,
+ customized mixing.
+
+config QTI_PP_AUDIOSPHERE
+ bool "Enable QTI AUDIOSPHERE PP"
+ depends on SND_SOC_MSM_QDSP6V2_INTF
+ help
+ To add support for QTI audio sphere post processing.
+ This support is to configure the post processing
+ parameters to DSP. The configuration includes sending
+ tuning parameters of audio sphere module.
+
+config SND_SOC_CPE
+ tristate "CPE drivers"
+ depends on SND_SOC_WCD_CPE
+ help
+ To add support for Codec Processing Engine. This support
+ is to enable CPE block on the codec and this config needs
+ to be added to codecs that contain the CPE hardware block.
+ The configuration includes the cpe lsm driver to enable
+ listen on codec.
+
+config SND_SOC_INT_CODEC
+ tristate "SoC Machine driver for SDM660_INT"
+ depends on ARCH_QCOM
+ select SND_SOC_QDSP6V2
+ select SND_SOC_MSM_STUB
+ select SND_SOC_MSM_HOSTLESS_PCM
+ select SND_DYNAMIC_MINORS
+ select MSM_QDSP6_APRV2_GLINK
+ select MSM_QDSP6_SSR
+ select MSM_QDSP6_PDR
+ select MSM_QDSP6_NOTIFIER
+ select MSM_QDSP6V2_CODECS
+ select MSM_CDC_PINCTRL
+ select SND_SOC_MSM_SDW
+ select SND_SOC_SDM660_CDC
+ select SND_SOC_MSM_HDMI_CODEC_RX
+ select QTI_PP
+ select DTS_SRS_TM
+ select DOLBY_LICENSE
+ select SND_HWDEP
+ select MSM_ULTRASOUND
+ select DTS_EAGLE
+ select SND_SOC_COMPRESS
+ select PINCTRL_LPI
+ help
+ To add support for SoC audio on MSM_INT.
+ This will enable sound soc drivers which
+ interfaces with DSP, also it will enable
+ the machine driver and the corresponding
+ DAI-links
+
+config SND_SOC_EXT_CODEC
+ tristate "SoC Machine driver for SDM660_EXT"
+ depends on ARCH_QCOM
+ select SND_SOC_QDSP6V2
+ select SND_SOC_MSM_STUB
+ select SND_SOC_MSM_HOSTLESS_PCM
+ select SND_DYNAMIC_MINORS
+ select MSM_QDSP6_APRV2_GLINK
+ select MSM_QDSP6_SSR
+ select MSM_QDSP6_PDR
+ select MSM_QDSP6_NOTIFIER
+ select MSM_QDSP6V2_CODECS
+ select SND_SOC_WCD9335
+ select SND_SOC_WCD934X
+ select SND_SOC_WSA881X
+ select SND_SOC_MSM_HDMI_CODEC_RX
+ select MFD_CORE
+ select QTI_PP
+ select DTS_SRS_TM
+ select DOLBY_LICENSE
+ select SND_SOC_CPE
+ select SND_SOC_WCD_CPE
+ select SND_HWDEP
+ select MSM_ULTRASOUND
+ select DTS_EAGLE
+ select SND_SOC_COMPRESS
+ select PINCTRL_LPI
+ help
+ To add support for SoC audio on MSM_EXT.
+ This will enable sound soc drivers which
+ interfaces with DSP, also it will enable
+ the machine driver and the corresponding
+ DAI-links
+
+config SND_SOC_MSM8996
+ tristate "SoC Machine driver for MSM8996 boards"
+ depends on ARCH_MSM8996 || MSM_GVM_QUIN
+ select SND_SOC_COMPRESS
+ select SND_SOC_QDSP6V2
+ select SND_SOC_MSM_STUB
+ select SND_SOC_MSM_HOSTLESS_PCM
+ select SND_DYNAMIC_MINORS
+ select MSM_QDSP6_APRV2
+ select MSM_QDSP6V2_CODECS
+ select SND_SOC_WCD9335
+ select SND_SOC_WSA881X
+ select SND_SOC_MSM_HDMI_CODEC_RX if ARCH_MSM8996
+ select DTS_SRS_TM
+ select QTI_PP
+ select QTI_PP_AUDIOSPHERE
+ select SND_SOC_CPE
+ select MSM_ULTRASOUND
+ select DOLBY_DS2
+ select SND_HWDEP
+ select DTS_EAGLE
+ help
+ To add support for SoC audio on MSM8996.
+ This will enable sound soc drivers which
+ interfaces with DSP, also it will enable
+ the machine driver and the corresponding
+ DAI-links
+
+config SND_SOC_MSM8996_VM
+ tristate "SoC Machine driver for MSM8996 virtualization"
+ select SND_SOC_QDSP6V2_VM
+ select SND_SOC_MSM_STUB
+ select SND_SOC_MSM_HOSTLESS_PCM
+ select SND_DYNAMIC_MINORS
+ select MSM_QDSP6_APRV2_VM
+ select QTI_PP
+ help
+ To add support for SoC audio on MSM8996
+ virtualization platform.
+ This will enable sound soc drivers which
+ interfaces with DSP using virtualized apr,
+ also it will enable the machine driver and
+ the corresponding DAI-links
+
+config SND_SOC_MSM8998
+ tristate "SoC Machine driver for MSM8998 boards"
+ depends on ARCH_QCOM
+ select SND_SOC_COMPRESS
+ select SND_SOC_QDSP6V2
+ select SND_SOC_MSM_STUB
+ select SND_SOC_MSM_HOSTLESS_PCM
+ select SND_DYNAMIC_MINORS
+ select MSM_QDSP6_APRV2_GLINK
+ select MSM_QDSP6_SSR
+ select MSM_QDSP6_PDR
+ select MSM_QDSP6_NOTIFIER
+ select MSM_QDSP6V2_CODECS
+ select SND_SOC_WCD9335
+ select SND_SOC_WCD934X
+ select SND_SOC_WSA881X
+ select SND_SOC_MSM_HDMI_CODEC_RX
+ select DTS_SRS_TM
+ select QTI_PP
+ select SND_SOC_CPE
+ select MSM_ULTRASOUND
+ select DOLBY_LICENSE
+ select SND_HWDEP
+ select DTS_EAGLE
+ help
+ To add support for SoC audio on MSM8998.
+ This will enable sound soc drivers which
+ interfaces with DSP, also it will enable
+ the machine driver and the corresponding
+ DAI-links
+
+config SND_SOC_SDM660_COMMON
+ tristate "SoC Machine driver for SDM660 boards"
+ depends on ARCH_SDM660
+ select SND_SOC_INT_CODEC
+ select SND_SOC_EXT_CODEC
+ help
+ To add support for SoC audio on SDM660.
+ This will enable sound soc drivers which
+ interfaces with DSP, also it will enable
+ the machine driver and the corresponding
+ DAI-links
+
+endmenu
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
new file mode 100644
index 000000000000..a1e33e0ee09f
--- /dev/null
+++ b/sound/soc/msm/Makefile
@@ -0,0 +1,39 @@
+# MSM Machine Support
+
+snd-soc-hostless-pcm-objs := msm-pcm-hostless.o
+obj-$(CONFIG_SND_SOC_MSM_HOSTLESS_PCM) += snd-soc-hostless-pcm.o
+
+obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += qdsp6v2/
+obj-$(CONFIG_SND_SOC_QDSP6V2_VM) += qdsp6v2/
+
+snd-soc-qdsp6v2-objs := msm-dai-fe.o
+obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
+obj-$(CONFIG_SND_SOC_QDSP6V2_VM) += snd-soc-qdsp6v2.o
+
+#for CPE drivers
+snd-soc-cpe-objs := msm-cpe-lsm.o
+obj-$(CONFIG_SND_SOC_CPE) += snd-soc-cpe.o
+
+# for MSM8996 sound card driver
+snd-soc-msm8996-objs := msm8996.o apq8096-auto.o
+obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-msm8996.o
+
+# for MSM8996 virtualization sound card driver
+snd-soc-msm8996-vm-objs := apq8096-auto.o
+obj-$(CONFIG_SND_SOC_MSM8996_VM) += snd-soc-msm8996-vm.o
+
+# for MSM8998 sound card driver
+snd-soc-msm8998-objs := msm8998.o
+obj-$(CONFIG_SND_SOC_MSM8998) += snd-soc-msm8998.o
+
+# for SDM660 sound card driver
+snd-soc-sdm660-common-objs := sdm660-common.o
+obj-$(CONFIG_SND_SOC_SDM660_COMMON) += snd-soc-sdm660-common.o
+
+# for SDM660 sound card driver
+snd-soc-int-codec-objs := sdm660-internal.o
+obj-$(CONFIG_SND_SOC_INT_CODEC) += snd-soc-int-codec.o
+
+# for SDM660 sound card driver
+snd-soc-ext-codec-objs := sdm660-external.o sdm660-ext-dai-links.o
+obj-$(CONFIG_SND_SOC_EXT_CODEC) += snd-soc-ext-codec.o
diff --git a/sound/soc/msm/apq8096-auto.c b/sound/soc/msm/apq8096-auto.c
new file mode 100644
index 000000000000..613ee45bdcec
--- /dev/null
+++ b/sound/soc/msm/apq8096-auto.c
@@ -0,0 +1,7761 @@
+/*
+ * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <linux/qdsp6v2/apr.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6core.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <device_event.h>
+#include <soc/qcom/boot_stats.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+
+#define DRV_NAME "apq8096-auto-asoc-snd"
+
+#define SAMPLING_RATE_8KHZ 8000
+#define SAMPLING_RATE_16KHZ 16000
+#define SAMPLING_RATE_32KHZ 32000
+#define SAMPLING_RATE_44P1KHZ 44100
+#define SAMPLING_RATE_48KHZ 48000
+#define SAMPLING_RATE_96KHZ 96000
+#define SAMPLING_RATE_192KHZ 192000
+#define SAMPLING_RATE_384KHZ 384000
+
+#define ADSP_STATE_READY_TIMEOUT_MS 10000
+
+static int hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_auxpcm_rate = SAMPLING_RATE_8KHZ;
+static int msm_hdmi_rx_ch = 2;
+static int msm_proxy_rx_ch = 2;
+static int hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int msm_sec_mi2s_tx_ch = 2;
+static int msm_sec_mi2s_rx_ch = 2;
+static int msm_tert_mi2s_tx_ch = 2;
+static int msm_quat_mi2s_rx_ch = 2;
+static int msm_sec_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_mi2s_rate = SAMPLING_RATE_48KHZ;
+
+/* TDM default channels */
+static int msm_pri_tdm_tx_0_ch = 2;
+static int msm_pri_tdm_tx_1_ch = 2;
+static int msm_pri_tdm_tx_2_ch = 2;
+static int msm_pri_tdm_tx_3_ch = 2;
+
+static int msm_pri_tdm_rx_0_ch = 2;
+static int msm_pri_tdm_rx_1_ch = 2;
+static int msm_pri_tdm_rx_2_ch = 2;
+static int msm_pri_tdm_rx_3_ch = 2;
+
+static int msm_sec_tdm_tx_0_ch = 2; /* STEREO MIC */
+static int msm_sec_tdm_tx_1_ch = 2;
+static int msm_sec_tdm_tx_2_ch = 2;
+static int msm_sec_tdm_tx_3_ch = 2;
+
+static int msm_sec_tdm_rx_0_ch = 6;
+static int msm_sec_tdm_rx_1_ch = 1;
+static int msm_sec_tdm_rx_2_ch = 1;
+static int msm_sec_tdm_rx_3_ch;
+
+static int msm_tert_tdm_rx_0_ch = 2; /* ICC STREAM */
+static int msm_tert_tdm_rx_1_ch = 2;
+static int msm_tert_tdm_rx_2_ch = 2;
+static int msm_tert_tdm_rx_3_ch = 2;
+static int msm_tert_tdm_rx_4_ch;
+
+static int msm_tert_tdm_tx_0_ch = 6; /* EC_REF1-EC_REF6(6 CHAN) */
+static int msm_tert_tdm_tx_1_ch = 1;
+static int msm_tert_tdm_tx_2_ch = 1;
+static int msm_tert_tdm_tx_3_ch;
+
+static int msm_quat_tdm_rx_0_ch = 6; /* ENT */
+static int msm_quat_tdm_rx_1_ch = 1; /* ANN */
+static int msm_quat_tdm_rx_2_ch = 1; /* TEL */
+static int msm_quat_tdm_rx_3_ch;
+
+static int msm_quat_tdm_tx_0_ch = 4; /*MIC1-MIC4*/
+static int msm_quat_tdm_tx_1_ch = 2; /*EC_REF(2 CHAN)*/
+static int msm_quat_tdm_tx_2_ch = 2; /*ENT RECORD*/
+static int msm_quat_tdm_tx_3_ch;
+
+/* TDM default bit format */
+static int msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_sec_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_pri_tdm_rate = SAMPLING_RATE_48KHZ;
+static int msm_pri_tdm_slot_width = 32;
+static int msm_pri_tdm_slot_num = 8;
+
+static int msm_sec_tdm_slot_width = 32;
+static int msm_sec_tdm_slot_num = 8;
+
+static int msm_tert_tdm_slot_width = 32;
+static int msm_tert_tdm_slot_num = 8;
+
+static int msm_quat_tdm_slot_width = 32;
+static int msm_quat_tdm_slot_num = 8;
+
+/* Group_MI2S default port channels */
+static int msm_sec_group_mi2s_rx_1_ch = 2;
+static int msm_sec_group_mi2s_rx_2_ch = 2;
+static int msm_sec_group_mi2s_rx_3_ch = 2;
+static int msm_sec_group_mi2s_rx_4_ch = 2;
+static int msm_sec_group_mi2s_tx_1_ch = 2;
+static int msm_sec_group_mi2s_tx_2_ch = 2;
+static int msm_sec_group_mi2s_tx_3_ch = 2;
+static int msm_sec_group_mi2s_tx_4_ch = 2;
+static int msm_tert_group_mi2s_rx_1_ch = 2;
+static int msm_tert_group_mi2s_rx_2_ch = 2;
+static int msm_tert_group_mi2s_rx_3_ch = 2;
+static int msm_tert_group_mi2s_rx_4_ch = 2;
+static int msm_tert_group_mi2s_tx_1_ch = 2;
+static int msm_tert_group_mi2s_tx_2_ch = 2;
+static int msm_tert_group_mi2s_tx_3_ch = 2;
+static int msm_tert_group_mi2s_tx_4_ch = 2;
+static int msm_quat_group_mi2s_rx_1_ch = 2;
+static int msm_quat_group_mi2s_rx_2_ch = 2;
+static int msm_quat_group_mi2s_rx_3_ch = 2;
+static int msm_quat_group_mi2s_rx_4_ch = 2;
+static int msm_quat_group_mi2s_tx_1_ch = 2;
+static int msm_quat_group_mi2s_tx_2_ch = 2;
+static int msm_quat_group_mi2s_tx_3_ch = 2;
+static int msm_quat_group_mi2s_tx_4_ch = 2;
+
+/* Group MI2S default sample rate*/
+static int msm_sec_group_mi2s_rate = SAMPLING_RATE_48KHZ;
+static int msm_tert_group_mi2s_rate = SAMPLING_RATE_48KHZ;
+static int msm_quat_group_mi2s_rate = SAMPLING_RATE_48KHZ;
+
+/* Group MI2S slot width bit format */
+static int msm_sec_group_mi2s_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+static int msm_tert_group_mi2s_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+static int msm_quat_group_mi2s_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+
+/* EC Reference default values are set in mixer_paths.xml */
+static int msm_ec_ref_ch = 4;
+static int msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ;
+
+static int msm_tdm_slot_width = 32;
+static int msm_tdm_num_slots = 8;
+
+static void *adsp_state_notifier;
+static bool dummy_device_registered;
+static struct snd_soc_card *sndcard;
+
+#define GROUP_MI2S_SLOT_OFFSET_MAX 8
+static unsigned int group_mi2s_slot_offset
+ [IDX_GROUP_MI2S_PORT_MAX][GROUP_MI2S_SLOT_OFFSET_MAX] = {
+ /* GROUP_SEC_MI2S_RX */
+ {0, 4, 0xFFFF},
+ {0, 4, 0xFFFF},
+ {0xFFFF},
+ {0xFFFF},
+ /* GROUP_SEC_MI2S_TX */
+ {0, 4, 0xFFFF},
+ {0, 4, 0xFFFF},
+ {0xFFFF},
+ {0xFFFF},
+ /* GROUP_TERT_MI2S_RX */
+ {0, 4, 0xFFFF},
+ {0, 4, 0xFFFF},
+ {0xFFFF},
+ {0xFFFF},
+ /* GROUP_TERT_MI2S_TX */
+ {0, 4, 0xFFFF},
+ {0, 4, 0xFFFF},
+ {0xFFFF},
+ {0xFFFF},
+ /* GROUP_QUAT_MI2S_RX */
+ {0, 4, 0xFFFF},
+ {0, 4, 0xFFFF},
+ {0, 4, 0xFFFF},
+ {0, 4, 0xFFFF},
+ /* GROUP_QUAT_MI2S_TX */
+ {0, 4, 0xFFFF},
+ {0, 4, 0xFFFF},
+ {0, 4, 0xFFFF},
+ {0, 4, 0xFFFF},
+};
+
+enum {
+ QUATERNARY_TDM_RX_0,
+ QUATERNARY_TDM_RX_1,
+ QUATERNARY_TDM_RX_2,
+ QUATERNARY_TDM_RX_3,
+ QUATERNARY_TDM_RX_4,
+ QUATERNARY_TDM_RX_5,
+ QUATERNARY_TDM_RX_6,
+ QUATERNARY_TDM_RX_7,
+ QUATERNARY_TDM_TX_0,
+ QUATERNARY_TDM_TX_1,
+ QUATERNARY_TDM_TX_2,
+ QUATERNARY_TDM_TX_3,
+ QUATERNARY_TDM_TX_4,
+ QUATERNARY_TDM_TX_5,
+ QUATERNARY_TDM_TX_6,
+ QUATERNARY_TDM_TX_7,
+ TERTIARY_TDM_RX_0,
+ TERTIARY_TDM_RX_1,
+ TERTIARY_TDM_RX_2,
+ TERTIARY_TDM_RX_3,
+ TERTIARY_TDM_RX_4,
+ TERTIARY_TDM_RX_5,
+ TERTIARY_TDM_RX_6,
+ TERTIARY_TDM_RX_7,
+ TERTIARY_TDM_TX_0,
+ TERTIARY_TDM_TX_1,
+ TERTIARY_TDM_TX_2,
+ TERTIARY_TDM_TX_3,
+ TERTIARY_TDM_TX_4,
+ TERTIARY_TDM_TX_5,
+ TERTIARY_TDM_TX_6,
+ TERTIARY_TDM_TX_7,
+ SECONDARY_TDM_RX_0,
+ SECONDARY_TDM_RX_1,
+ SECONDARY_TDM_RX_2,
+ SECONDARY_TDM_RX_3,
+ SECONDARY_TDM_RX_4,
+ SECONDARY_TDM_RX_5,
+ SECONDARY_TDM_RX_6,
+ SECONDARY_TDM_RX_7,
+ SECONDARY_TDM_TX_0,
+ SECONDARY_TDM_TX_1,
+ SECONDARY_TDM_TX_2,
+ SECONDARY_TDM_TX_3,
+ SECONDARY_TDM_TX_4,
+ SECONDARY_TDM_TX_5,
+ SECONDARY_TDM_TX_6,
+ SECONDARY_TDM_TX_7,
+ PRIMARY_TDM_RX_0,
+ PRIMARY_TDM_RX_1,
+ PRIMARY_TDM_RX_2,
+ PRIMARY_TDM_RX_3,
+ PRIMARY_TDM_RX_4,
+ PRIMARY_TDM_RX_5,
+ PRIMARY_TDM_RX_6,
+ PRIMARY_TDM_RX_7,
+ PRIMARY_TDM_TX_0,
+ PRIMARY_TDM_TX_1,
+ PRIMARY_TDM_TX_2,
+ PRIMARY_TDM_TX_3,
+ PRIMARY_TDM_TX_4,
+ PRIMARY_TDM_TX_5,
+ PRIMARY_TDM_TX_6,
+ PRIMARY_TDM_TX_7,
+ TDM_MAX,
+};
+
+#define TDM_SLOT_OFFSET_MAX 32
+/* TDM default offset */
+static unsigned int tdm_slot_offset[TDM_MAX][TDM_SLOT_OFFSET_MAX] = {
+ /* QUAT_TDM_RX */
+ {0, 4, 8, 12, 16, 20, 0xFFFF},
+ {24, 0xFFFF},
+ {28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* QUAT_TDM_TX */
+ {0, 4, 8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_RX */
+ {0, 4, 8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_TX */
+ {0, 4, 8, 12, 16, 20, 0xFFFF},
+ {24, 0xFFFF},
+ {28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_RX */
+ {0, 4, 8, 12, 16, 20, 0xFFFF},
+ {24, 0xFFFF},
+ {28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_TX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* PRI_TDM_RX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* PRI_TDM_TX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+};
+
+
+/***************************************************************************
+* Codec/Platform specific tdm slot offset table
+* NOTE:
+* each entry represents the slot offset array of one backend tdm device
+* valid offset represents the starting offset in byte for the channel
+* use 0xFFFF for end or unused slot offset entry
+***************************************************************************/
+static unsigned int tdm_slot_offset_adp_mmxf[TDM_MAX][TDM_SLOT_OFFSET_MAX] = {
+ /* QUAT_TDM_RX */
+ {2, 5, 8, 11, 14, 17, 20, 23, 0xFFFF},
+ {26, 0xFFFF},
+ {28, 0xFFFF},
+ {30, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* QUAT_TDM_TX */
+ {2, 0xFFFF},
+ {10, 0xFFFF},
+ {20, 22, 26, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_RX */
+ {2, 5, 8, 11, 14, 17, 20, 23, 0xFFFF},
+ {26, 0xFFFF},
+ {28, 0xFFFF},
+ {30, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_TX */
+ {2, 0xFFFF},
+ {10, 0xFFFF},
+ {20, 22, 26, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_RX */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_TX */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* PRI_TDM_RX */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* PRI_TDM_TX */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+};
+
+static unsigned int tdm_slot_offset_custom[TDM_MAX][TDM_SLOT_OFFSET_MAX] = {
+ /* QUAT_TDM_RX */
+ {0, 2, 0xFFFF},
+ {4, 6, 8, 10, 12, 14, 16, 18, 0xFFFF},
+ {20, 22, 24, 26, 28, 30, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* QUAT_TDM_TX */
+ {0, 2, 4, 6, 8, 10, 12, 0xFFFF},
+ {14, 16, 0xFFFF},
+ {18, 20, 22, 24, 26, 28, 30, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_RX */
+ {0, 2, 0xFFFF},
+ {4, 0xFFFF},
+ {6, 0xFFFF},
+ {8, 0xFFFF},
+ {10, 0xFFFF},
+ {12, 14, 16, 18, 20, 22, 24, 26, 0xFFFF},
+ {28, 30, 0xFFFF},
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_TX */
+ {0, 2, 0xFFFF},
+ {4, 6, 8, 10, 12, 14, 16, 18, 0xFFFF},
+ {20, 22, 24, 26, 28, 30, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_RX */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_TX */
+ {0xFFFF},
+ {0xFFFF},
+ {0xFFFF},
+ {0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* PRI_TDM_RX */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* PRI_TDM_TX */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+};
+
+static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five",
+ "Six", "Seven", "Eight"};
+
+static char const *hdmi_rx_bit_format_text[] = {"S16_LE", "S24_LE"};
+
+static const char *const proxy_rx_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight"};
+
+static char const *hdmi_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+ "KHZ_192"};
+
+static const char *const auxpcm_rate_text[] = {"8000", "16000"};
+
+static char const *tdm_ch_text[] = {
+ "One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight",
+ "Nine", "Ten", "Eleven", "Twelve",
+ "Thirteen", "Fourteen", "Fifteen", "Sixteen",
+ "Seventeen", "Eighteen", "Nineteen", "Twenty",
+ "TwentyOne", "TwentyTwo", "TwentyThree", "TwentyFour",
+ "TwentyFive", "TwentySix", "TwentySeven", "TwentyEight",
+ "TwentyNine", "Thirty", "ThirtyOne", "ThirtyTwo"
+};
+
+static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE"};
+
+static char const *mi2s_bit_format_text[] = {"S16_LE", "S24_LE"};
+
+static const char *const ec_ref_ch_text[] = {"Zero", "One", "Two", "Three",
+ "Four", "Five", "Six", "Seven", "Eight"};
+
+static char const *ec_ref_bit_format_text[] = {"0", "S16_LE", "S24_LE"};
+
+static const char *const ec_ref_rate_text[] = {"0", "8000", "16000",
+ "32000", "44100", "48000", "96000", "192000", "384000"};
+
+static const char *const mi2s_rate_text[] = {"32000", "44100", "48000"};
+
+static const char *const tdm_rate_text[] = {"8000", "16000", "48000"};
+
+static const char *const tdm_slot_num_text[] = {"One", "Two", "Four",
+ "Eight", "Sixteen", "ThirtyTwo"};
+
+
+static const char *const tdm_slot_width_text[] = {"16", "24", "32"};
+
+static struct afe_clk_set sec_mi2s_tx_clk = {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static struct afe_clk_set mi2s_tx_clk = {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT,
+ Q6AFE_LPASS_IBIT_CLK_DISABLE,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static struct afe_clk_set mi2s_rx_clk = {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static inline int param_is_mask(int p)
+{
+ return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p,
+ int n)
+{
+ return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
+{
+ if (bit >= SNDRV_MASK_MAX)
+ return;
+ if (param_is_mask(n)) {
+ struct snd_mask *m = param_to_mask(p, n);
+
+ m->bits[0] = 0;
+ m->bits[1] = 0;
+ m->bits[bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
+static int hdmi_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ switch (hdmi_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, hdmi_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int hdmi_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, hdmi_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_hdmi_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__,
+ msm_hdmi_rx_ch);
+ ucontrol->value.integer.value[0] = msm_hdmi_rx_ch - 2;
+
+ return 0;
+}
+
+static int msm_hdmi_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_hdmi_rx_ch = ucontrol->value.integer.value[0] + 2;
+ if (msm_hdmi_rx_ch > 8) {
+ pr_err("%s: channels %d exceeded 8.Limiting to max chs-8\n",
+ __func__, msm_hdmi_rx_ch);
+ msm_hdmi_rx_ch = 8;
+ }
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__, msm_hdmi_rx_ch);
+
+ return 1;
+}
+
+static int hdmi_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val = 0;
+
+ switch (hdmi_rx_sample_rate) {
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 2;
+ break;
+
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 1;
+ break;
+
+ case SAMPLING_RATE_48KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__,
+ hdmi_rx_sample_rate);
+
+ return 0;
+}
+
+static int hdmi_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: ucontrol value = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ hdmi_rx_sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 1:
+ hdmi_rx_sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 0:
+ default:
+ hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ;
+ }
+
+ pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__,
+ hdmi_rx_sample_rate);
+
+ return 0;
+}
+
+static int msm_auxpcm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_auxpcm_rate;
+ return 0;
+}
+
+static int msm_auxpcm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_auxpcm_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ msm_auxpcm_rate = SAMPLING_RATE_16KHZ;
+ break;
+ default:
+ msm_auxpcm_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ return 0;
+}
+
+static int msm_proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch);
+ ucontrol->value.integer.value[0] = msm_proxy_rx_ch - 1;
+ return 0;
+}
+
+static int msm_proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_proxy_rx_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch);
+ return 1;
+}
+
+static int msm_quat_mi2s_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_mi2s_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_mi2s_rx_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_mi2s_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_mi2s_rx_bit_format = %d\n",
+ __func__, msm_quat_mi2s_rx_bit_format);
+ return 0;
+}
+
+static int msm_tert_mi2s_tx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_mi2s_tx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_mi2s_tx_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_mi2s_tx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_mi2s_tx_bit_format = %d\n",
+ __func__, msm_tert_mi2s_tx_bit_format);
+ return 0;
+}
+
+static int msm_sec_mi2s_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_mi2s_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_mi2s_rx_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_mi2s_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_mi2s_rx_bit_format = %d\n",
+ __func__, msm_sec_mi2s_rx_bit_format);
+ return 0;
+}
+
+
+static int msm_sec_mi2s_tx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_mi2s_tx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_mi2s_tx_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_mi2s_tx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_mi2s_tx_bit_format = %d\n",
+ __func__, msm_sec_mi2s_tx_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_tx_0_ch = %d\n", __func__,
+ msm_pri_tdm_tx_0_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_tx_0_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_tx_0_ch = %d\n", __func__,
+ msm_pri_tdm_tx_0_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: pri_tdm_tx_1_ch = %d\n", __func__,
+ msm_pri_tdm_tx_1_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_tx_1_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_tx_1_ch = %d\n", __func__,
+ msm_pri_tdm_tx_1_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_tx_2_ch = %d\n", __func__,
+ msm_pri_tdm_tx_2_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_tx_2_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_tx_2_ch = %d\n", __func__,
+ msm_pri_tdm_tx_2_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_tx_3_ch = %d\n", __func__,
+ msm_pri_tdm_tx_3_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_tx_3_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_tx_3_ch = %d\n", __func__,
+ msm_pri_tdm_tx_3_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_rx_0_ch = %d\n", __func__,
+ msm_pri_tdm_rx_0_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_rx_0_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_rx_0_ch = %d\n", __func__,
+ msm_pri_tdm_rx_0_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_rx_1_ch = %d\n", __func__,
+ msm_pri_tdm_rx_1_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_rx_1_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_rx_1_ch = %d\n", __func__,
+ msm_pri_tdm_rx_1_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_rx_2_ch = %d\n", __func__,
+ msm_pri_tdm_rx_2_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_rx_2_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_rx_2_ch = %d\n", __func__,
+ msm_pri_tdm_rx_2_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_rx_3_ch = %d\n", __func__,
+ msm_pri_tdm_rx_3_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_rx_3_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_rx_3_ch = %d\n", __func__,
+ msm_pri_tdm_rx_3_ch);
+ return 0;
+}
+
+static int msm_sec_mi2s_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_sec_mi2s_rate;
+ pr_debug("%s: msm_sec_mi2s_rate = %d\n", __func__, msm_sec_mi2s_rate);
+ return 0;
+}
+
+static int msm_sec_mi2s_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_sec_mi2s_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 1:
+ msm_sec_mi2s_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 2:
+ msm_sec_mi2s_rate = SAMPLING_RATE_48KHZ;
+ break;
+ default:
+ msm_sec_mi2s_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ pr_debug("%s: msm_sec_mi2s_rate = %d\n",
+ __func__, msm_sec_mi2s_rate);
+ return 0;
+}
+
+static int msm_pri_tdm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_pri_tdm_rate;
+ pr_debug("%s: msm_pri_tdm_rate = %d\n", __func__, msm_pri_tdm_rate);
+ return 0;
+}
+
+static int msm_pri_tdm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_pri_tdm_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ msm_pri_tdm_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ msm_pri_tdm_rate = SAMPLING_RATE_48KHZ;
+ break;
+ default:
+ msm_pri_tdm_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rate = %d\n",
+ __func__, msm_pri_tdm_rate);
+ return 0;
+}
+
+static int msm_pri_tdm_slot_width_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_pri_tdm_slot_width;
+ pr_debug("%s: msm_pri_tdm_slot_width = %d\n",
+ __func__, msm_pri_tdm_slot_width);
+ return 0;
+}
+
+static int msm_pri_tdm_slot_width_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_pri_tdm_slot_width = 16;
+ break;
+ case 1:
+ msm_pri_tdm_slot_width = 24;
+ break;
+ case 2:
+ msm_pri_tdm_slot_width = 32;
+ break;
+ default:
+ msm_pri_tdm_slot_width = 32;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_slot_width= %d\n",
+ __func__, msm_pri_tdm_slot_width);
+ return 0;
+}
+
+static int msm_pri_tdm_slot_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_slot_num) {
+ case 1:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ case 2:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case 4:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case 8:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case 16:
+ ucontrol->value.integer.value[0] = 4;
+ break;
+ case 32:
+ default:
+ ucontrol->value.integer.value[0] = 5;
+ break;
+ }
+
+ pr_debug("%s: msm_pri_tdm_slot_num = %d\n",
+ __func__, msm_pri_tdm_slot_num);
+ return 0;
+}
+
+static int msm_pri_tdm_slot_num_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_pri_tdm_slot_num = 1;
+ break;
+ case 1:
+ msm_pri_tdm_slot_num = 2;
+ break;
+ case 2:
+ msm_pri_tdm_slot_num = 4;
+ break;
+ case 3:
+ msm_pri_tdm_slot_num = 8;
+ break;
+ case 4:
+ msm_pri_tdm_slot_num = 16;
+ break;
+ case 5:
+ msm_pri_tdm_slot_num = 32;
+ break;
+ default:
+ msm_pri_tdm_slot_num = 8;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_slot_num = %d\n",
+ __func__, msm_pri_tdm_slot_num);
+ return 0;
+}
+
+static int msm_sec_tdm_slot_width_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_sec_tdm_slot_width;
+ pr_debug("%s: msm_sec_tdm_slot_width = %d\n",
+ __func__, msm_sec_tdm_slot_width);
+ return 0;
+}
+
+static int msm_sec_tdm_slot_width_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_sec_tdm_slot_width = 16;
+ break;
+ case 1:
+ msm_sec_tdm_slot_width = 24;
+ break;
+ case 2:
+ msm_sec_tdm_slot_width = 32;
+ break;
+ default:
+ msm_sec_tdm_slot_width = 32;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_slot_width= %d\n",
+ __func__, msm_sec_tdm_slot_width);
+ return 0;
+}
+
+static int msm_sec_tdm_slot_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_slot_num) {
+ case 1:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ case 2:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case 4:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case 8:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case 16:
+ ucontrol->value.integer.value[0] = 4;
+ break;
+ case 32:
+ default:
+ ucontrol->value.integer.value[0] = 5;
+ break;
+ }
+
+ pr_debug("%s: msm_sec_tdm_slot_num = %d\n",
+ __func__, msm_sec_tdm_slot_num);
+ return 0;
+}
+
+static int msm_sec_tdm_slot_num_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_sec_tdm_slot_num = 1;
+ break;
+ case 1:
+ msm_sec_tdm_slot_num = 2;
+ break;
+ case 2:
+ msm_sec_tdm_slot_num = 4;
+ break;
+ case 3:
+ msm_sec_tdm_slot_num = 8;
+ break;
+ case 4:
+ msm_sec_tdm_slot_num = 16;
+ break;
+ case 5:
+ msm_sec_tdm_slot_num = 32;
+ break;
+ default:
+ msm_sec_tdm_slot_num = 8;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_slot_num = %d\n",
+ __func__, msm_sec_tdm_slot_num);
+ return 0;
+}
+
+static int msm_tert_tdm_slot_width_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_tert_tdm_slot_width;
+ pr_debug("%s: msm_tert_tdm_slot_width = %d\n",
+ __func__, msm_tert_tdm_slot_width);
+ return 0;
+}
+
+static int msm_tert_tdm_slot_width_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_tert_tdm_slot_width = 16;
+ break;
+ case 1:
+ msm_tert_tdm_slot_width = 24;
+ break;
+ case 2:
+ msm_tert_tdm_slot_width = 32;
+ break;
+ default:
+ msm_tert_tdm_slot_width = 32;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_slot_width= %d\n",
+ __func__, msm_tert_tdm_slot_width);
+ return 0;
+}
+
+static int msm_tert_tdm_slot_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_slot_num) {
+ case 1:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ case 2:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case 4:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case 8:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case 16:
+ ucontrol->value.integer.value[0] = 4;
+ break;
+ case 32:
+ default:
+ ucontrol->value.integer.value[0] = 5;
+ break;
+ }
+
+ pr_debug("%s: msm_tert_tdm_slot_num = %d\n",
+ __func__, msm_tert_tdm_slot_num);
+ return 0;
+}
+
+static int msm_tert_tdm_slot_num_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_tert_tdm_slot_num = 1;
+ break;
+ case 1:
+ msm_tert_tdm_slot_num = 2;
+ break;
+ case 2:
+ msm_tert_tdm_slot_num = 4;
+ break;
+ case 3:
+ msm_tert_tdm_slot_num = 8;
+ break;
+ case 4:
+ msm_tert_tdm_slot_num = 16;
+ break;
+ case 5:
+ msm_tert_tdm_slot_num = 32;
+ break;
+ default:
+ msm_tert_tdm_slot_num = 8;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_slot_num = %d\n",
+ __func__, msm_tert_tdm_slot_num);
+ return 0;
+}
+
+static int msm_quat_tdm_slot_width_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_quat_tdm_slot_width;
+ pr_debug("%s: msm_quat_tdm_slot_width = %d\n",
+ __func__, msm_quat_tdm_slot_width);
+ return 0;
+}
+
+static int msm_quat_tdm_slot_width_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_quat_tdm_slot_width = 16;
+ break;
+ case 1:
+ msm_quat_tdm_slot_width = 24;
+ break;
+ case 2:
+ msm_quat_tdm_slot_width = 32;
+ break;
+ default:
+ msm_quat_tdm_slot_width = 32;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_slot_width= %d\n",
+ __func__, msm_quat_tdm_slot_width);
+ return 0;
+}
+
+static int msm_quat_tdm_slot_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_slot_num) {
+ case 1:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ case 2:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case 4:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case 8:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case 16:
+ ucontrol->value.integer.value[0] = 4;
+ break;
+ case 32:
+ default:
+ ucontrol->value.integer.value[0] = 5;
+ break;
+ }
+
+ pr_debug("%s: msm_quat_tdm_slot_num = %d\n",
+ __func__, msm_quat_tdm_slot_num);
+ return 0;
+}
+
+static int msm_quat_tdm_slot_num_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_quat_tdm_slot_num = 1;
+ break;
+ case 1:
+ msm_quat_tdm_slot_num = 2;
+ break;
+ case 2:
+ msm_quat_tdm_slot_num = 4;
+ break;
+ case 3:
+ msm_quat_tdm_slot_num = 8;
+ break;
+ case 4:
+ msm_quat_tdm_slot_num = 16;
+ break;
+ case 5:
+ msm_quat_tdm_slot_num = 32;
+ break;
+ default:
+ msm_quat_tdm_slot_num = 8;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_slot_num = %d\n",
+ __func__, msm_quat_tdm_slot_num);
+ return 0;
+}
+
+static int msm_tdm_slot_mapping_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_multi_mixer_control *mc =
+ (struct soc_multi_mixer_control *)kcontrol->private_value;
+ unsigned int *slot_offset;
+ int i;
+
+ if (mc->shift >= TDM_MAX) {
+ pr_err("%s invalid port index %d\n", __func__, mc->shift);
+ return -EINVAL;
+ }
+
+ slot_offset = tdm_slot_offset[mc->shift];
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ ucontrol->value.integer.value[i] = slot_offset[i];
+ pr_debug("%s port index %d offset %d value %d\n",
+ __func__, mc->shift, i, slot_offset[i]);
+ }
+
+ return 0;
+}
+
+static int msm_tdm_slot_mapping_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_multi_mixer_control *mc =
+ (struct soc_multi_mixer_control *)kcontrol->private_value;
+ unsigned int *slot_offset;
+ int i;
+
+ if (mc->shift >= TDM_MAX) {
+ pr_err("%s invalid port index %d\n", __func__, mc->shift);
+ return -EINVAL;
+ }
+
+ slot_offset = tdm_slot_offset[mc->shift];
+
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ slot_offset[i] = ucontrol->value.integer.value[i];
+ pr_debug("%s port index %d offset %d value %d\n",
+ __func__, mc->shift, i, slot_offset[i]);
+ }
+
+ return 0;
+}
+
+static int msm_sec_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_rx_0_ch = %d\n", __func__,
+ msm_sec_tdm_rx_0_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_rx_0_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_rx_0_ch = %d\n", __func__,
+ msm_sec_tdm_rx_0_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_rx_1_ch = %d\n", __func__,
+ msm_sec_tdm_rx_1_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_rx_1_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_rx_1_ch = %d\n", __func__,
+ msm_sec_tdm_rx_1_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_rx_2_ch = %d\n", __func__,
+ msm_sec_tdm_rx_2_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_rx_2_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_rx_2_ch = %d\n", __func__,
+ msm_sec_tdm_rx_2_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_rx_3_ch = %d\n", __func__,
+ msm_sec_tdm_rx_3_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_rx_3_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_rx_3_ch = %d\n", __func__,
+ msm_sec_tdm_rx_3_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_0_ch = %d\n", __func__,
+ msm_sec_tdm_tx_0_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_0_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_0_ch = %d\n", __func__,
+ msm_sec_tdm_tx_0_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_1_ch = %d\n", __func__,
+ msm_sec_tdm_tx_1_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_1_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_1_ch = %d\n", __func__,
+ msm_sec_tdm_tx_1_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_2_ch = %d\n", __func__,
+ msm_sec_tdm_tx_2_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_2_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_2_ch = %d\n", __func__,
+ msm_sec_tdm_tx_2_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_3_ch = %d\n", __func__,
+ msm_sec_tdm_tx_3_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_3_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_3_ch = %d\n", __func__,
+ msm_sec_tdm_tx_3_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_0_ch = %d\n", __func__,
+ msm_tert_tdm_rx_0_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_0_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_0_ch = %d\n", __func__,
+ msm_tert_tdm_rx_0_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_1_ch = %d\n", __func__,
+ msm_tert_tdm_rx_1_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_1_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_1_ch = %d\n", __func__,
+ msm_tert_tdm_rx_1_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_2_ch = %d\n", __func__,
+ msm_tert_tdm_rx_2_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_2_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_2_ch = %d\n", __func__,
+ msm_tert_tdm_rx_2_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_3_ch = %d\n", __func__,
+ msm_tert_tdm_rx_3_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_3_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_3_ch = %d\n", __func__,
+ msm_tert_tdm_rx_3_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_4_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_4_ch = %d\n", __func__,
+ msm_tert_tdm_rx_4_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_4_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_4_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_4_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_4_ch = %d\n", __func__,
+ msm_tert_tdm_rx_4_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_0_ch = %d\n", __func__,
+ msm_tert_tdm_tx_0_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_0_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_0_ch = %d\n", __func__,
+ msm_tert_tdm_tx_0_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_1_ch = %d\n", __func__,
+ msm_tert_tdm_tx_1_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_1_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_1_ch = %d\n", __func__,
+ msm_tert_tdm_tx_1_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_2_ch = %d\n", __func__,
+ msm_tert_tdm_tx_2_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_2_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_2_ch = %d\n", __func__,
+ msm_tert_tdm_tx_2_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_3_ch = %d\n", __func__,
+ msm_tert_tdm_tx_3_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_3_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_3_ch = %d\n", __func__,
+ msm_tert_tdm_tx_3_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_0_ch = %d\n", __func__,
+ msm_quat_tdm_rx_0_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_0_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_0_ch = %d\n", __func__,
+ msm_quat_tdm_rx_0_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_1_ch = %d\n", __func__,
+ msm_quat_tdm_rx_1_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_1_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_1_ch = %d\n", __func__,
+ msm_quat_tdm_rx_1_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_2_ch = %d\n", __func__,
+ msm_quat_tdm_rx_2_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_2_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_2_ch = %d\n", __func__,
+ msm_quat_tdm_rx_2_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_3_ch = %d\n", __func__,
+ msm_quat_tdm_rx_3_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_3_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_3_ch = %d\n", __func__,
+ msm_quat_tdm_rx_3_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_0_ch = %d\n", __func__,
+ msm_quat_tdm_tx_0_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_0_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_0_ch = %d\n", __func__,
+ msm_quat_tdm_tx_0_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_1_ch = %d\n", __func__,
+ msm_quat_tdm_tx_1_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_1_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_1_ch = %d\n", __func__,
+ msm_quat_tdm_tx_1_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_2_ch = %d\n", __func__,
+ msm_quat_tdm_tx_2_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_2_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_2_ch = %d\n", __func__,
+ msm_quat_tdm_tx_2_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_3_ch = %d\n", __func__,
+ msm_quat_tdm_tx_3_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_3_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_3_ch = %d\n", __func__,
+ msm_quat_tdm_tx_3_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_tx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_0_bit_format = %d\n",
+ __func__, msm_pri_tdm_tx_0_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_tx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_1_bit_format = %d\n",
+ __func__, msm_pri_tdm_tx_1_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_tx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_2_bit_format = %d\n",
+ __func__, msm_pri_tdm_tx_2_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_tx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_3_bit_format = %d\n",
+ __func__, msm_pri_tdm_tx_3_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_rx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_0_bit_format = %d\n",
+ __func__, msm_pri_tdm_rx_0_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_rx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_1_bit_format = %d\n",
+ __func__, msm_pri_tdm_rx_1_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_rx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_2_bit_format = %d\n",
+ __func__, msm_pri_tdm_rx_2_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_rx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_3_bit_format = %d\n",
+ __func__, msm_pri_tdm_rx_3_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_rx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_0_bit_format = %d\n",
+ __func__, msm_sec_tdm_rx_0_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_rx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_1_bit_format = %d\n",
+ __func__, msm_sec_tdm_rx_1_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_rx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_2_bit_format = %d\n",
+ __func__, msm_sec_tdm_rx_2_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_rx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_3_bit_format = %d\n",
+ __func__, msm_sec_tdm_rx_3_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_0_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_0_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_1_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_1_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_2_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_2_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_3_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_3_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_0_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_0_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_1_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_1_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_2_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_2_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_3_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_3_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_4_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_4_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_4_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_4_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_4_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_4_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_0_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_0_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_1_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_1_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_2_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_2_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_3_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_3_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_0_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_0_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_1_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_1_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_2_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_2_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_3_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_3_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_0_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_0_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_1_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_1_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_2_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_2_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_3_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_3_bit_format);
+ return 0;
+}
+
+static int msm_ec_ref_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_ec_ref_ch;
+ pr_debug("%s: msm_ec_ref_ch = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_ec_ref_ch = ucontrol->value.integer.value[0];
+ pr_debug("%s: msm_ec_ref_ch = %d\n", __func__, msm_ec_ref_ch);
+ adm_num_ec_ref_rx_chans(msm_ec_ref_ch);
+ return 0;
+}
+
+static int msm_ec_ref_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_ec_ref_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_ec_ref_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 1:
+ msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ default:
+ msm_ec_ref_bit_format = 0;
+ break;
+ }
+ pr_debug("%s: msm_ec_ref_bit_format = %d\n",
+ __func__, msm_ec_ref_bit_format);
+ adm_ec_ref_rx_bit_width(msm_ec_ref_bit_format);
+ return 0;
+}
+
+static int msm_ec_ref_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_ec_ref_sampling_rate;
+ pr_debug("%s: msm_ec_ref_sampling_rate = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_ec_ref_sampling_rate = 0;
+ break;
+ case 1:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 2:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 3:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 4:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 5:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 6:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 7:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 8:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_384KHZ;
+ break;
+ default:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ pr_debug("%s: msm_ec_ref_sampling_rate = %d\n",
+ __func__, msm_ec_ref_sampling_rate);
+ adm_ec_ref_rx_sampling_rate(msm_ec_ref_sampling_rate);
+ return 0;
+}
+
+static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = msm_auxpcm_rate;
+ channels->min = channels->max = 1;
+
+ return 0;
+}
+
+static int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s: msm_proxy_rx_ch =%d\n", __func__, msm_proxy_rx_ch);
+
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
+ channels->min = channels->max = msm_proxy_rx_ch;
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ return 0;
+}
+
+static int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ return 0;
+}
+
+static int msm_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
+ channels->min, channels->max);
+
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ hdmi_rx_bit_format);
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
+ rate->min = rate->max = hdmi_rx_sample_rate;
+ channels->min = channels->max = msm_hdmi_rx_ch;
+
+ return 0;
+}
+
+static int msm_mi2s_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+
+ switch (cpu_dai->id) {
+ case 1: /*MSM_SEC_MI2S*/
+ pr_debug("%s: channel:%d\n", __func__, msm_sec_mi2s_rx_ch);
+ rate->min = rate->max = msm_sec_mi2s_rate;
+ channels->min = channels->max = msm_sec_mi2s_rx_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_mi2s_rx_bit_format);
+ break;
+ case 3: /*MSM_QUAT_MI2S*/
+ pr_debug("%s: channel:%d\n", __func__, msm_quat_mi2s_rx_ch);
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ channels->min = channels->max = msm_quat_mi2s_rx_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_mi2s_rx_bit_format);
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n",
+ __func__, cpu_dai->id, channels->max, rate->max,
+ params_format(params));
+
+ return 0;
+}
+
+static int msm_mi2s_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+
+ switch (cpu_dai->id) {
+ case 0: /*MSM_PRIM_MI2S*/
+ break;
+ case 1: /*MSM_SEC_MI2S*/
+ pr_debug("%s: channel:%d\n", __func__, msm_sec_mi2s_tx_ch);
+ rate->min = rate->max = msm_sec_mi2s_rate;
+ channels->min = channels->max = msm_sec_mi2s_tx_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_mi2s_tx_bit_format);
+ break;
+ case 2: /*MSM_TERT_MI2S*/
+ pr_debug("%s: channel:%d\n", __func__, msm_tert_mi2s_tx_ch);
+ channels->min = channels->max = msm_tert_mi2s_tx_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_mi2s_tx_bit_format);
+ break;
+ case 3: /*MSM_QUAT_MI2S*/
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n",
+ __func__, cpu_dai->id, channels->max, rate->max,
+ params_format(params));
+
+ return 0;
+}
+
+static int msm_group_mi2s_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_interval *rate =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ channels->min = channels->max = msm_sec_group_mi2s_rx_1_ch;
+ rate->min = rate->max = msm_sec_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ channels->min = channels->max = msm_sec_group_mi2s_rx_2_ch;
+ rate->min = rate->max = msm_sec_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ channels->min = channels->max = msm_sec_group_mi2s_rx_3_ch;
+ rate->min = rate->max = msm_sec_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ channels->min = channels->max = msm_sec_group_mi2s_rx_4_ch;
+ rate->min = rate->max = msm_sec_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ channels->min = channels->max = msm_sec_group_mi2s_tx_1_ch;
+ rate->min = rate->max = msm_sec_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ channels->min = channels->max = msm_sec_group_mi2s_tx_2_ch;
+ rate->min = rate->max = msm_sec_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ channels->min = channels->max = msm_sec_group_mi2s_tx_3_ch;
+ rate->min = rate->max = msm_sec_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ channels->min = channels->max = msm_sec_group_mi2s_tx_4_ch;
+ rate->min = rate->max = msm_sec_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ channels->min = channels->max = msm_tert_group_mi2s_rx_1_ch;
+ rate->min = rate->max = msm_tert_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ channels->min = channels->max = msm_tert_group_mi2s_rx_2_ch;
+ rate->min = rate->max = msm_tert_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ channels->min = channels->max = msm_tert_group_mi2s_rx_3_ch;
+ rate->min = rate->max = msm_tert_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ channels->min = channels->max = msm_tert_group_mi2s_rx_4_ch;
+ rate->min = rate->max = msm_tert_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ channels->min = channels->max = msm_tert_group_mi2s_tx_1_ch;
+ rate->min = rate->max = msm_tert_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ channels->min = channels->max = msm_tert_group_mi2s_tx_2_ch;
+ rate->min = rate->max = msm_tert_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ channels->min = channels->max = msm_tert_group_mi2s_tx_3_ch;
+ rate->min = rate->max = msm_tert_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ channels->min = channels->max = msm_tert_group_mi2s_tx_4_ch;
+ rate->min = rate->max = msm_tert_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ channels->min = channels->max = msm_quat_group_mi2s_rx_1_ch;
+ rate->min = rate->max = msm_quat_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ channels->min = channels->max = msm_quat_group_mi2s_rx_2_ch;
+ rate->min = rate->max = msm_quat_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ channels->min = channels->max = msm_quat_group_mi2s_rx_3_ch;
+ rate->min = rate->max = msm_quat_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ channels->min = channels->max = msm_quat_group_mi2s_rx_4_ch;
+ rate->min = rate->max = msm_quat_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ channels->min = channels->max = msm_quat_group_mi2s_tx_1_ch;
+ rate->min = rate->max = msm_quat_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ channels->min = channels->max = msm_quat_group_mi2s_tx_2_ch;
+ rate->min = rate->max = msm_quat_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ channels->min = channels->max = msm_quat_group_mi2s_tx_3_ch;
+ rate->min = rate->max = msm_quat_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_group_mi2s_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ channels->min = channels->max = msm_quat_group_mi2s_tx_4_ch;
+ rate->min = rate->max = msm_quat_group_mi2s_rate;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_group_mi2s_bit_format);
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n",
+ __func__, cpu_dai->id, channels->max, rate->max,
+ params_format(params));
+
+ return 0;
+}
+
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ pr_debug("%s:\n", __func__);
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ return 0;
+}
+
+static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ channels->min = channels->max = msm_pri_tdm_tx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_tx_0_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ channels->min = channels->max = msm_pri_tdm_tx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_tx_1_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ channels->min = channels->max = msm_pri_tdm_tx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_tx_2_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ channels->min = channels->max = msm_pri_tdm_tx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_tx_3_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ channels->min = channels->max = msm_pri_tdm_rx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_rx_0_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ channels->min = channels->max = msm_pri_tdm_rx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_rx_1_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ channels->min = channels->max = msm_pri_tdm_rx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_rx_2_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ channels->min = channels->max = msm_pri_tdm_rx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_rx_3_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ channels->min = channels->max = msm_sec_tdm_rx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_rx_0_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ channels->min = channels->max = msm_sec_tdm_rx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_rx_1_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ channels->min = channels->max = msm_sec_tdm_rx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_rx_2_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ channels->min = channels->max = msm_sec_tdm_rx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_rx_3_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ channels->min = channels->max = msm_sec_tdm_tx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_0_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ channels->min = channels->max = msm_sec_tdm_tx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_1_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ channels->min = channels->max = msm_sec_tdm_tx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_2_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ channels->min = channels->max = msm_sec_tdm_tx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_3_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ channels->min = channels->max = msm_tert_tdm_rx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_0_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ channels->min = channels->max = msm_tert_tdm_rx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_1_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ channels->min = channels->max = msm_tert_tdm_rx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_2_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ channels->min = channels->max = msm_tert_tdm_rx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_3_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ channels->min = channels->max = msm_tert_tdm_rx_4_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_4_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ channels->min = channels->max = msm_tert_tdm_tx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_0_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ channels->min = channels->max = msm_tert_tdm_tx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_1_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ channels->min = channels->max = msm_tert_tdm_tx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_2_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ channels->min = channels->max = msm_tert_tdm_tx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_3_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ channels->min = channels->max = msm_quat_tdm_rx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_0_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ channels->min = channels->max = msm_quat_tdm_rx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_1_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ channels->min = channels->max = msm_quat_tdm_rx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_2_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ channels->min = channels->max = msm_quat_tdm_rx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_3_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ channels->min = channels->max = msm_quat_tdm_tx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_0_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ channels->min = channels->max = msm_quat_tdm_tx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_1_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ channels->min = channels->max = msm_quat_tdm_tx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_2_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ channels->min = channels->max = msm_quat_tdm_tx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_3_bit_format);
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n",
+ __func__, cpu_dai->id, channels->max, rate->max,
+ params_format(params));
+
+ return 0;
+}
+
+static int apq8096_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ pr_debug("%s: substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ switch (cpu_dai->id) {
+ case 0: /*MSM_PRIM_MI2S*/
+ break;
+ case 1: /*MSM_SEC_MI2S*/
+ sec_mi2s_tx_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_SECONDARY_MI2S_TX,
+ &sec_mi2s_tx_clk);
+ if (ret < 0) {
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ pr_err("%s: set fmt cpu dai failed, err:%d\n",
+ __func__, ret);
+ break;
+ case 2: /*MSM_TERT_MI2S*/
+ mi2s_tx_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX,
+ &mi2s_tx_clk);
+ if (ret < 0) {
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ pr_err("%s: set fmt cpu dai failed, err:%d\n",
+ __func__, ret);
+ break;
+ case 3: /*MSM_QUAT_MI2S*/
+ mi2s_rx_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_QUATERNARY_MI2S_RX,
+ &mi2s_rx_clk);
+ if (ret < 0) {
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ pr_err("%s: set fmt cpu dai failed, err:%d\n",
+ __func__, ret);
+ break;
+ default:
+ pr_err("%s: invalid cpu_dai id 0x%x\n", __func__, cpu_dai->id);
+ break;
+ }
+
+err:
+ return ret;
+}
+
+static void apq8096_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ pr_debug("%s: substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ switch (cpu_dai->id) {
+ case 0: /*MSM_PRIM_MI2S*/
+ break;
+ case 1: /*MSM_SEC_MI2S*/
+ sec_mi2s_tx_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_SECONDARY_MI2S_TX,
+ &sec_mi2s_tx_clk);
+ if (ret < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ break;
+ case 2: /*MSM_TERT_MI2S*/
+ mi2s_tx_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX,
+ &mi2s_tx_clk);
+ if (ret < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ break;
+ case 3: /*MSM_QUAT_MI2S*/
+ mi2s_rx_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_QUATERNARY_MI2S_RX,
+ &mi2s_rx_clk);
+ if (ret < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ break;
+ default:
+ pr_err("%s: invalid cpu_dai id 0x%x\n", __func__, cpu_dai->id);
+ break;
+ }
+}
+
+static struct snd_soc_ops apq8096_mi2s_be_ops = {
+ .startup = apq8096_mi2s_snd_startup,
+ .shutdown = apq8096_mi2s_snd_shutdown,
+};
+
+static unsigned int tdm_param_set_slot_mask(int slots)
+{
+ unsigned int slot_mask = 0;
+ unsigned int i = 0;
+
+ if ((slots <= 0) || (slots > 32)) {
+ pr_err("%s: invalid slot number %d\n", __func__, slots);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < slots ; i++)
+ slot_mask |= 1 << i;
+ return slot_mask;
+}
+
+static int apq8096_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ int channels, slot_width, slots, rate;
+ unsigned int slot_mask;
+ unsigned int *slot_offset;
+ int offset_channels = 0;
+ int i;
+ int clk_freq;
+
+ pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+ rate = params_rate(params);
+ channels = params_channels(params);
+ if (channels < 1 || channels > 32) {
+ pr_err("%s: invalid param channels %d\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /*
+ * up to 8 channel HW configuration should
+ * use 32 bit slot width for max support of
+ * stream bit width. (slot_width > bit_width)
+ */
+ slot_width = msm_tdm_slot_width;
+ break;
+ default:
+ pr_err("%s: invalid param format 0x%x\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ slots = msm_tdm_num_slots;
+
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_0];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_1];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_2];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_3];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_4];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_5];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_6];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_7];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_0];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_1];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_2];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_3];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_4];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_5];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_6];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_7];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_0];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_1];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_2];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_3];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_4];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_5];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_6];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_7];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_0];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_1];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_2];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_3];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_4];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_5];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_6];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_7];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_0];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_1];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_2];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_3];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_4];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_5];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_6];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_7];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_0];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_1];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_2];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_3];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_4];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_5];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_6];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_7];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_0];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_1];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_2];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_3];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_4];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_5];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_6];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_7];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_0];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_1];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_2];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_3];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_4];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_5];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_6];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_7];
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ offset_channels++;
+ else
+ break;
+ }
+
+ if (offset_channels == 0) {
+ pr_err("%s: slot offset not supported, offset_channels %d\n",
+ __func__, offset_channels);
+ return -EINVAL;
+ }
+
+ if (channels > offset_channels) {
+ pr_err("%s: channels %d exceed offset_channels %d\n",
+ __func__, channels, offset_channels);
+ return -EINVAL;
+ }
+
+ slot_mask = tdm_param_set_slot_mask(slots);
+ if (!slot_mask) {
+ pr_err("%s: invalid slot_mask 0x%x\n",
+ __func__, slot_mask);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ 0, NULL, channels, slot_offset);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ channels, slot_offset, 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+
+ clk_freq = rate * slot_width * slots;
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm clk, err:%d\n",
+ __func__, ret);
+ }
+
+end:
+ return ret;
+}
+
+static struct snd_soc_ops apq8096_tdm_be_ops = {
+ .hw_params = apq8096_tdm_snd_hw_params,
+};
+
+static int apq8096_group_mi2s_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ int channels, rate;
+ unsigned int *slot_offset;
+ int offset_channels = 0;
+ int i;
+
+ rate = params_rate(params);
+ channels = params_channels(params);
+
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ slot_offset = group_mi2s_slot_offset[IDX_SECONDARY_MI2S_RX_1];
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ slot_offset = group_mi2s_slot_offset[IDX_SECONDARY_MI2S_RX_2];
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ slot_offset = group_mi2s_slot_offset[IDX_SECONDARY_MI2S_RX_3];
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ slot_offset = group_mi2s_slot_offset[IDX_SECONDARY_MI2S_RX_4];
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ slot_offset = group_mi2s_slot_offset[IDX_SECONDARY_MI2S_TX_1];
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ slot_offset = group_mi2s_slot_offset[IDX_SECONDARY_MI2S_TX_2];
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ slot_offset = group_mi2s_slot_offset[IDX_SECONDARY_MI2S_TX_3];
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ slot_offset = group_mi2s_slot_offset[IDX_SECONDARY_MI2S_TX_4];
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ slot_offset = group_mi2s_slot_offset[IDX_TERTIARY_MI2S_RX_1];
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ slot_offset = group_mi2s_slot_offset[IDX_TERTIARY_MI2S_RX_2];
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ slot_offset = group_mi2s_slot_offset[IDX_TERTIARY_MI2S_RX_3];
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ slot_offset = group_mi2s_slot_offset[IDX_TERTIARY_MI2S_RX_4];
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ slot_offset = group_mi2s_slot_offset[IDX_TERTIARY_MI2S_TX_1];
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ slot_offset = group_mi2s_slot_offset[IDX_TERTIARY_MI2S_TX_2];
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ slot_offset = group_mi2s_slot_offset[IDX_TERTIARY_MI2S_TX_3];
+ break;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ slot_offset = group_mi2s_slot_offset[IDX_TERTIARY_MI2S_TX_4];
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ slot_offset = group_mi2s_slot_offset[IDX_QUATERNARY_MI2S_RX_1];
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ slot_offset = group_mi2s_slot_offset[IDX_QUATERNARY_MI2S_RX_2];
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ slot_offset = group_mi2s_slot_offset[IDX_QUATERNARY_MI2S_RX_3];
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ slot_offset = group_mi2s_slot_offset[IDX_QUATERNARY_MI2S_RX_4];
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ slot_offset = group_mi2s_slot_offset[IDX_QUATERNARY_MI2S_TX_1];
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ slot_offset = group_mi2s_slot_offset[IDX_QUATERNARY_MI2S_TX_2];
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ slot_offset = group_mi2s_slot_offset[IDX_QUATERNARY_MI2S_TX_3];
+ break;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ slot_offset = group_mi2s_slot_offset[IDX_QUATERNARY_MI2S_TX_4];
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < GROUP_MI2S_SLOT_OFFSET_MAX; i++) {
+ if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ offset_channels++;
+ }
+
+ if (offset_channels == 0) {
+ pr_err("%s: slot offset not supported, offset_channels %d\n",
+ __func__, offset_channels);
+ return -EINVAL;
+ }
+
+ if (channels > offset_channels) {
+ pr_err("%s: channels %d and offset_channels %d not match\n",
+ __func__, channels, offset_channels);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ channels, slot_offset);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ channels, slot_offset,
+ 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+end:
+ return ret;
+}
+
+static struct snd_soc_ops apq8096_group_mi2s_be_ops = {
+ .hw_params = apq8096_group_mi2s_snd_hw_params,
+};
+
+static const struct soc_enum msm_snd_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text),
+ SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(2, hdmi_rx_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(8, proxy_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(3, hdmi_rx_sample_rate_text),
+ SOC_ENUM_SINGLE_EXT(32, tdm_ch_text),
+ SOC_ENUM_SINGLE_EXT(2, tdm_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(2, mi2s_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(9, ec_ref_ch_text),
+ SOC_ENUM_SINGLE_EXT(3, ec_ref_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(9, ec_ref_rate_text),
+ SOC_ENUM_SINGLE_EXT(3, mi2s_rate_text),
+ SOC_ENUM_SINGLE_EXT(3, tdm_rate_text),
+ SOC_ENUM_SINGLE_EXT(6, tdm_slot_num_text),
+ SOC_ENUM_SINGLE_EXT(3, tdm_slot_width_text),
+};
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+ SOC_ENUM_EXT("AUX PCM SampleRate", msm_snd_enum[0],
+ msm_auxpcm_rate_get, msm_auxpcm_rate_put),
+ SOC_ENUM_EXT("HDMI_RX Channels", msm_snd_enum[1],
+ msm_hdmi_rx_ch_get, msm_hdmi_rx_ch_put),
+ SOC_ENUM_EXT("HDMI_RX Bit Format", msm_snd_enum[2],
+ hdmi_rx_bit_format_get, hdmi_rx_bit_format_put),
+ SOC_ENUM_EXT("PROXY_RX Channels", msm_snd_enum[3],
+ msm_proxy_rx_ch_get, msm_proxy_rx_ch_put),
+ SOC_ENUM_EXT("HDMI_RX SampleRate", msm_snd_enum[4],
+ hdmi_rx_sample_rate_get, hdmi_rx_sample_rate_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", msm_snd_enum[5],
+ msm_pri_tdm_tx_0_ch_get, msm_pri_tdm_tx_0_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_1 Channels", msm_snd_enum[5],
+ msm_pri_tdm_tx_1_ch_get, msm_pri_tdm_tx_1_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_2 Channels", msm_snd_enum[5],
+ msm_pri_tdm_tx_2_ch_get, msm_pri_tdm_tx_2_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_3 Channels", msm_snd_enum[5],
+ msm_pri_tdm_tx_3_ch_get, msm_pri_tdm_tx_3_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", msm_snd_enum[5],
+ msm_pri_tdm_rx_0_ch_get, msm_pri_tdm_rx_0_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_1 Channels", msm_snd_enum[5],
+ msm_pri_tdm_rx_1_ch_get, msm_pri_tdm_rx_1_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_2 Channels", msm_snd_enum[5],
+ msm_pri_tdm_rx_2_ch_get, msm_pri_tdm_rx_2_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_3 Channels", msm_snd_enum[5],
+ msm_pri_tdm_rx_3_ch_get, msm_pri_tdm_rx_3_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", msm_snd_enum[5],
+ msm_sec_tdm_rx_0_ch_get, msm_sec_tdm_rx_0_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_1 Channels", msm_snd_enum[5],
+ msm_sec_tdm_rx_1_ch_get, msm_sec_tdm_rx_1_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_2 Channels", msm_snd_enum[5],
+ msm_sec_tdm_rx_2_ch_get, msm_sec_tdm_rx_2_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_3 Channels", msm_snd_enum[5],
+ msm_sec_tdm_rx_3_ch_get, msm_sec_tdm_rx_3_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", msm_snd_enum[5],
+ msm_sec_tdm_tx_0_ch_get, msm_sec_tdm_tx_0_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_1 Channels", msm_snd_enum[5],
+ msm_sec_tdm_tx_1_ch_get, msm_sec_tdm_tx_1_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_2 Channels", msm_snd_enum[5],
+ msm_sec_tdm_tx_2_ch_get, msm_sec_tdm_tx_2_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_3 Channels", msm_snd_enum[5],
+ msm_sec_tdm_tx_3_ch_get, msm_sec_tdm_tx_3_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", msm_snd_enum[5],
+ msm_tert_tdm_rx_0_ch_get, msm_tert_tdm_rx_0_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_1 Channels", msm_snd_enum[5],
+ msm_tert_tdm_rx_1_ch_get, msm_tert_tdm_rx_1_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_2 Channels", msm_snd_enum[5],
+ msm_tert_tdm_rx_2_ch_get, msm_tert_tdm_rx_2_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_3 Channels", msm_snd_enum[5],
+ msm_tert_tdm_rx_3_ch_get, msm_tert_tdm_rx_3_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_4 Channels", msm_snd_enum[5],
+ msm_tert_tdm_rx_4_ch_get, msm_tert_tdm_rx_4_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", msm_snd_enum[5],
+ msm_tert_tdm_tx_0_ch_get, msm_tert_tdm_tx_0_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_1 Channels", msm_snd_enum[5],
+ msm_tert_tdm_tx_1_ch_get, msm_tert_tdm_tx_1_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_2 Channels", msm_snd_enum[5],
+ msm_tert_tdm_tx_2_ch_get, msm_tert_tdm_tx_2_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_3 Channels", msm_snd_enum[5],
+ msm_tert_tdm_tx_3_ch_get, msm_tert_tdm_tx_3_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", msm_snd_enum[5],
+ msm_quat_tdm_rx_0_ch_get, msm_quat_tdm_rx_0_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_1 Channels", msm_snd_enum[5],
+ msm_quat_tdm_rx_1_ch_get, msm_quat_tdm_rx_1_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_2 Channels", msm_snd_enum[5],
+ msm_quat_tdm_rx_2_ch_get, msm_quat_tdm_rx_2_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_3 Channels", msm_snd_enum[5],
+ msm_quat_tdm_rx_3_ch_get, msm_quat_tdm_rx_3_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", msm_snd_enum[5],
+ msm_quat_tdm_tx_0_ch_get, msm_quat_tdm_tx_0_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_1 Channels", msm_snd_enum[5],
+ msm_quat_tdm_tx_1_ch_get, msm_quat_tdm_tx_1_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_2 Channels", msm_snd_enum[5],
+ msm_quat_tdm_tx_2_ch_get, msm_quat_tdm_tx_2_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_3 Channels", msm_snd_enum[5],
+ msm_quat_tdm_tx_3_ch_get, msm_quat_tdm_tx_3_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 Bit Format", msm_snd_enum[6],
+ msm_pri_tdm_tx_0_bit_format_get,
+ msm_pri_tdm_tx_0_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_1 Bit Format", msm_snd_enum[6],
+ msm_pri_tdm_tx_1_bit_format_get,
+ msm_pri_tdm_tx_1_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_2 Bit Format", msm_snd_enum[6],
+ msm_pri_tdm_tx_2_bit_format_get,
+ msm_pri_tdm_tx_2_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_3 Bit Format", msm_snd_enum[6],
+ msm_pri_tdm_tx_3_bit_format_get,
+ msm_pri_tdm_tx_3_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_0 Bit Format", msm_snd_enum[6],
+ msm_pri_tdm_rx_0_bit_format_get,
+ msm_pri_tdm_rx_0_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_1 Bit Format", msm_snd_enum[6],
+ msm_pri_tdm_rx_1_bit_format_get,
+ msm_pri_tdm_rx_1_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_2 Bit Format", msm_snd_enum[6],
+ msm_pri_tdm_rx_2_bit_format_get,
+ msm_pri_tdm_rx_2_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_3 Bit Format", msm_snd_enum[6],
+ msm_pri_tdm_rx_3_bit_format_get,
+ msm_pri_tdm_rx_3_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_rx_0_bit_format_get,
+ msm_sec_tdm_rx_0_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_1 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_rx_1_bit_format_get,
+ msm_sec_tdm_rx_1_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_2 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_rx_2_bit_format_get,
+ msm_sec_tdm_rx_2_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_3 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_rx_3_bit_format_get,
+ msm_sec_tdm_rx_3_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_tx_0_bit_format_get,
+ msm_sec_tdm_tx_0_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_1 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_tx_1_bit_format_get,
+ msm_sec_tdm_tx_1_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_2 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_tx_2_bit_format_get,
+ msm_sec_tdm_tx_2_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_3 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_tx_3_bit_format_get,
+ msm_sec_tdm_tx_3_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_rx_0_bit_format_get,
+ msm_tert_tdm_rx_0_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_1 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_rx_1_bit_format_get,
+ msm_tert_tdm_rx_1_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_2 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_rx_2_bit_format_get,
+ msm_tert_tdm_rx_2_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_3 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_rx_3_bit_format_get,
+ msm_tert_tdm_rx_3_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_4 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_rx_4_bit_format_get,
+ msm_tert_tdm_rx_4_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_tx_0_bit_format_get,
+ msm_tert_tdm_tx_0_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_1 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_tx_1_bit_format_get,
+ msm_tert_tdm_tx_1_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_2 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_tx_2_bit_format_get,
+ msm_tert_tdm_tx_2_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_3 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_tx_3_bit_format_get,
+ msm_tert_tdm_tx_3_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_rx_0_bit_format_get,
+ msm_quat_tdm_rx_0_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_1 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_rx_1_bit_format_get,
+ msm_quat_tdm_rx_1_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_2 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_rx_2_bit_format_get,
+ msm_quat_tdm_rx_2_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_3 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_rx_3_bit_format_get,
+ msm_quat_tdm_rx_3_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_tx_0_bit_format_get,
+ msm_quat_tdm_tx_0_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_1 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_tx_1_bit_format_get,
+ msm_quat_tdm_tx_1_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_2 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_tx_2_bit_format_get,
+ msm_quat_tdm_tx_2_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_3 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_tx_3_bit_format_get,
+ msm_quat_tdm_tx_3_bit_format_put),
+ SOC_ENUM_EXT("QUAT_MI2S_RX Bit Format", msm_snd_enum[7],
+ msm_quat_mi2s_rx_bit_format_get,
+ msm_quat_mi2s_rx_bit_format_put),
+ SOC_ENUM_EXT("TERT_MI2S_TX Bit Format", msm_snd_enum[7],
+ msm_tert_mi2s_tx_bit_format_get,
+ msm_tert_mi2s_tx_bit_format_put),
+ SOC_ENUM_EXT("SEC_MI2S_RX Bit Format", msm_snd_enum[7],
+ msm_sec_mi2s_rx_bit_format_get,
+ msm_sec_mi2s_rx_bit_format_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX Bit Format", msm_snd_enum[7],
+ msm_sec_mi2s_tx_bit_format_get,
+ msm_sec_mi2s_tx_bit_format_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", msm_snd_enum[11],
+ msm_sec_mi2s_rate_get, msm_sec_mi2s_rate_put),
+ SOC_ENUM_EXT("PRI_TDM SampleRate", msm_snd_enum[12],
+ msm_pri_tdm_rate_get, msm_pri_tdm_rate_put),
+ SOC_ENUM_EXT("PRI_TDM Slot Number", msm_snd_enum[13],
+ msm_pri_tdm_slot_num_get, msm_pri_tdm_slot_num_put),
+ SOC_ENUM_EXT("PRI_TDM Slot Width", msm_snd_enum[14],
+ msm_pri_tdm_slot_width_get, msm_pri_tdm_slot_width_put),
+ SOC_ENUM_EXT("SEC_TDM Slot Number", msm_snd_enum[13],
+ msm_sec_tdm_slot_num_get, msm_sec_tdm_slot_num_put),
+ SOC_ENUM_EXT("SEC_TDM Slot Width", msm_snd_enum[14],
+ msm_sec_tdm_slot_width_get, msm_sec_tdm_slot_width_put),
+ SOC_ENUM_EXT("TERT_TDM Slot Number", msm_snd_enum[13],
+ msm_tert_tdm_slot_num_get,
+ msm_tert_tdm_slot_num_put),
+ SOC_ENUM_EXT("TERT_TDM Slot Width", msm_snd_enum[14],
+ msm_tert_tdm_slot_width_get,
+ msm_tert_tdm_slot_width_put),
+ SOC_ENUM_EXT("QUAT_TDM Slot Number", msm_snd_enum[13],
+ msm_quat_tdm_slot_num_get,
+ msm_quat_tdm_slot_num_put),
+ SOC_ENUM_EXT("QUAT_TDM Slot Width", msm_snd_enum[14],
+ msm_quat_tdm_slot_width_get,
+ msm_quat_tdm_slot_width_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_0 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_0, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_1 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_1, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_2 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_2, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_3 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_3, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_4 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_4, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_5 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_5, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_6 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_6, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_7 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_7, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_0 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_0, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_1 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_1, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_2 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_2, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_3 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_3, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_4 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_4, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_5 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_5, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_6 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_6, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_7 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_7, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_0 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_0, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_1 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_1, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_2 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_2, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_3 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_3, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_4 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_4, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_5 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_5, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_6 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_6, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_7 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_7, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_0 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_0, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_1 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_1, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_2 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_2, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_3 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_3, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_4 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_4, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_5 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_5, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_6 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_6, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_7 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_7, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_0 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_0, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_1 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_1, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_2 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_2, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_3 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_3, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_4 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_4, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_5 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_5, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_6 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_6, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_7 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_7, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_0 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_0, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_1 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_1, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_2 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_2, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_3 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_3, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_4 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_4, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_5 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_5, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_6 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_6, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_7 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_7, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_0 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_0, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_1 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_1, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_2 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_2, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_3 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_3, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_4 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_4, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_5 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_5, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_6 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_6, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_7 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_7, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_0 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_0, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_1 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_1, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_2 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_2, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_3 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_3, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_4 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_4, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_5 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_5, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_6 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_6, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_7 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_7, 0xFFFF,
+ 0, TDM_SLOT_OFFSET_MAX, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_ENUM_EXT("EC Reference Channels", msm_snd_enum[8],
+ msm_ec_ref_ch_get, msm_ec_ref_ch_put),
+ SOC_ENUM_EXT("EC Reference Bit Format", msm_snd_enum[9],
+ msm_ec_ref_bit_format_get, msm_ec_ref_bit_format_put),
+ SOC_ENUM_EXT("EC Reference SampleRate", msm_snd_enum[10],
+ msm_ec_ref_rate_get, msm_ec_ref_rate_put),
+};
+
+static int apq8096_get_ll_qos_val(struct snd_pcm_runtime *runtime)
+{
+ int usecs;
+
+ /* take 10% of period time as the deadline */
+ usecs = (100000 / runtime->rate) * runtime->period_size;
+ usecs += ((100000 % runtime->rate) * runtime->period_size) /
+ runtime->rate;
+
+ return usecs;
+}
+
+static int apq8096_ll_prepare(struct snd_pcm_substream *substream)
+{
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
+ pm_qos_add_request(&substream->latency_pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY,
+ apq8096_get_ll_qos_val(substream->runtime));
+ return 0;
+}
+
+static struct snd_soc_ops apq8096_ll_ops = {
+ .prepare = apq8096_ll_prepare,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link apq8096_common_dai_links[] = {
+ /* FrontEnd DAI Links */
+ {
+ .name = "MSM8996 Media1",
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+ {
+ .name = "MSM8996 Media2",
+ .stream_name = "MultiMedia2",
+ .cpu_dai_name = "MultiMedia2",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+ },
+ {
+ .name = "VoiceMMode1",
+ .stream_name = "VoiceMMode1",
+ .cpu_dai_name = "VoiceMMode1",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE1,
+ },
+ {
+ .name = "MSM VoIP",
+ .stream_name = "VoIP",
+ .cpu_dai_name = "VoIP",
+ .platform_name = "msm-voip-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_VOIP,
+ },
+ {
+ .name = "MSM8996 ULL",
+ .stream_name = "MultiMedia3",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-dsp.2",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+ /* Hostless PCM purpose */
+ {
+ .name = "SLIMBUS_0 Hostless",
+ .stream_name = "SLIMBUS_0 Hostless",
+ .cpu_dai_name = "SLIMBUS0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary MI2S TX_Hostless",
+ .stream_name = "Tertiary MI2S_TX Hostless Capture",
+ .cpu_dai_name = "TERT_MI2S_TX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "MSM AFE-PCM RX",
+ .stream_name = "AFE-PROXY RX",
+ .cpu_dai_name = "msm-dai-q6-dev.241",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = "MSM AFE-PCM TX",
+ .stream_name = "AFE-PROXY TX",
+ .cpu_dai_name = "msm-dai-q6-dev.240",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "MSM8996 Compress1",
+ .stream_name = "Compress1",
+ .cpu_dai_name = "MultiMedia4",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+ },
+ {
+ .name = "AUXPCM Hostless",
+ .stream_name = "AUXPCM Hostless",
+ .cpu_dai_name = "AUXPCM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_1 Hostless",
+ .stream_name = "SLIMBUS_1 Hostless",
+ .cpu_dai_name = "SLIMBUS1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_3 Hostless",
+ .stream_name = "SLIMBUS_3 Hostless",
+ .cpu_dai_name = "SLIMBUS3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "MSM8996 HFP RX",
+ .stream_name = "MultiMedia21",
+ .cpu_dai_name = "MultiMedia21",
+ .platform_name = "msm-pcm-loopback",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA21,
+ },
+ {
+ .name = "VoLTE",
+ .stream_name = "VoLTE",
+ .cpu_dai_name = "VoLTE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOLTE,
+ },
+ {
+ .name = "MSM8996 LowLatency",
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+ .ops = &apq8096_ll_ops,
+ },
+ {
+ .name = "MSM8996 Media20",
+ .stream_name = "MultiMedia20",
+ .cpu_dai_name = "MultiMedia20",
+ .platform_name = "msm-pcm-loopback",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA20,
+ },
+ /* Multiple Tunnel instances */
+ {
+ .name = "MSM8996 Compress2",
+ .stream_name = "Compress2",
+ .cpu_dai_name = "MultiMedia7",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+ },
+ {
+ .name = "MSM8996 Compress3",
+ .stream_name = "Compress3",
+ .cpu_dai_name = "MultiMedia10",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10,
+ },
+ {
+ .name = "MSM8996 ULL NOIRQ",
+ .stream_name = "MM_NOIRQ",
+ .cpu_dai_name = "MultiMedia8",
+ .platform_name = "msm-pcm-dsp-noirq",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+ },
+ {
+ .name = "QCHAT",
+ .stream_name = "QCHAT",
+ .cpu_dai_name = "QCHAT",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_QCHAT,
+ },
+ /* HDMI Hostless */
+ {
+ .name = "HDMI_RX_HOSTLESS",
+ .stream_name = "HDMI_RX_HOSTLESS",
+ .cpu_dai_name = "HDMI_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "VoiceMMode2",
+ .stream_name = "VoiceMMode2",
+ .cpu_dai_name = "VoiceMMode2",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE2,
+ },
+ {
+ .name = "INT_HFP_BT Hostless",
+ .stream_name = "INT_HFP_BT Hostless",
+ .cpu_dai_name = "INT_HFP_BT_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "MSM8996 HFP TX",
+ .stream_name = "MultiMedia6",
+ .cpu_dai_name = "MultiMedia6",
+ .platform_name = "msm-pcm-loopback",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,
+ },
+ /* LSM FE */
+ {
+ .name = "Listen 2 Audio Service",
+ .stream_name = "Listen 2 Audio Service",
+ .cpu_dai_name = "LSM2",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM2,
+ },
+ {
+ .name = "Listen 3 Audio Service",
+ .stream_name = "Listen 3 Audio Service",
+ .cpu_dai_name = "LSM3",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM3,
+ },
+ {
+ .name = "Listen 4 Audio Service",
+ .stream_name = "Listen 4 Audio Service",
+ .cpu_dai_name = "LSM4",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM4,
+ },
+ {
+ .name = "Listen 5 Audio Service",
+ .stream_name = "Listen 5 Audio Service",
+ .cpu_dai_name = "LSM5",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM5,
+ },
+ {
+ .name = "Listen 6 Audio Service",
+ .stream_name = "Listen 6 Audio Service",
+ .cpu_dai_name = "LSM6",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM6,
+ },
+ {
+ .name = "Listen 7 Audio Service",
+ .stream_name = "Listen 7 Audio Service",
+ .cpu_dai_name = "LSM7",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM7,
+ },
+ {
+ .name = "MSM8996 LowLatency2",
+ .stream_name = "MultiMedia22",
+ .cpu_dai_name = "MultiMedia22",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA22,
+ .ops = &apq8096_ll_ops,
+ },
+ {
+ .name = "MSM8996 LowLatency Loopback",
+ .stream_name = "MultiMedia9",
+ .cpu_dai_name = "MultiMedia9",
+ .platform_name = "msm-pcm-loopback.1",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+ },
+ {
+ .name = "VoWLAN",
+ .stream_name = "VoWLAN",
+ .cpu_dai_name = "VoWLAN",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOWLAN,
+ },
+ {
+ .name = "MSM8996 Compress4",
+ .stream_name = "Compress4",
+ .cpu_dai_name = "MultiMedia11",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11,
+ },
+ {
+ .name = "MSM8996 Compress5",
+ .stream_name = "Compress5",
+ .cpu_dai_name = "MultiMedia12",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12,
+ },
+ {
+ .name = "MSM8996 Compress6",
+ .stream_name = "Compress6",
+ .cpu_dai_name = "MultiMedia13",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13,
+ },
+ {
+ .name = "MSM8996 Compress7",
+ .stream_name = "Compress7",
+ .cpu_dai_name = "MultiMedia14",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+ },
+ {
+ .name = "MSM8996 Compress8",
+ .stream_name = "Compress8",
+ .cpu_dai_name = "MultiMedia15",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
+ },
+ {
+ .name = "MSM8996 ULL NOIRQ 2",
+ .stream_name = "MM_NOIRQ_2",
+ .cpu_dai_name = "MultiMedia16",
+ .platform_name = "msm-pcm-dsp-noirq",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16,
+ },
+ {
+ .name = "Circuit-Switch Voice",
+ .stream_name = "CS-Voice",
+ .cpu_dai_name = "CS-VOICE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_CS_VOICE,
+ },
+ {
+ .name = "Voice2",
+ .stream_name = "Voice2",
+ .cpu_dai_name = "Voice2",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICE2,
+ },
+};
+
+static struct snd_soc_dai_link apq8096_auto_fe_dai_links[] = {
+ {
+ .name = "Tertiary TDM RX 0 Hostless",
+ .stream_name = "Tertiary TDM RX 0 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM RX 1 Hostless",
+ .stream_name = "Tertiary TDM RX 1 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM RX 2 Hostless",
+ .stream_name = "Tertiary TDM RX 2 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM RX 3 Hostless",
+ .stream_name = "Tertiary TDM RX 3 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 0 Hostless",
+ .stream_name = "Tertiary TDM TX 0 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 1 Hostless",
+ .stream_name = "Tertiary TDM TX 1 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 2 Hostless",
+ .stream_name = "Tertiary TDM TX 2 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 3 Hostless",
+ .stream_name = "Tertiary TDM TX 3 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 0 Hostless",
+ .stream_name = "Quaternary TDM RX 0 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 1 Hostless",
+ .stream_name = "Quaternary TDM RX 1 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 2 Hostless",
+ .stream_name = "Quaternary TDM RX 2 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 3 Hostless",
+ .stream_name = "Quaternary TDM RX 3 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 0 Hostless",
+ .stream_name = "Quaternary TDM TX 0 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 1 Hostless",
+ .stream_name = "Quaternary TDM TX 1 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 2 Hostless",
+ .stream_name = "Quaternary TDM TX 2 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 3 Hostless",
+ .stream_name = "Quaternary TDM TX 3 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary MI2S_RX Hostless Playback",
+ .stream_name = "Quaternary MI2S_RX Hostless Playback",
+ .cpu_dai_name = "QUAT_MI2S_RX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Secondary MI2S_TX Hostless Capture",
+ .stream_name = "Secondary MI2S_TX Hostless Capture",
+ .cpu_dai_name = "SEC_MI2S_TX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM RX 0 Hostless",
+ .stream_name = "Primary TDM RX 0 Hostless",
+ .cpu_dai_name = "PRI_TDM_RX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM RX 1 Hostless",
+ .stream_name = "Primary TDM RX 1 Hostless",
+ .cpu_dai_name = "PRI_TDM_RX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM RX 2 Hostless",
+ .stream_name = "Primary TDM RX 2 Hostless",
+ .cpu_dai_name = "PRI_TDM_RX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM RX 3 Hostless",
+ .stream_name = "Primary TDM RX 3 Hostless",
+ .cpu_dai_name = "PRI_TDM_RX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM TX 0 Hostless",
+ .stream_name = "Primary TDM TX 0 Hostless",
+ .cpu_dai_name = "PRI_TDM_TX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM TX 1 Hostless",
+ .stream_name = "Primary TDM TX 1 Hostless",
+ .cpu_dai_name = "PRI_TDM_TX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM TX 2 Hostless",
+ .stream_name = "Primary TDM TX 2 Hostless",
+ .cpu_dai_name = "PRI_TDM_TX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM TX 3 Hostless",
+ .stream_name = "Primary TDM TX 3 Hostless",
+ .cpu_dai_name = "PRI_TDM_TX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "DTMF RX Hostless",
+ .stream_name = "DTMF RX Hostless",
+ .cpu_dai_name = "DTMF_RX_HOSTLESS",
+ .platform_name = "msm-pcm-dtmf",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_DTMF_RX,
+ }
+};
+
+static struct snd_soc_dai_link apq8096_custom_fe_dai_links[] = {
+ /* FrontEnd DAI Links */
+ {
+ .name = "MSM8996 Media1",
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1,
+ .ops = &apq8096_ll_ops,
+ },
+ {
+ .name = "MSM8996 Media2",
+ .stream_name = "MultiMedia2",
+ .cpu_dai_name = "MultiMedia2",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+ .ops = &apq8096_ll_ops,
+ },
+ {
+ .name = "MSM8996 Media3",
+ .stream_name = "MultiMedia3",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ .ops = &apq8096_ll_ops,
+ },
+ {
+ .name = "MSM8996 Media5",
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+ .ops = &apq8096_ll_ops,
+ },
+ {
+ .name = "MSM8996 Media6",
+ .stream_name = "MultiMedia6",
+ .cpu_dai_name = "MultiMedia6",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,
+ .ops = &apq8096_ll_ops,
+ },
+ {
+ .name = "MSM8996 Media8",
+ .stream_name = "MultiMedia8",
+ .cpu_dai_name = "MultiMedia8",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+ .ops = &apq8096_ll_ops,
+ },
+ {
+ .name = "MSM8996 Media9",
+ .stream_name = "MultiMedia9",
+ .cpu_dai_name = "MultiMedia9",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+ .ops = &apq8096_ll_ops,
+ },
+ {
+ .name = "INT_HFP_BT Hostless",
+ .stream_name = "INT_HFP_BT Hostless",
+ .cpu_dai_name = "INT_HFP_BT_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "AUXPCM Hostless",
+ .stream_name = "AUXPCM Hostless",
+ .cpu_dai_name = "AUXPCM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "MSM8996 Media20",
+ .stream_name = "MultiMedia20",
+ .cpu_dai_name = "MultiMedia20",
+ .platform_name = "msm-pcm-loopback",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA20,
+ },
+};
+
+static struct snd_soc_dai_link apq8096_common_be_dai_links[] = {
+ /* Backend AFE DAI Links */
+ {
+ .name = LPASS_BE_AFE_PCM_RX,
+ .stream_name = "AFE Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.224",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+ .be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_AFE_PCM_TX,
+ .stream_name = "AFE Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.225",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+ .be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Primary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Uplink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_TX,
+ .stream_name = "Voice Uplink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32772",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Downlink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_RX,
+ .stream_name = "Voice Downlink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32771",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE_PLAYBACK_TX,
+ .stream_name = "Voice Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32773",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music 2 BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE2_PLAYBACK_TX,
+ .stream_name = "Voice2 Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32770",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ }
+};
+
+static struct snd_soc_dai_link apq8096_auto_be_dai_links[] = {
+ /* Backend DAI Links */
+ {
+ .name = LPASS_BE_SEC_MI2S_RX,
+ .stream_name = "Secondary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ .be_hw_params_fixup = msm_mi2s_rx_be_hw_params_fixup,
+ .ops = &apq8096_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_TX,
+ .stream_name = "Secondary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ .be_hw_params_fixup = msm_mi2s_tx_be_hw_params_fixup,
+ .ops = &apq8096_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_TX,
+ .stream_name = "Tertiary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ .be_hw_params_fixup = msm_mi2s_tx_be_hw_params_fixup,
+ .ops = &apq8096_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_RX,
+ .stream_name = "Quaternary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ .be_hw_params_fixup = msm_mi2s_rx_be_hw_params_fixup,
+ .ops = &apq8096_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_RX_0,
+ .stream_name = "Secondary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36880",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_RX_1,
+ .stream_name = "Secondary TDM1 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36882",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_RX_2,
+ .stream_name = "Secondary TDM2 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36884",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_RX_3,
+ .stream_name = "Secondary TDM3 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36886",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_0,
+ .stream_name = "Secondary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36881",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_1,
+ .stream_name = "Secondary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36883",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_2,
+ .stream_name = "Secondary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36885",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_3,
+ .stream_name = "Secondary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36887",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_0,
+ .stream_name = "Tertiary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36896",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_1,
+ .stream_name = "Tertiary TDM1 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36898",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_2,
+ .stream_name = "Tertiary TDM2 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36900",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_3,
+ .stream_name = "Tertiary TDM3 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36902",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_4,
+ .stream_name = "Tertiary TDM4 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36904",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_0,
+ .stream_name = "Tertiary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36897",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_1,
+ .stream_name = "Tertiary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36899",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_2,
+ .stream_name = "Tertiary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36901",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_3,
+ .stream_name = "Tertiary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36903",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_0,
+ .stream_name = "Quaternary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36912",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_1,
+ .stream_name = "Quaternary TDM1 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36914",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_2,
+ .stream_name = "Quaternary TDM2 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36916",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_3,
+ .stream_name = "Quaternary TDM3 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36918",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_0,
+ .stream_name = "Quaternary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36913",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_1,
+ .stream_name = "Quaternary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36915",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_2,
+ .stream_name = "Quaternary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36917",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_3,
+ .stream_name = "Quaternary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36919",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_RX_0,
+ .stream_name = "Primary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36864",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_RX_1,
+ .stream_name = "Primary TDM1 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36866",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_RX_2,
+ .stream_name = "Primary TDM2 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36868",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_RX_3,
+ .stream_name = "Primary TDM3 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36870",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_0,
+ .stream_name = "Primary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36865",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_1,
+ .stream_name = "Primary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36867",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_2,
+ .stream_name = "Primary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36869",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_3,
+ .stream_name = "Primary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36871",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_TX_1,
+ .stream_name = "Secondary MI2S1 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4161",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX_1,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_TX_2,
+ .stream_name = "Secondary MI2S2 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4163",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX_2,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_TX_3,
+ .stream_name = "Secondary MI2S3 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4165",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX_3,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_TX_4,
+ .stream_name = "Secondary MI2S4 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4167",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX_4,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_TX_1,
+ .stream_name = "Tertiary MI2S1 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4169",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX_1,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_TX_2,
+ .stream_name = "Tertiary MI2S2 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4171",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX_2,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_TX_3,
+ .stream_name = "Tertiary MI2S3 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4173",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX_3,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_TX_4,
+ .stream_name = "Tertiary MI2S4 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4175",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX_4,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_TX_1,
+ .stream_name = "Quaternary MI2S1 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4129",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_1,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_TX_2,
+ .stream_name = "Quaternary MI2S2 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4131",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_2,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_TX_3,
+ .stream_name = "Quaternary MI2S3 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4133",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_3,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_TX_4,
+ .stream_name = "Quaternary MI2S4 Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4135",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_4,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_RX_1,
+ .stream_name = "Secondary MI2S1 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4160",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_RX_2,
+ .stream_name = "Secondary MI2S2 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4162",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_RX_3,
+ .stream_name = "Secondary MI2S3 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4164",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_RX_4,
+ .stream_name = "Secondary MI2S4 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4166",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_RX_1,
+ .stream_name = "Tertiary MI2S1 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4168",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_RX_2,
+ .stream_name = "Tertiary MI2S2 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4170",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_RX_3,
+ .stream_name = "Tertiary MI2S3 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4172",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_RX_4,
+ .stream_name = "Tertiary MI2S4 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4174",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_RX_1,
+ .stream_name = "Quaternary MI2S1 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4128",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_RX_2,
+ .stream_name = "Quaternary MI2S2 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4130",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_RX_3,
+ .stream_name = "Quaternary MI2S3 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4132",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_RX_4,
+ .stream_name = "Quaternary MI2S4 Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.4134",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ .be_hw_params_fixup = msm_group_mi2s_be_hw_params_fixup,
+ .ops = &apq8096_group_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_TX,
+ .stream_name = "Quaternary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ .be_hw_params_fixup = msm_mi2s_rx_be_hw_params_fixup,
+ .ops = &apq8096_mi2s_be_ops,
+ .ignore_suspend = 1,
+ }
+};
+
+static struct snd_soc_dai_link apq8096_hdmi_dai_link[] = {
+ /* HDMI BACK END DAI Link */
+ {
+ .name = LPASS_BE_HDMI,
+ .stream_name = "HDMI Playback",
+ .cpu_dai_name = "msm-dai-q6-hdmi.8",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_HDMI_RX,
+ .be_hw_params_fixup = msm_hdmi_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link apq8096_auto_dai_links[
+ ARRAY_SIZE(apq8096_common_dai_links) +
+ ARRAY_SIZE(apq8096_auto_fe_dai_links) +
+ ARRAY_SIZE(apq8096_common_be_dai_links) +
+ ARRAY_SIZE(apq8096_auto_be_dai_links) +
+ ARRAY_SIZE(apq8096_hdmi_dai_link)];
+
+static struct snd_soc_dai_link apq8096_auto_custom_dai_links[
+ ARRAY_SIZE(apq8096_custom_fe_dai_links) +
+ ARRAY_SIZE(apq8096_auto_fe_dai_links) +
+ ARRAY_SIZE(apq8096_common_be_dai_links) +
+ ARRAY_SIZE(apq8096_auto_be_dai_links) +
+ ARRAY_SIZE(apq8096_hdmi_dai_link)];
+
+struct snd_soc_card snd_soc_card_auto_apq8096 = {
+ .name = "apq8096-auto-snd-card",
+};
+
+struct snd_soc_card snd_soc_card_adp_agave_apq8096 = {
+ .name = "apq8096-adp-agave-snd-card",
+};
+
+struct snd_soc_card snd_soc_card_adp_mmxf_apq8096 = {
+ .name = "apq8096-adp-mmxf-snd-card",
+};
+
+struct snd_soc_card snd_soc_card_auto_custom_apq8096 = {
+ .name = "apq8096-auto-custom-snd-card",
+};
+
+static int apq8096_populate_dai_link_component_of_node(
+ struct snd_soc_card *card)
+{
+ int i, index, ret = 0;
+ struct device *cdev = card->dev;
+ struct snd_soc_dai_link *dai_link = card->dai_link;
+ struct device_node *np;
+
+ if (!cdev) {
+ pr_err("%s: Sound card device memory NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < card->num_links; i++) {
+ if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node)
+ continue;
+
+ /* populate platform_of_node for snd card dai links */
+ if (dai_link[i].platform_name &&
+ !dai_link[i].platform_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-platform-names",
+ dai_link[i].platform_name);
+ if (index < 0) {
+ pr_err("%s: No match found for platform name: %s\n",
+ __func__, dai_link[i].platform_name);
+ ret = index;
+ goto err;
+ }
+ np = of_parse_phandle(cdev->of_node, "asoc-platform",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for platform %s, index %d failed\n",
+ __func__, dai_link[i].platform_name,
+ index);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].platform_of_node = np;
+ dai_link[i].platform_name = NULL;
+ }
+
+ /* populate cpu_of_node for snd card dai links */
+ if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-cpu-names",
+ dai_link[i].cpu_dai_name);
+ if (index >= 0) {
+ np = of_parse_phandle(cdev->of_node, "asoc-cpu",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for cpu dai %s failed\n",
+ __func__,
+ dai_link[i].cpu_dai_name);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].cpu_of_node = np;
+ dai_link[i].cpu_dai_name = NULL;
+ }
+ }
+
+ /* populate codec_of_node for snd card dai links */
+ if (dai_link[i].codec_name && !dai_link[i].codec_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-codec-names",
+ dai_link[i].codec_name);
+ if (index < 0)
+ continue;
+ np = of_parse_phandle(cdev->of_node, "asoc-codec",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for codec %s failed\n",
+ __func__, dai_link[i].codec_name);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].codec_of_node = np;
+ dai_link[i].codec_name = NULL;
+ }
+ }
+
+err:
+ return ret;
+}
+
+static const struct of_device_id apq8096_asoc_machine_of_match[] = {
+ { .compatible = "qcom,apq8096-asoc-snd-auto",
+ .data = "auto_codec"},
+ { .compatible = "qcom,apq8096-asoc-snd-adp-agave",
+ .data = "adp_agave_codec"},
+ { .compatible = "qcom,apq8096-asoc-snd-adp-mmxf",
+ .data = "adp_mmxf_codec"},
+ { .compatible = "qcom,apq8096-asoc-snd-auto-custom",
+ .data = "auto_custom_codec"},
+ {},
+};
+
+static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
+{
+ struct snd_soc_card *card = NULL;
+ struct snd_soc_dai_link *dailink;
+ int len_1, len_2, len_3, len_4;
+ const struct of_device_id *match;
+
+ match = of_match_node(apq8096_asoc_machine_of_match, dev->of_node);
+ if (!match) {
+ dev_err(dev, "%s: No DT match found for sound card\n",
+ __func__);
+ return NULL;
+ }
+
+ if (!strcmp(match->data, "auto_codec"))
+ card = &snd_soc_card_auto_apq8096;
+ else if (!strcmp(match->data, "adp_agave_codec"))
+ card = &snd_soc_card_adp_agave_apq8096;
+ else if (!strcmp(match->data, "adp_mmxf_codec"))
+ card = &snd_soc_card_adp_mmxf_apq8096;
+ else if (!strcmp(match->data, "auto_custom_codec")) {
+ card = &snd_soc_card_auto_custom_apq8096;
+ } else {
+ dev_err(dev, "%s: Codec not supported\n",
+ __func__);
+ return NULL;
+ }
+
+ if (!strcmp(match->data, "auto_custom_codec")) {
+ len_1 = ARRAY_SIZE(apq8096_custom_fe_dai_links);
+ len_2 = len_1 + ARRAY_SIZE(apq8096_auto_fe_dai_links);
+ len_3 = len_2 + ARRAY_SIZE(apq8096_common_be_dai_links);
+
+ memcpy(apq8096_auto_custom_dai_links,
+ apq8096_custom_fe_dai_links,
+ sizeof(apq8096_custom_fe_dai_links));
+ memcpy(apq8096_auto_custom_dai_links + len_1,
+ apq8096_auto_fe_dai_links,
+ sizeof(apq8096_auto_fe_dai_links));
+ memcpy(apq8096_auto_custom_dai_links + len_2,
+ apq8096_common_be_dai_links,
+ sizeof(apq8096_common_be_dai_links));
+ memcpy(apq8096_auto_custom_dai_links + len_3,
+ apq8096_auto_be_dai_links,
+ sizeof(apq8096_auto_be_dai_links));
+
+ dailink = apq8096_auto_custom_dai_links;
+ } else {
+ /* same FE and BE used for all non-custom codec */
+ len_1 = ARRAY_SIZE(apq8096_common_dai_links);
+ len_2 = len_1 + ARRAY_SIZE(apq8096_auto_fe_dai_links);
+ len_3 = len_2 + ARRAY_SIZE(apq8096_common_be_dai_links);
+
+ memcpy(apq8096_auto_dai_links,
+ apq8096_common_dai_links,
+ sizeof(apq8096_common_dai_links));
+ memcpy(apq8096_auto_dai_links + len_1,
+ apq8096_auto_fe_dai_links,
+ sizeof(apq8096_auto_fe_dai_links));
+ memcpy(apq8096_auto_dai_links + len_2,
+ apq8096_common_be_dai_links,
+ sizeof(apq8096_common_be_dai_links));
+ memcpy(apq8096_auto_dai_links + len_3,
+ apq8096_auto_be_dai_links,
+ sizeof(apq8096_auto_be_dai_links));
+
+ dailink = apq8096_auto_dai_links;
+ }
+
+ len_4 = len_3 + ARRAY_SIZE(apq8096_auto_be_dai_links);
+
+ if (of_property_read_bool(dev->of_node, "qcom,hdmi-audio-rx")) {
+ dev_dbg(dev, "%s(): hdmi audio support present\n",
+ __func__);
+ memcpy(dailink + len_4, apq8096_hdmi_dai_link,
+ sizeof(apq8096_hdmi_dai_link));
+ len_4 += ARRAY_SIZE(apq8096_hdmi_dai_link);
+ } else {
+ dev_dbg(dev, "%s(): No hdmi audio support\n", __func__);
+ }
+
+ if (card) {
+ card->dai_link = dailink;
+ card->num_links = len_4;
+ }
+
+ return card;
+}
+
+/*
+ * TDM offset mapping is per platform/codec specific.
+ * TO BE UPDATED if new platform/codec is introduced.
+ */
+static int apq8096_init_tdm_dev(struct device *dev)
+{
+ const struct of_device_id *match;
+
+ match = of_match_node(apq8096_asoc_machine_of_match, dev->of_node);
+ if (!match) {
+ dev_err(dev, "%s: No DT match found for sound card\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!strcmp(match->data, "adp_mmxf_codec")) {
+ dev_dbg(dev, "%s: ADP MMXF tdm slot offset\n", __func__);
+ memcpy(tdm_slot_offset,
+ tdm_slot_offset_adp_mmxf,
+ sizeof(tdm_slot_offset_adp_mmxf));
+ } else if (!strcmp(match->data, "auto_custom_codec")) {
+ dev_dbg(dev, "%s: custom tdm slot offset\n", __func__);
+ msm_pri_tdm_slot_width = 16;
+ msm_pri_tdm_slot_num = 16;
+ msm_sec_tdm_slot_width = 16;
+ msm_sec_tdm_slot_num = 16;
+ msm_tert_tdm_slot_width = 16;
+ msm_tert_tdm_slot_num = 16;
+ msm_quat_tdm_slot_width = 16;
+ msm_quat_tdm_slot_num = 16;
+ memcpy(tdm_slot_offset,
+ tdm_slot_offset_custom,
+ sizeof(tdm_slot_offset_custom));
+ } else {
+ dev_dbg(dev, "%s: DEFAULT tdm slot offset\n", __func__);
+ }
+
+ dev_dbg(dev, "%s: tdm slot_width %d, num_slots %d\n",
+ __func__, msm_tdm_slot_width, msm_tdm_num_slots);
+
+ return 0;
+}
+
+static int apq8096_asoc_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ const struct of_device_id *match;
+ int ret;
+ enum apr_subsys_state q6_state;
+ static int first_probe = 1;
+
+ if (first_probe) {
+ place_marker("M - DRIVER Audio Init");
+ first_probe = 0;
+ }
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "No platform supplied from device tree\n");
+ return -EINVAL;
+ }
+
+ q6_state = apr_get_q6_state();
+ if (q6_state == APR_SUBSYS_DOWN) {
+ dev_dbg(&pdev->dev, "deferring %s, adsp_state %d\n",
+ __func__, q6_state);
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
+
+ card = populate_snd_card_dailinks(&pdev->dev);
+ if (!card) {
+ dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+
+ ret = snd_soc_of_parse_card_name(card, "qcom,model");
+ if (ret) {
+ dev_err(&pdev->dev, "Parse card name failed, err:%d\n",
+ ret);
+ goto err;
+ }
+
+ match = of_match_node(apq8096_asoc_machine_of_match,
+ pdev->dev.of_node);
+ if (!match) {
+ dev_err(&pdev->dev, "%s: No matched codec is found.\n",
+ __func__);
+ goto err;
+ }
+
+ ret = apq8096_populate_dai_link_component_of_node(card);
+ if (ret) {
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
+
+ /* populate controls of snd card */
+ card->controls = msm_snd_controls;
+ card->num_controls = ARRAY_SIZE(msm_snd_controls);
+
+ ret = apq8096_init_tdm_dev(&pdev->dev);
+ if (ret) {
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
+
+ ret = snd_soc_register_card(card);
+ if (ret == -EPROBE_DEFER) {
+ goto err;
+ } else if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err;
+ }
+ dev_info(&pdev->dev, "Sound card %s registered\n", card->name);
+ place_marker("M - DRIVER Audio Ready");
+ sndcard = card;
+ return 0;
+
+err:
+ return ret;
+}
+
+static int apq8096_asoc_machine_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ sndcard = NULL;
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static struct platform_driver apq8096_asoc_machine_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = apq8096_asoc_machine_of_match,
+ },
+ .probe = apq8096_asoc_machine_probe,
+ .remove = apq8096_asoc_machine_remove,
+};
+
+static int dummy_machine_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int dummy_machine_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_device dummy_machine_device = {
+ .name = "apq8096-auto-asoc-dummy",
+};
+
+static struct platform_driver apq8096_asoc_machine_dummy_driver = {
+ .driver = {
+ .name = "apq8096-auto-asoc-dummy",
+ .owner = THIS_MODULE,
+ },
+ .probe = dummy_machine_probe,
+ .remove = dummy_machine_remove,
+};
+
+static int apq8096_is_adsp_ready(void)
+{
+ int adsp_ready = 0;
+ unsigned long timeout;
+
+ timeout = jiffies +
+ msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+ do {
+ if (!q6core_is_adsp_ready()) {
+ pr_err_ratelimited("%s: ADSP Audio isn't ready\n",
+ __func__);
+ /*
+ * ADSP will be coming up after subsystem restart and
+ * it might not be fully up when the control reaches
+ * here. So, wait for 50msec before checking ADSP state
+ */
+ msleep(50);
+ } else {
+ pr_debug("%s: ADSP Audio is ready\n", __func__);
+ adsp_ready = 1;
+ break;
+ }
+ } while (time_after(timeout, jiffies));
+
+ return adsp_ready;
+}
+
+static int apq8096_adsp_state_callback(struct notifier_block *nb,
+ unsigned long value, void *priv)
+{
+ if (!dummy_device_registered && SUBSYS_AFTER_POWERUP == value) {
+ platform_driver_register(&apq8096_asoc_machine_dummy_driver);
+ platform_device_register(&dummy_machine_device);
+ dummy_device_registered = true;
+ }
+ if (sndcard) {
+ switch (value) {
+ case SUBSYS_AFTER_POWERUP:
+ if (!apq8096_is_adsp_ready()) {
+ pr_err("%s: timed out waiting for ADSP\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ pr_debug("%s:SSR complete, set sndcard state as ONLINE\n",
+ __func__);
+ snd_soc_card_change_online_state(sndcard, 1);
+ break;
+ case SUBSYS_AFTER_SHUTDOWN:
+ pr_debug("%s:SSR start, set sndcard state as OFFLINE\n",
+ __func__);
+ snd_soc_card_change_online_state(sndcard, 0);
+ break;
+ default:
+ pr_debug("%s: unsupport value\n", __func__);
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block adsp_state_notifier_block = {
+ .notifier_call = apq8096_adsp_state_callback,
+ .priority = -INT_MAX,
+};
+
+static int __init apq8096_soc_platform_init(void)
+{
+ adsp_state_notifier = subsys_notif_register_notifier("adsp",
+ &adsp_state_notifier_block);
+ return 0;
+}
+
+module_init(apq8096_soc_platform_init);
+module_platform_driver(apq8096_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, apq8096_asoc_machine_of_match);
diff --git a/sound/soc/msm/device_event.h b/sound/soc/msm/device_event.h
new file mode 100644
index 000000000000..408d114e3c84
--- /dev/null
+++ b/sound/soc/msm/device_event.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DEVICE_EVENT_H
+#define __DEVICE_EVENT_H
+
+#define QC_AUDIO_EXTERNAL_SPK_1_EVENT "qc_ext_spk_1"
+#define QC_AUDIO_EXTERNAL_SPK_2_EVENT "qc_ext_spk_2"
+#define QC_AUDIO_EXTERNAL_MIC_EVENT "qc_ext_mic"
+
+#endif /* __DEVICE_EVENT_H */
diff --git a/sound/soc/msm/msm-audio-pinctrl.c b/sound/soc/msm/msm-audio-pinctrl.c
new file mode 100644
index 000000000000..2b30271500eb
--- /dev/null
+++ b/sound/soc/msm/msm-audio-pinctrl.c
@@ -0,0 +1,316 @@
+ /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include "msm-audio-pinctrl.h"
+
+/*
+ * pinctrl -- handle to query pinctrl apis
+ * cdc lines -- stores pinctrl handles for pinctrl states
+ * active_set -- maintain the overall pinctrl state
+ */
+struct cdc_pinctrl_info {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state **cdc_lines;
+ int active_set;
+};
+
+/*
+ * gpiosets -- stores all gpiosets mentioned in dtsi file
+ * gpiosets_comb_names -- stores all possible gpioset combinations
+ * gpioset_state -- maintains counter for each gpioset
+ * gpiosets_max -- maintain the total supported gpiosets
+ * gpiosets_comb_max -- maintain the total gpiosets combinations
+ */
+struct cdc_gpioset_info {
+ char **gpiosets;
+ char **gpiosets_comb_names;
+ uint8_t *gpioset_state;
+ int gpiosets_max;
+ int gpiosets_comb_max;
+};
+
+static struct cdc_pinctrl_info pinctrl_info[MAX_PINCTRL_CLIENT];
+static struct cdc_gpioset_info gpioset_info[MAX_PINCTRL_CLIENT];
+
+/* Finds the index for the gpio set in the dtsi file */
+int msm_get_gpioset_index(enum pinctrl_client client, char *keyword)
+{
+ int i;
+
+ for (i = 0; i < gpioset_info[client].gpiosets_max; i++) {
+ if (!(strcmp(gpioset_info[client].gpiosets[i], keyword)))
+ break;
+ }
+ /* Checking if the keyword is present in dtsi or not */
+ if (i != gpioset_info[client].gpiosets_max)
+ return i;
+ else
+ return -EINVAL;
+}
+
+/*
+ * This function reads the following from dtsi file
+ * 1. All gpio sets
+ * 2. All combinations of gpio sets
+ * 3. Pinctrl handles to gpio sets
+ *
+ * Returns error if there is
+ * 1. Problem reading from dtsi file
+ * 2. Memory allocation failure
+ */
+int msm_gpioset_initialize(enum pinctrl_client client,
+ struct device *dev)
+{
+ struct pinctrl *pinctrl;
+ const char *gpioset_names = "qcom,msm-gpios";
+ const char *gpioset_combinations = "qcom,pinctrl-names";
+ const char *gpioset_names_str = NULL;
+ const char *gpioset_comb_str = NULL;
+ int num_strings = 0;
+ int ret = 0;
+ int i = 0;
+
+ pr_debug("%s\n", __func__);
+ pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(pinctrl)) {
+ pr_err("%s: Unable to get pinctrl handle\n",
+ __func__);
+ return -EINVAL;
+ }
+ pinctrl_info[client].pinctrl = pinctrl;
+
+ /* Reading of gpio sets */
+ num_strings = of_property_count_strings(dev->of_node,
+ gpioset_names);
+ if (num_strings < 0) {
+ dev_err(dev,
+ "%s: missing %s in dt node or length is incorrect\n",
+ __func__, gpioset_names);
+ goto err;
+ }
+ gpioset_info[client].gpiosets_max = num_strings;
+ gpioset_info[client].gpiosets = devm_kzalloc(dev,
+ gpioset_info[client].gpiosets_max *
+ sizeof(char *), GFP_KERNEL);
+ if (!gpioset_info[client].gpiosets) {
+ dev_err(dev, "Can't allocate memory for gpio set names\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < num_strings; i++) {
+ ret = of_property_read_string_index(dev->of_node,
+ gpioset_names, i, &gpioset_names_str);
+
+ gpioset_info[client].gpiosets[i] = devm_kzalloc(dev,
+ (strlen(gpioset_names_str) + 1), GFP_KERNEL);
+
+ if (!gpioset_info[client].gpiosets[i]) {
+ dev_err(dev, "%s: Can't allocate gpiosets[%d] data\n",
+ __func__, i);
+ ret = -ENOMEM;
+ goto err;
+ }
+ strlcpy(gpioset_info[client].gpiosets[i],
+ gpioset_names_str, strlen(gpioset_names_str)+1);
+ gpioset_names_str = NULL;
+ }
+ num_strings = 0;
+
+ /* Allocating memory for gpio set counter */
+ gpioset_info[client].gpioset_state = devm_kzalloc(dev,
+ gpioset_info[client].gpiosets_max *
+ sizeof(uint8_t), GFP_KERNEL);
+ if (!gpioset_info[client].gpioset_state) {
+ dev_err(dev, "Can't allocate memory for gpio set counter\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* Reading of all combinations of gpio sets */
+ num_strings = of_property_count_strings(dev->of_node,
+ gpioset_combinations);
+ if (num_strings < 0) {
+ dev_err(dev,
+ "%s: missing %s in dt node or length is incorrect\n",
+ __func__, gpioset_combinations);
+ goto err;
+ }
+ gpioset_info[client].gpiosets_comb_max = num_strings;
+ gpioset_info[client].gpiosets_comb_names = devm_kzalloc(dev,
+ num_strings * sizeof(char *), GFP_KERNEL);
+ if (!gpioset_info[client].gpiosets_comb_names) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < gpioset_info[client].gpiosets_comb_max; i++) {
+ ret = of_property_read_string_index(dev->of_node,
+ gpioset_combinations, i, &gpioset_comb_str);
+
+ gpioset_info[client].gpiosets_comb_names[i] = devm_kzalloc(dev,
+ (strlen(gpioset_comb_str) + 1), GFP_KERNEL);
+ if (!gpioset_info[client].gpiosets_comb_names[i]) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ strlcpy(gpioset_info[client].gpiosets_comb_names[i],
+ gpioset_comb_str,
+ strlen(gpioset_comb_str)+1);
+ pr_debug("%s: GPIO configuration %s\n",
+ __func__,
+ gpioset_info[client].gpiosets_comb_names[i]);
+ gpioset_comb_str = NULL;
+ }
+
+ /* Allocating memory for handles to pinctrl states */
+ pinctrl_info[client].cdc_lines = devm_kzalloc(dev,
+ num_strings * sizeof(char *), GFP_KERNEL);
+ if (!pinctrl_info[client].cdc_lines) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* Get pinctrl handles for gpio sets in dtsi file */
+ for (i = 0; i < num_strings; i++) {
+ pinctrl_info[client].cdc_lines[i] = pinctrl_lookup_state(
+ pinctrl,
+ (const char *)gpioset_info[client].
+ gpiosets_comb_names[i]);
+ if (IS_ERR(pinctrl_info[client].cdc_lines[i]))
+ pr_err("%s: Unable to get pinctrl handle for %s\n",
+ __func__, gpioset_info[client].
+ gpiosets_comb_names[i]);
+ }
+ goto success;
+
+err:
+ /* Free up memory allocated for gpio set combinations */
+ for (i = 0; i < gpioset_info[client].gpiosets_max; i++) {
+ if (gpioset_info[client].gpiosets[i] != NULL) {
+ devm_kfree(dev, gpioset_info[client].gpiosets[i]);
+ gpioset_info[client].gpiosets[i] = NULL;
+ }
+ }
+ if (gpioset_info[client].gpiosets != NULL) {
+ devm_kfree(dev, gpioset_info[client].gpiosets);
+ gpioset_info[client].gpiosets = NULL;
+ }
+
+ /* Free up memory allocated for gpio set combinations */
+ for (i = 0; i < gpioset_info[client].gpiosets_comb_max; i++) {
+ if (gpioset_info[client].gpiosets_comb_names[i] != NULL) {
+ devm_kfree(dev,
+ gpioset_info[client].gpiosets_comb_names[i]);
+ gpioset_info[client].gpiosets_comb_names[i] = NULL;
+ }
+ }
+ if (gpioset_info[client].gpiosets_comb_names != NULL) {
+ devm_kfree(dev, gpioset_info[client].gpiosets_comb_names);
+ gpioset_info[client].gpiosets_comb_names = NULL;
+ }
+
+ /* Free up memory allocated for handles to pinctrl states */
+ if (pinctrl_info[client].cdc_lines != NULL) {
+ devm_kfree(dev, pinctrl_info[client].cdc_lines);
+ pinctrl_info[client].cdc_lines = NULL;
+ }
+
+ /* Free up memory allocated for counter of gpio sets */
+ if (gpioset_info[client].gpioset_state != NULL) {
+ devm_kfree(dev, gpioset_info[client].gpioset_state);
+ gpioset_info[client].gpioset_state = NULL;
+ }
+
+success:
+ return ret;
+}
+
+int msm_gpioset_activate(enum pinctrl_client client, char *keyword)
+{
+ int ret = 0;
+ int gp_set = 0;
+ int active_set = 0;
+
+ gp_set = msm_get_gpioset_index(client, keyword);
+ if (gp_set < 0) {
+ pr_err("%s: gpio set name does not exist\n",
+ __func__);
+ return gp_set;
+ }
+
+ if (!gpioset_info[client].gpioset_state[gp_set]) {
+ /*
+ * If pinctrl pointer is not valid,
+ * no need to proceed further
+ */
+ active_set = pinctrl_info[client].active_set;
+ if (IS_ERR(pinctrl_info[client].cdc_lines[active_set]))
+ return 0;
+
+ pinctrl_info[client].active_set |= (1 << gp_set);
+ active_set = pinctrl_info[client].active_set;
+ pr_debug("%s: pinctrl.active_set: %d\n", __func__, active_set);
+
+ /* Select the appropriate pinctrl state */
+ ret = pinctrl_select_state(pinctrl_info[client].pinctrl,
+ pinctrl_info[client].cdc_lines[active_set]);
+ }
+ gpioset_info[client].gpioset_state[gp_set]++;
+
+ return ret;
+}
+
+int msm_gpioset_suspend(enum pinctrl_client client, char *keyword)
+{
+ int ret = 0;
+ int gp_set = 0;
+ int active_set = 0;
+
+ gp_set = msm_get_gpioset_index(client, keyword);
+ if (gp_set < 0) {
+ pr_err("%s: gpio set name does not exist\n",
+ __func__);
+ return gp_set;
+ }
+
+ if (1 == gpioset_info[client].gpioset_state[gp_set]) {
+ pinctrl_info[client].active_set &= ~(1 << gp_set);
+ /*
+ * If pinctrl pointer is not valid,
+ * no need to proceed further
+ */
+ active_set = pinctrl_info[client].active_set;
+ if (IS_ERR(pinctrl_info[client].cdc_lines[active_set]))
+ return -EINVAL;
+
+ pr_debug("%s: pinctrl.active_set: %d\n", __func__,
+ pinctrl_info[client].active_set);
+ /* Select the appropriate pinctrl state */
+ ret = pinctrl_select_state(pinctrl_info[client].pinctrl,
+ pinctrl_info[client].cdc_lines[pinctrl_info[client].
+ active_set]);
+ }
+ if (!(gpioset_info[client].gpioset_state[gp_set])) {
+ pr_err("%s: Invalid call to de activate gpios: %d\n", __func__,
+ gpioset_info[client].gpioset_state[gp_set]);
+ return -EINVAL;
+ }
+
+ gpioset_info[client].gpioset_state[gp_set]--;
+
+ return ret;
+}
diff --git a/sound/soc/msm/msm-audio-pinctrl.h b/sound/soc/msm/msm-audio-pinctrl.h
new file mode 100644
index 000000000000..73eeaff39bfd
--- /dev/null
+++ b/sound/soc/msm/msm-audio-pinctrl.h
@@ -0,0 +1,43 @@
+ /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_AUDIO_PINCTRL_H
+#define __MSM_AUDIO_PINCTRL_H
+
+enum pinctrl_client {
+ CLIENT_WCD,
+ CLIENT_WSA_BONGO_1,
+ CLIENT_WSA_BONGO_2,
+ MAX_PINCTRL_CLIENT,
+};
+
+
+/* finds the index for the gpio set in the dtsi file */
+int msm_get_gpioset_index(enum pinctrl_client client, char *keyword);
+
+/*
+ * this function reads the following from dtsi file
+ * 1. all gpio sets
+ * 2. all combinations of gpio sets
+ * 3. pinctrl handles to gpio sets
+ *
+ * returns error if there is
+ * 1. problem reading from dtsi file
+ * 2. memory allocation failure
+ */
+int msm_gpioset_initialize(enum pinctrl_client client, struct device *dev);
+
+int msm_gpioset_activate(enum pinctrl_client client, char *keyword);
+
+int msm_gpioset_suspend(enum pinctrl_client client, char *keyword);
+
+#endif /* __MSM_AUDIO_PINCTRL_H */
diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c
new file mode 100644
index 000000000000..e6148c02c4eb
--- /dev/null
+++ b/sound/soc/msm/msm-cpe-lsm.c
@@ -0,0 +1,3358 @@
+/*
+ * Copyright (c) 2013-2017, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/kthread.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/freezer.h>
+#include <sound/soc.h>
+#include <sound/cpe_core.h>
+#include <sound/lsm_params.h>
+#include <sound/pcm_params.h>
+#include <sound/msm-slim-dma.h>
+
+#define SAMPLE_RATE_48KHZ 48000
+#define SAMPLE_RATE_16KHZ 16000
+#define LSM_VOICE_WAKEUP_APP_V2 2
+#define AFE_PORT_ID_1 1
+#define AFE_PORT_ID_3 3
+#define AFE_OUT_PORT_2 2
+#define LISTEN_MIN_NUM_PERIODS 2
+#define LISTEN_MAX_NUM_PERIODS 12
+#define LISTEN_MAX_PERIOD_SIZE 61440
+#define LISTEN_MIN_PERIOD_SIZE 320
+#define LISTEN_MAX_STATUS_PAYLOAD_SIZE 256
+#define MSM_CPE_MAX_CUSTOM_PARAM_SIZE 2048
+
+#define MSM_CPE_LAB_THREAD_TIMEOUT (3 * (HZ/10))
+
+#define MSM_CPE_LSM_GRAB_LOCK(lock, name) \
+{ \
+ pr_debug("%s: %s lock acquire\n", \
+ __func__, name); \
+ mutex_lock(lock); \
+}
+
+#define MSM_CPE_LSM_REL_LOCK(lock, name) \
+{ \
+ pr_debug("%s: %s lock release\n", \
+ __func__, name); \
+ mutex_unlock(lock); \
+}
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 16000, 48000, 192000, 384000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+
+static struct snd_pcm_hardware msm_pcm_hardware_listen = {
+ .info = (SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .rates = (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000),
+ .rate_min = 16000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = LISTEN_MAX_NUM_PERIODS *
+ LISTEN_MAX_PERIOD_SIZE,
+ .period_bytes_min = LISTEN_MIN_PERIOD_SIZE,
+ .period_bytes_max = LISTEN_MAX_PERIOD_SIZE,
+ .periods_min = LISTEN_MIN_NUM_PERIODS,
+ .periods_max = LISTEN_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+enum {
+ AFE_CMD_INVALID = 0,
+ AFE_CMD_PORT_START,
+ AFE_CMD_PORT_SUSPEND,
+ AFE_CMD_PORT_RESUME,
+ AFE_CMD_PORT_STOP,
+};
+
+enum cpe_lab_thread_status {
+ MSM_LSM_LAB_THREAD_STOP,
+ MSM_LSM_LAB_THREAD_RUNNING,
+ MSM_LSM_LAB_THREAD_ERROR,
+};
+
+struct cpe_hw_params {
+ u32 sample_rate;
+ u16 sample_size;
+ u32 buf_sz;
+ u32 period_count;
+ u16 channels;
+};
+
+struct cpe_data_pcm_buf {
+ u8 *mem;
+ phys_addr_t phys;
+};
+
+struct cpe_lsm_lab {
+ atomic_t in_count;
+ atomic_t abort_read;
+ u32 dma_write;
+ u32 buf_idx;
+ u32 pcm_size;
+ enum cpe_lab_thread_status thread_status;
+ struct cpe_data_pcm_buf *pcm_buf;
+ wait_queue_head_t period_wait;
+ struct completion comp;
+ struct completion thread_complete;
+};
+
+struct cpe_priv {
+ void *core_handle;
+ struct snd_soc_codec *codec;
+ struct wcd_cpe_lsm_ops lsm_ops;
+ struct wcd_cpe_afe_ops afe_ops;
+ bool afe_mad_ctl;
+ u32 input_port_id;
+};
+
+struct cpe_lsm_data {
+ struct device *dev;
+ struct cpe_lsm_session *lsm_session;
+ struct mutex lsm_api_lock;
+ struct cpe_lsm_lab lab;
+ struct cpe_hw_params hw_params;
+ struct snd_pcm_substream *substream;
+
+ wait_queue_head_t event_wait;
+ atomic_t event_avail;
+ atomic_t event_stop;
+
+ u8 ev_det_status;
+ u8 ev_det_pld_size;
+ u8 *ev_det_payload;
+
+ bool cpe_prepared;
+};
+
+static int msm_cpe_afe_mad_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cpe_priv *cpe = kcontrol->private_data;
+
+ ucontrol->value.integer.value[0] = cpe->afe_mad_ctl;
+ return 0;
+}
+
+static int msm_cpe_afe_mad_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cpe_priv *cpe = kcontrol->private_data;
+
+ cpe->afe_mad_ctl = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static struct snd_kcontrol_new msm_cpe_kcontrols[] = {
+ SOC_SINGLE_EXT("CPE AFE MAD Enable", SND_SOC_NOPM, 0, 1, 0,
+ msm_cpe_afe_mad_ctl_get, msm_cpe_afe_mad_ctl_put),
+};
+
+/*
+ * cpe_get_private_data: obtain ASoC platform driver private data
+ * @substream: ASoC substream for which private data to be obtained
+ */
+static struct cpe_priv *cpe_get_private_data(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd;
+
+ if (!substream || !substream->private_data) {
+ pr_err("%s: %s is invalid\n",
+ __func__,
+ (!substream) ? "substream" : "private_data");
+ goto err_ret;
+ }
+
+ rtd = substream->private_data;
+
+ if (!rtd || !rtd->platform) {
+ pr_err("%s: %s is invalid\n",
+ __func__,
+ (!rtd) ? "runtime" : "platform");
+ goto err_ret;
+ }
+
+ return snd_soc_platform_get_drvdata(rtd->platform);
+
+err_ret:
+ return NULL;
+}
+
+/*
+ * cpe_get_lsm_data: obtain the lsm session data given the substream
+ * @substream: ASoC substream for which lsm session data to be obtained
+ */
+static struct cpe_lsm_data *cpe_get_lsm_data(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ return runtime->private_data;
+}
+
+static void msm_cpe_process_event_status(void *data,
+ u8 detect_status, u8 size, u8 *payload)
+{
+ struct cpe_lsm_data *lsm_d = data;
+
+ lsm_d->ev_det_status = detect_status;
+ lsm_d->ev_det_pld_size = size;
+
+ lsm_d->ev_det_payload = kzalloc(size, GFP_KERNEL);
+ if (!lsm_d->ev_det_payload) {
+ pr_err("%s: no memory for event payload, size = %u\n",
+ __func__, size);
+ return;
+ }
+ memcpy(lsm_d->ev_det_payload, payload, size);
+
+ atomic_set(&lsm_d->event_avail, 1);
+ wake_up(&lsm_d->event_wait);
+}
+
+static void msm_cpe_process_event_status_done(struct cpe_lsm_data *lsm_data)
+{
+ kfree(lsm_data->ev_det_payload);
+ lsm_data->ev_det_payload = NULL;
+
+ lsm_data->ev_det_status = 0;
+ lsm_data->ev_det_pld_size = 0;
+}
+
+/*
+ * msm_cpe_afe_port_cntl: Perform the afe port control
+ * @substream: substream for which afe port command to be performed
+ * @core_handle: handle to core
+ * @afe_ops: handle to the afe operations
+ * @afe_cfg: afe port configuration data
+ * @cmd: command to be sent to AFE
+ *
+ */
+static int msm_cpe_afe_port_cntl(
+ struct snd_pcm_substream *substream,
+ void *core_handle,
+ struct wcd_cpe_afe_ops *afe_ops,
+ struct wcd_cpe_afe_port_cfg *afe_cfg,
+ int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int rc = 0;
+
+ if (!afe_cfg->port_id) {
+ /*
+ * It is possible driver can get closed without prepare,
+ * in which case afe ports will not be initialized.
+ */
+ dev_dbg(rtd->dev,
+ "%s: Invalid afe port id\n",
+ __func__);
+ return 0;
+ }
+
+ switch (cmd) {
+ case AFE_CMD_PORT_START:
+ rc = afe_ops->afe_port_start(core_handle, afe_cfg);
+ if (rc != 0)
+ dev_err(rtd->dev,
+ "%s: AFE port start failed\n",
+ __func__);
+ break;
+ case AFE_CMD_PORT_SUSPEND:
+ rc = afe_ops->afe_port_suspend(core_handle, afe_cfg);
+ if (rc != 0)
+ dev_err(rtd->dev,
+ "%s: afe_suspend failed, err = %d\n",
+ __func__, rc);
+ break;
+ case AFE_CMD_PORT_RESUME:
+ rc = afe_ops->afe_port_resume(core_handle, afe_cfg);
+ if (rc != 0)
+ dev_err(rtd->dev,
+ "%s: afe_resume failed, err = %d\n",
+ __func__, rc);
+ break;
+ case AFE_CMD_PORT_STOP:
+ rc = afe_ops->afe_port_stop(core_handle, afe_cfg);
+ if (rc != 0)
+ dev_err(rtd->dev,
+ "%s: afe_stopfailed, err = %d\n",
+ __func__, rc);
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_cpe_lsm_lab_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+ struct cpe_priv *cpe = cpe_get_private_data(substream);
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ struct wcd_cpe_afe_ops *afe_ops;
+ struct cpe_lsm_session *session;
+ struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+ struct msm_slim_dma_data *dma_data = NULL;
+ int rc;
+
+ /*
+ * the caller is not aware of LAB status and will
+ * try to stop lab even if it is already stopped.
+ * return success right away is LAB is already stopped
+ */
+ if (lab_d->thread_status == MSM_LSM_LAB_THREAD_STOP) {
+ dev_dbg(rtd->dev,
+ "%s: lab already stopped\n",
+ __func__);
+ return 0;
+ }
+
+ if (!cpe || !cpe->core_handle) {
+ dev_err(rtd->dev,
+ "%s: Invalid private data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!lsm_d->lsm_session) {
+ dev_err(rtd->dev,
+ "%s: Invalid session data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ lsm_ops = &cpe->lsm_ops;
+ afe_ops = &cpe->afe_ops;
+ session = lsm_d->lsm_session;
+ if (rtd->cpu_dai)
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai,
+ substream);
+ if (!dma_data || !dma_data->dai_channel_ctl) {
+ dev_err(rtd->dev,
+ "%s: dma_data is not set\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (lab_d->thread_status == MSM_LSM_LAB_THREAD_RUNNING) {
+ dev_dbg(rtd->dev, "%s: stopping lab thread\n",
+ __func__);
+ rc = kthread_stop(session->lsm_lab_thread);
+
+ /*
+ * kthread_stop returns EINTR if the thread_fn
+ * was not scheduled before calling kthread_stop.
+ * In this case, we dont need to wait for lab
+ * thread to complete as lab thread will not be
+ * scheduled at all.
+ */
+ if (rc == -EINTR)
+ goto done;
+
+ /* Wait for the lab thread to exit */
+ rc = wait_for_completion_timeout(
+ &lab_d->thread_complete,
+ MSM_CPE_LAB_THREAD_TIMEOUT);
+ if (!rc) {
+ dev_err(rtd->dev,
+ "%s: Wait for lab thread timedout\n",
+ __func__);
+ return -ETIMEDOUT;
+ }
+ }
+
+ rc = lsm_ops->lab_ch_setup(cpe->core_handle,
+ session,
+ WCD_CPE_PRE_DISABLE);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: PRE ch teardown failed, err = %d\n",
+ __func__, rc);
+ /* continue with teardown even if any intermediate step fails */
+ rc = dma_data->dai_channel_ctl(dma_data, rtd->cpu_dai, false);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: open data failed %d\n", __func__, rc);
+ dma_data->ph = 0;
+
+ /*
+ * Even though LAB stop failed,
+ * output AFE port needs to be stopped
+ */
+ rc = afe_ops->afe_port_stop(cpe->core_handle,
+ &session->afe_out_port_cfg);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: AFE out port stop failed, err = %d\n",
+ __func__, rc);
+
+ rc = lsm_ops->lab_ch_setup(cpe->core_handle,
+ session,
+ WCD_CPE_POST_DISABLE);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: POST ch teardown failed, err = %d\n",
+ __func__, rc);
+
+done:
+ lab_d->thread_status = MSM_LSM_LAB_THREAD_STOP;
+ lab_d->buf_idx = 0;
+ atomic_set(&lab_d->in_count, 0);
+ lab_d->dma_write = 0;
+
+ return 0;
+}
+
+static int msm_cpe_lab_buf_alloc(struct snd_pcm_substream *substream,
+ struct cpe_lsm_session *session,
+ struct msm_slim_dma_data *dma_data)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+ struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+ struct cpe_hw_params *hw_params = &lsm_d->hw_params;
+ struct cpe_data_pcm_buf *pcm_buf = NULL;
+ int rc = 0;
+ int dma_alloc = 0;
+ u32 count = 0;
+ u32 bufsz, bufcnt;
+
+ if (lab_d->pcm_buf &&
+ lab_d->pcm_buf->mem) {
+ dev_dbg(rtd->dev,
+ "%s: LAB buf already allocated\n",
+ __func__);
+ goto exit;
+ }
+
+ bufsz = hw_params->buf_sz;
+ bufcnt = hw_params->period_count;
+
+ dev_dbg(rtd->dev,
+ "%s:Buf Size %d Buf count %d\n",
+ __func__,
+ bufsz, bufcnt);
+
+ pcm_buf = kzalloc(((sizeof(struct cpe_data_pcm_buf)) * bufcnt),
+ GFP_KERNEL);
+ if (!pcm_buf) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ lab_d->pcm_buf = pcm_buf;
+ dma_alloc = bufsz * bufcnt;
+ pcm_buf->mem = NULL;
+ pcm_buf->mem = dma_alloc_coherent(dma_data->sdev->dev.parent,
+ dma_alloc,
+ &(pcm_buf->phys),
+ GFP_KERNEL);
+ if (!pcm_buf->mem) {
+ dev_err(rtd->dev,
+ "%s:DMA alloc failed size = %x\n",
+ __func__, dma_alloc);
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ count = 0;
+ while (count < bufcnt) {
+ pcm_buf[count].mem = pcm_buf[0].mem + (count * bufsz);
+ pcm_buf[count].phys = pcm_buf[0].phys + (count * bufsz);
+ dev_dbg(rtd->dev,
+ "%s: pcm_buf[%d].mem %pK pcm_buf[%d].phys %pK\n",
+ __func__, count,
+ (void *)pcm_buf[count].mem,
+ count, &(pcm_buf[count].phys));
+ count++;
+ }
+
+ return 0;
+fail:
+ if (pcm_buf) {
+ if (pcm_buf->mem)
+ dma_free_coherent(dma_data->sdev->dev.parent, dma_alloc,
+ pcm_buf->mem, pcm_buf->phys);
+ kfree(pcm_buf);
+ lab_d->pcm_buf = NULL;
+ }
+exit:
+ return rc;
+}
+
+static int msm_cpe_lab_buf_dealloc(struct snd_pcm_substream *substream,
+ struct cpe_lsm_session *session, struct msm_slim_dma_data *dma_data)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+ struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+ struct cpe_hw_params *hw_params = &lsm_d->hw_params;
+ int rc = 0;
+ int dma_alloc = 0;
+ struct cpe_data_pcm_buf *pcm_buf = NULL;
+ int bufsz, bufcnt;
+
+ bufsz = hw_params->buf_sz;
+ bufcnt = hw_params->period_count;
+
+ dev_dbg(rtd->dev,
+ "%s:Buf Size %d Buf count %d\n", __func__,
+ bufsz, bufcnt);
+
+ if (bufcnt <= 0 || bufsz <= 0) {
+ dev_err(rtd->dev,
+ "%s: Invalid params, bufsz = %u, bufcnt = %u\n",
+ __func__, bufsz, bufcnt);
+ return -EINVAL;
+ }
+
+ pcm_buf = lab_d->pcm_buf;
+ dma_alloc = bufsz * bufcnt;
+ if (dma_data && pcm_buf)
+ dma_free_coherent(dma_data->sdev->dev.parent, dma_alloc,
+ pcm_buf->mem, pcm_buf->phys);
+ kfree(pcm_buf);
+ lab_d->pcm_buf = NULL;
+ return rc;
+}
+
+/*
+ * msm_cpe_lab_thread: Initiated on KW detection
+ * @data: lab data
+ *
+ * Start lab thread and call CPE core API for SLIM
+ * read operations.
+ */
+static int msm_cpe_lab_thread(void *data)
+{
+ struct cpe_lsm_data *lsm_d = data;
+ struct cpe_lsm_session *session = lsm_d->lsm_session;
+ struct snd_pcm_substream *substream = lsm_d->substream;
+ struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+ struct cpe_hw_params *hw_params = &lsm_d->hw_params;
+ struct cpe_priv *cpe = cpe_get_private_data(substream);
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ struct wcd_cpe_afe_ops *afe_ops;
+ struct cpe_data_pcm_buf *cur_buf, *next_buf;
+ struct msm_slim_dma_data *dma_data = NULL;
+ struct snd_soc_pcm_runtime *rtd = NULL;
+ bool wait_timedout = false;
+ int rc = 0;
+ u32 done_len = 0;
+ u32 buf_count = 0;
+ u32 prd_cnt;
+
+ allow_signal(SIGKILL);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ pr_debug("%s: Lab thread start\n", __func__);
+ init_completion(&lab_d->comp);
+
+ if (PCM_RUNTIME_CHECK(substream)) {
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (!cpe || !cpe->core_handle) {
+ pr_err("%s: Handle to %s is invalid\n",
+ __func__,
+ (!cpe) ? "cpe" : "core");
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rtd = substream->private_data;
+ if (rtd->cpu_dai)
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai,
+ substream);
+ if (!dma_data || !dma_data->dai_channel_ctl) {
+ pr_err("%s: dma_data is not set\n", __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ lsm_ops = &cpe->lsm_ops;
+ afe_ops = &cpe->afe_ops;
+
+ rc = lsm_ops->lab_ch_setup(cpe->core_handle,
+ session,
+ WCD_CPE_PRE_ENABLE);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: PRE ch setup failed, err = %d\n",
+ __func__, rc);
+ goto done;
+ }
+
+ rc = dma_data->dai_channel_ctl(dma_data, rtd->cpu_dai, true);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: open data failed %d\n", __func__, rc);
+ goto done;
+ }
+
+ dev_dbg(rtd->dev, "%s: Established data channel\n",
+ __func__);
+
+ init_waitqueue_head(&lab_d->period_wait);
+ memset(lab_d->pcm_buf[0].mem, 0, lab_d->pcm_size);
+
+ rc = slim_port_xfer(dma_data->sdev, dma_data->ph,
+ lab_d->pcm_buf[0].phys,
+ hw_params->buf_sz, &lab_d->comp);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: buf[0] slim_port_xfer failed, err = %d\n",
+ __func__, rc);
+ goto done;
+ }
+
+ rc = slim_port_xfer(dma_data->sdev, dma_data->ph,
+ lab_d->pcm_buf[1].phys,
+ hw_params->buf_sz, &lab_d->comp);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: buf[0] slim_port_xfer failed, err = %d\n",
+ __func__, rc);
+ goto done;
+ }
+
+ cur_buf = &lab_d->pcm_buf[0];
+ next_buf = &lab_d->pcm_buf[2];
+ prd_cnt = hw_params->period_count;
+ rc = lsm_ops->lab_ch_setup(cpe->core_handle,
+ session,
+ WCD_CPE_POST_ENABLE);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: POST ch setup failed, err = %d\n",
+ __func__, rc);
+ goto done;
+ }
+
+ rc = afe_ops->afe_port_start(cpe->core_handle,
+ &session->afe_out_port_cfg);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: AFE out port start failed, err = %d\n",
+ __func__, rc);
+ goto done;
+ }
+
+ while (!kthread_should_stop() &&
+ lab_d->thread_status != MSM_LSM_LAB_THREAD_ERROR) {
+
+ rc = slim_port_xfer(dma_data->sdev, dma_data->ph,
+ next_buf->phys,
+ hw_params->buf_sz, &lab_d->comp);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: slim_port_xfer failed, err = %d\n",
+ __func__, rc);
+ lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR;
+ }
+
+ rc = wait_for_completion_timeout(&lab_d->comp, (2 * HZ/10));
+ if (!rc) {
+ dev_err(rtd->dev,
+ "%s: wait timedout for slim buffer\n",
+ __func__);
+ wait_timedout = true;
+ } else {
+ wait_timedout = false;
+ }
+
+ rc = slim_port_get_xfer_status(dma_data->sdev,
+ dma_data->ph,
+ &cur_buf->phys, &done_len);
+ if (rc ||
+ (!rc && wait_timedout)) {
+ dev_err(rtd->dev,
+ "%s: xfer_status failure, rc = %d, wait_timedout = %s\n",
+ __func__, rc,
+ (wait_timedout ? "true" : "false"));
+ lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR;
+ }
+
+ if (done_len ||
+ ((!done_len) &&
+ lab_d->thread_status == MSM_LSM_LAB_THREAD_ERROR)) {
+ atomic_inc(&lab_d->in_count);
+ lab_d->dma_write += snd_pcm_lib_period_bytes(substream);
+ snd_pcm_period_elapsed(substream);
+ wake_up(&lab_d->period_wait);
+ buf_count++;
+
+ cur_buf = &lab_d->pcm_buf[buf_count % prd_cnt];
+ next_buf = &lab_d->pcm_buf[(buf_count + 2) % prd_cnt];
+ dev_dbg(rtd->dev,
+ "%s: Cur buf.mem = %pK Next Buf.mem = %pK\n"
+ " buf count = 0x%x\n", __func__,
+ cur_buf->mem, next_buf->mem, buf_count);
+ } else {
+ dev_err(rtd->dev,
+ "%s: SB get status, invalid len = 0x%x\n",
+ __func__, done_len);
+ }
+ done_len = 0;
+ }
+
+done:
+ if (rc)
+ lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR;
+ pr_debug("%s: Exit lab_thread, exit_status=%d, thread_status=%d\n",
+ __func__, rc, lab_d->thread_status);
+ complete(&lab_d->thread_complete);
+
+ return 0;
+}
+
+/*
+ * msm_cpe_lsm_open: ASoC call to open the stream
+ * @substream: substream that is to be opened
+ *
+ * Create session data for lsm session and open the lsm session
+ * on CPE.
+ */
+static int msm_cpe_lsm_open(struct snd_pcm_substream *substream)
+{
+ struct cpe_lsm_data *lsm_d;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct cpe_priv *cpe = cpe_get_private_data(substream);
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ int rc = 0;
+
+ if (!cpe || !cpe->codec) {
+ dev_err(rtd->dev,
+ "%s: Invalid private data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ runtime->hw = msm_pcm_hardware_listen;
+
+ rc = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (rc < 0) {
+ pr_err("snd_pcm_hw_constraint_list failed rc %d\n", rc);
+ return -EINVAL;
+ }
+
+ /* Ensure that buffer size is a multiple of period size */
+ rc = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (rc < 0) {
+ pr_err("%s: Unable to set pcm_param_periods, rc %d\n",
+ __func__, rc);
+ return -EINVAL;
+ }
+
+ rc = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ LISTEN_MIN_NUM_PERIODS * LISTEN_MIN_PERIOD_SIZE,
+ LISTEN_MAX_NUM_PERIODS * LISTEN_MAX_PERIOD_SIZE);
+ if (rc < 0) {
+ pr_err("%s: Unable to set pcm constraints, rc %d\n",
+ __func__, rc);
+ return -EINVAL;
+ }
+
+ cpe->core_handle = wcd_cpe_get_core_handle(cpe->codec);
+
+ if (!cpe->core_handle) {
+ dev_err(rtd->dev,
+ "%s: Invalid handle to codec core\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ lsm_ops = &cpe->lsm_ops;
+ lsm_d = kzalloc(sizeof(struct cpe_lsm_data), GFP_KERNEL);
+ if (!lsm_d) {
+ dev_err(rtd->dev,
+ "%s: ENOMEM for lsm session, size = %zd\n",
+ __func__, sizeof(struct cpe_lsm_data));
+ rc = -ENOMEM;
+ goto fail_return;
+ }
+ mutex_init(&lsm_d->lsm_api_lock);
+
+ lsm_d->lsm_session = lsm_ops->lsm_alloc_session(cpe->core_handle,
+ lsm_d, msm_cpe_process_event_status);
+ if (!lsm_d->lsm_session) {
+ dev_err(rtd->dev,
+ "%s: session allocation failed",
+ __func__);
+ rc = -EINVAL;
+ goto fail_session_alloc;
+ }
+ /* Explicitly Assign the LAB thread to STOP state */
+ lsm_d->lab.thread_status = MSM_LSM_LAB_THREAD_STOP;
+ lsm_d->lsm_session->started = false;
+ lsm_d->substream = substream;
+ init_waitqueue_head(&lsm_d->lab.period_wait);
+ lsm_d->cpe_prepared = false;
+
+ dev_dbg(rtd->dev, "%s: allocated session with id = %d\n",
+ __func__, lsm_d->lsm_session->id);
+
+
+ rc = lsm_ops->lsm_open_tx(cpe->core_handle, lsm_d->lsm_session,
+ LSM_VOICE_WAKEUP_APP_V2, 16000);
+ if (rc < 0) {
+ dev_err(rtd->dev,
+ "%s: OPEN_TX cmd failed, err = %d\n",
+ __func__, rc);
+ goto fail_open_tx;
+ }
+
+ init_waitqueue_head(&lsm_d->event_wait);
+ atomic_set(&lsm_d->event_avail, 0);
+ atomic_set(&lsm_d->event_stop, 0);
+ runtime->private_data = lsm_d;
+
+ return 0;
+
+fail_open_tx:
+ lsm_ops->lsm_dealloc_session(cpe->core_handle, lsm_d->lsm_session);
+
+fail_session_alloc:
+ mutex_destroy(&lsm_d->lsm_api_lock);
+ kfree(lsm_d);
+fail_return:
+ return rc;
+}
+
+/*
+ * msm_cpe_lsm_close: ASoC call to close/cleanup the stream
+ * @substream: substream that is to be closed
+ *
+ * Deallocate the session and release the AFE port. It is not
+ * required to deregister the sound model as long as we close
+ * the lsm session on CPE.
+ */
+static int msm_cpe_lsm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+ struct cpe_priv *cpe = cpe_get_private_data(substream);
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ struct cpe_lsm_session *session;
+ struct wcd_cpe_afe_ops *afe_ops;
+ struct wcd_cpe_afe_port_cfg *afe_cfg;
+ int rc = 0;
+
+ if (!cpe || !cpe->core_handle) {
+ dev_err(rtd->dev,
+ "%s: Invalid private data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!lsm_d || !lsm_d->lsm_session) {
+ dev_err(rtd->dev,
+ "%s: Invalid session data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ lsm_ops = &cpe->lsm_ops;
+ session = lsm_d->lsm_session;
+ afe_ops = &cpe->afe_ops;
+ afe_cfg = &(lsm_d->lsm_session->afe_port_cfg);
+
+ /*
+ * If driver is closed without stopping LAB,
+ * explicitly stop LAB before cleaning up the
+ * driver resources.
+ */
+ rc = msm_cpe_lsm_lab_stop(substream);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: Failed to stop lab, error = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = msm_cpe_afe_port_cntl(substream,
+ cpe->core_handle,
+ afe_ops, afe_cfg,
+ AFE_CMD_PORT_STOP);
+
+ lsm_d->cpe_prepared = false;
+
+ rc = lsm_ops->lsm_close_tx(cpe->core_handle, session);
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: lsm_close fail, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ lsm_ops->lsm_dealloc_session(cpe->core_handle, session);
+ runtime->private_data = NULL;
+ mutex_destroy(&lsm_d->lsm_api_lock);
+ kfree(lsm_d);
+
+ return rc;
+}
+
+static int msm_cpe_lsm_get_conf_levels(
+ struct cpe_lsm_session *session,
+ u8 *conf_levels_ptr)
+{
+ int rc = 0;
+
+ if (session->num_confidence_levels <= 0) {
+ pr_debug("%s: conf_levels (%u), skip set params\n",
+ __func__,
+ session->num_confidence_levels);
+ goto done;
+ }
+
+ session->conf_levels = kzalloc(session->num_confidence_levels,
+ GFP_KERNEL);
+ if (!session->conf_levels) {
+ pr_err("%s: No memory for confidence levels %u\n",
+ __func__, session->num_confidence_levels);
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(session->conf_levels,
+ conf_levels_ptr,
+ session->num_confidence_levels)) {
+ pr_err("%s: copy_from_user failed for confidence levels %u\n",
+ __func__, session->num_confidence_levels);
+ kfree(session->conf_levels);
+ session->conf_levels = NULL;
+ rc = -EFAULT;
+ goto done;
+ }
+
+done:
+ return rc;
+}
+
+static int msm_cpe_lsm_validate_out_format(
+ struct snd_pcm_substream *substream,
+ struct snd_lsm_output_format_cfg *cfg)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int rc = 0;
+
+ if (!cfg) {
+ dev_err(rtd->dev,
+ "%s: Invalid lsm out cfg\n", __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (cfg->format != LSM_OUT_FORMAT_PCM &&
+ cfg->format != LSM_OUT_FORMAT_ADPCM) {
+ dev_err(rtd->dev,
+ "%s: Invalid format %u\n",
+ __func__, cfg->format);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (cfg->packing != LSM_OUT_DATA_RAW &&
+ cfg->packing != LSM_OUT_DATA_PACKED) {
+ dev_err(rtd->dev,
+ "%s: Invalid packing method %u\n",
+ __func__, cfg->packing);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (cfg->events != LSM_OUT_DATA_EVENTS_DISABLED &&
+ cfg->events != LSM_OUT_DATA_EVENTS_ENABLED) {
+ dev_err(rtd->dev,
+ "%s: Invalid events provided %u\n",
+ __func__, cfg->events);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (cfg->mode != LSM_OUT_TRANSFER_MODE_RT &&
+ cfg->mode != LSM_OUT_TRANSFER_MODE_FTRT) {
+ dev_err(rtd->dev,
+ "%s: Invalid transfer mode %u\n",
+ __func__, cfg->mode);
+ rc = -EINVAL;
+ goto done;
+ }
+
+done:
+ return rc;
+}
+
+/*
+ * msm_cpe_lsm_ioctl_shared: Shared IOCTL for this platform driver
+ * @substream: ASoC substream for which the operation is invoked
+ * @cmd: command for the ioctl
+ * @arg: argument for the ioctl
+ *
+ * Perform dedicated listen functions like register sound model,
+ * deregister sound model, etc
+ * Called with lsm_api_lock acquired.
+ */
+static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_lsm_sound_model_v2 snd_model;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+ struct cpe_priv *cpe = cpe_get_private_data(substream);
+ struct cpe_lsm_session *session;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct msm_slim_dma_data *dma_data = NULL;
+ struct snd_lsm_detection_params det_params;
+ int rc = 0;
+
+ if (!cpe || !cpe->core_handle) {
+ dev_err(rtd->dev,
+ "%s: Invalid private data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!lsm_d || !lsm_d->lsm_session) {
+ dev_err(rtd->dev,
+ "%s: Invalid session data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+
+ switch (cmd) {
+ case SNDRV_LSM_STOP_LAB:
+ dev_dbg(rtd->dev,
+ "%s: %s, lab_enable = %d, lab_thread_ststus = %d\n",
+ __func__, "SNDRV_LSM_STOP_LAB",
+ session->lab_enable,
+ lab_d->thread_status);
+
+ if (session->lab_enable &&
+ lab_d->thread_status != MSM_LSM_LAB_THREAD_STOP) {
+ atomic_inc(&lab_d->abort_read);
+ wake_up(&lab_d->period_wait);
+ rc = msm_cpe_lsm_lab_stop(substream);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: stop LAB failed, error = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ } else if (!session->lab_enable) {
+ dev_dbg(rtd->dev,
+ "%s: LAB already stopped\n",
+ __func__);
+ }
+
+ break;
+
+ case SNDRV_LSM_LAB_CONTROL:
+ if (copy_from_user(&session->lab_enable, (void *)arg,
+ sizeof(u32))) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed, size %zd\n",
+ __func__, sizeof(u32));
+ return -EFAULT;
+ }
+
+ dev_dbg(rtd->dev,
+ "%s: %s, lab_enable = %d\n",
+ __func__, "SNDRV_LSM_LAB_CONTROL",
+ session->lab_enable);
+ if (rtd->cpu_dai)
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai,
+ substream);
+ if (!dma_data || !dma_data->dai_channel_ctl) {
+ dev_err(rtd->dev,
+ "%s: dma_data is not set\n", __func__);
+ return -EINVAL;
+ }
+
+ if (session->lab_enable) {
+ rc = msm_cpe_lab_buf_alloc(substream,
+ session, dma_data);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(rtd->dev,
+ "%s: lab buffer alloc failed, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = lab_d->pcm_buf[0].mem;
+ dma_buf->addr = lab_d->pcm_buf[0].phys;
+ dma_buf->bytes = (lsm_d->hw_params.buf_sz *
+ lsm_d->hw_params.period_count);
+ init_completion(&lab_d->thread_complete);
+ snd_pcm_set_runtime_buffer(substream,
+ &substream->dma_buffer);
+ rc = lsm_ops->lsm_lab_control(cpe->core_handle,
+ session, true);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(rtd->dev,
+ "%s: Lab Enable Failed rc %d\n",
+ __func__, rc);
+ return rc;
+ }
+ } else {
+ /*
+ * It is possible that lab is still enabled
+ * when trying to de-allocate the lab buffer.
+ * Make sure to disable lab before de-allocating
+ * the lab buffer.
+ */
+ rc = msm_cpe_lsm_lab_stop(substream);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(rtd->dev,
+ "%s: LAB stop failed, error = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ /*
+ * Buffer has to be de-allocated even if
+ * lab_control failed.
+ */
+ rc = msm_cpe_lab_buf_dealloc(substream,
+ session, dma_data);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(rtd->dev,
+ "%s: lab buffer free failed, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+ break;
+ case SNDRV_LSM_REG_SND_MODEL_V2:
+ dev_dbg(rtd->dev,
+ "%s: %s\n",
+ __func__, "SNDRV_LSM_REG_SND_MODEL_V2");
+
+ memcpy(&snd_model, arg,
+ sizeof(struct snd_lsm_sound_model_v2));
+
+ session->num_confidence_levels =
+ snd_model.num_confidence_levels;
+ rc = msm_cpe_lsm_get_conf_levels(session,
+ snd_model.confidence_level);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: %s get_conf_levels fail, err = %d\n",
+ __func__, "SNDRV_LSM_REG_SND_MODEL_V2",
+ rc);
+ break;
+ }
+
+ session->snd_model_data = kzalloc(snd_model.data_size,
+ GFP_KERNEL);
+ if (!session->snd_model_data) {
+ dev_err(rtd->dev, "%s: No memory for sound model\n",
+ __func__);
+ kfree(session->conf_levels);
+ session->conf_levels = NULL;
+ return -ENOMEM;
+ }
+ session->snd_model_size = snd_model.data_size;
+
+ if (copy_from_user(session->snd_model_data,
+ snd_model.data, snd_model.data_size)) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed for snd_model\n",
+ __func__);
+ kfree(session->conf_levels);
+ kfree(session->snd_model_data);
+ session->conf_levels = NULL;
+ session->snd_model_data = NULL;
+ return -EFAULT;
+ }
+
+ rc = lsm_ops->lsm_shmem_alloc(cpe->core_handle, session,
+ session->snd_model_size);
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: shared memory allocation failed, err = %d\n",
+ __func__, rc);
+ kfree(session->snd_model_data);
+ kfree(session->conf_levels);
+ session->snd_model_data = NULL;
+ session->conf_levels = NULL;
+ return rc;
+ }
+
+ rc = lsm_ops->lsm_register_snd_model(cpe->core_handle, session,
+ snd_model.detection_mode,
+ snd_model.detect_failure);
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: snd_model_reg failed, err = %d\n",
+ __func__, rc);
+ lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session);
+ kfree(session->snd_model_data);
+ kfree(session->conf_levels);
+ session->snd_model_data = NULL;
+ session->conf_levels = NULL;
+ return rc;
+ }
+
+ break;
+
+ case SNDRV_LSM_DEREG_SND_MODEL:
+ dev_dbg(rtd->dev,
+ "%s: %s\n",
+ __func__, "SNDRV_LSM_DEREG_SND_MODEL");
+
+ if (session->lab_enable) {
+ /*
+ * It is possible that lab is still enabled
+ * when trying to deregister sound model.
+ * Make sure to disable lab before de-allocating
+ * the lab buffer.
+ */
+ rc = msm_cpe_lsm_lab_stop(substream);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: LAB stop failed, error = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = lsm_ops->lsm_lab_control(cpe->core_handle,
+ session, false);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: Lab Disable Failed rc %d\n",
+ __func__, rc);
+
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai,
+ substream);
+ if (!dma_data || !dma_data->dai_channel_ctl)
+ dev_err(rtd->dev,
+ "%s: dma_data is not set\n", __func__);
+
+ /*
+ * Buffer has to be de-allocated even if
+ * lab_control failed and/or dma data is invalid.
+ */
+ rc = msm_cpe_lab_buf_dealloc(substream,
+ session, dma_data);
+ if (IS_ERR_VALUE(rc))
+ dev_err(rtd->dev,
+ "%s: lab buffer free failed, err = %d\n",
+ __func__, rc);
+ }
+
+ rc = lsm_ops->lsm_deregister_snd_model(
+ cpe->core_handle, session);
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: snd_model de-reg failed, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ kfree(session->snd_model_data);
+ kfree(session->conf_levels);
+ session->snd_model_data = NULL;
+ session->conf_levels = NULL;
+
+ rc = lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session);
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: LSM shared memory dealloc failed, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ break;
+
+ case SNDRV_LSM_EVENT_STATUS:
+ case SNDRV_LSM_EVENT_STATUS_V3: {
+ struct snd_lsm_event_status *user;
+ struct snd_lsm_event_status_v3 *user_v3;
+
+ dev_dbg(rtd->dev,
+ "%s: %s\n",
+ __func__, "SNDRV_LSM_EVENT_STATUS(_V3)");
+ if (!arg) {
+ dev_err(rtd->dev,
+ "%s: Invalid argument to ioctl %s\n",
+ __func__,
+ "SNDRV_LSM_EVENT_STATUS(_V3)");
+ return -EINVAL;
+ }
+
+ /*
+ * Release the api lock before wait to allow
+ * other IOCTLs to be invoked while waiting
+ * for event
+ */
+ MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock,
+ "lsm_api_lock");
+
+ rc = wait_event_freezable(lsm_d->event_wait,
+ (atomic_read(&lsm_d->event_avail) == 1) ||
+ (atomic_read(&lsm_d->event_stop) == 1));
+
+ MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock,
+ "lsm_api_lock");
+
+ if (!rc) {
+ if (atomic_read(&lsm_d->event_avail) == 1) {
+ rc = 0;
+ atomic_set(&lsm_d->event_avail, 0);
+
+ if (cmd == SNDRV_LSM_EVENT_STATUS) {
+ user = arg;
+ if (lsm_d->ev_det_pld_size >
+ user->payload_size) {
+ dev_err(rtd->dev,
+ "%s: avail pld_bytes = %u, needed = %u\n",
+ __func__,
+ user->payload_size,
+ lsm_d->ev_det_pld_size);
+ return -EINVAL;
+ }
+
+ user->status = lsm_d->ev_det_status;
+ user->payload_size =
+ lsm_d->ev_det_pld_size;
+ memcpy(user->payload,
+ lsm_d->ev_det_payload,
+ lsm_d->ev_det_pld_size);
+ } else {
+ user_v3 = arg;
+ if (lsm_d->ev_det_pld_size >
+ user_v3->payload_size) {
+ dev_err(rtd->dev,
+ "%s: avail pld_bytes = %u, needed = %u\n",
+ __func__,
+ user_v3->payload_size,
+ lsm_d->ev_det_pld_size);
+ return -EINVAL;
+ }
+ /* event status timestamp not supported
+ * on CPE mode. Set msw and lsw to 0.
+ */
+ user_v3->timestamp_lsw = 0;
+ user_v3->timestamp_msw = 0;
+ user_v3->status = lsm_d->ev_det_status;
+ user_v3->payload_size =
+ lsm_d->ev_det_pld_size;
+ memcpy(user_v3->payload,
+ lsm_d->ev_det_payload,
+ lsm_d->ev_det_pld_size);
+ }
+ } else if (atomic_read(&lsm_d->event_stop) == 1) {
+ dev_dbg(rtd->dev,
+ "%s: wait_aborted\n", __func__);
+ if (cmd == SNDRV_LSM_EVENT_STATUS) {
+ user = arg;
+ user->payload_size = 0;
+ } else {
+ user_v3 = arg;
+ user_v3->payload_size = 0;
+ }
+ rc = 0;
+ }
+ }
+ }
+ break;
+
+ case SNDRV_LSM_ABORT_EVENT:
+ dev_dbg(rtd->dev,
+ "%s: %s\n",
+ __func__, "SNDRV_LSM_ABORT_EVENT");
+ atomic_set(&lsm_d->event_stop, 1);
+ wake_up(&lsm_d->event_wait);
+ break;
+
+ case SNDRV_LSM_START:
+ dev_dbg(rtd->dev,
+ "%s: %s\n",
+ __func__, "SNDRV_LSM_START");
+ rc = lsm_ops->lsm_start(cpe->core_handle, session);
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: lsm_start fail, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ session->started = true;
+ break;
+
+ case SNDRV_LSM_STOP:
+ dev_dbg(rtd->dev,
+ "%s: %s, lab_enable = %d, lab_thread_status = %d\n",
+ __func__, "SNDRV_LSM_STOP",
+ session->lab_enable,
+ lab_d->thread_status);
+ if ((session->lab_enable &&
+ lab_d->thread_status ==
+ MSM_LSM_LAB_THREAD_RUNNING)) {
+ /* Explicitly stop LAB */
+ rc = msm_cpe_lsm_lab_stop(substream);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: lab_stop failed, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ rc = lsm_ops->lsm_stop(cpe->core_handle, session);
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: lsm_stop fail err = %d\n",
+ __func__, rc);
+
+ return rc;
+ }
+ session->started = false;
+ break;
+
+ case SNDRV_LSM_SET_PARAMS:
+ memcpy(&det_params, arg,
+ sizeof(det_params));
+ if (det_params.num_confidence_levels <= 0) {
+ dev_err(rtd->dev,
+ "%s: %s: Invalid confidence levels %u\n",
+ __func__, "SNDRV_LSM_SET_PARAMS",
+ det_params.num_confidence_levels);
+ return -EINVAL;
+ }
+
+ session->num_confidence_levels =
+ det_params.num_confidence_levels;
+ rc = msm_cpe_lsm_get_conf_levels(session,
+ det_params.conf_level);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: %s get_conf_levels fail, err = %d\n",
+ __func__, "SNDRV_LSM_SET_PARAMS",
+ rc);
+ break;
+ }
+
+ rc = lsm_ops->lsm_set_data(cpe->core_handle, session,
+ det_params.detect_mode,
+ det_params.detect_failure);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: lsm_set_data failed, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ kfree(session->conf_levels);
+ session->conf_levels = NULL;
+
+ break;
+
+ case SNDRV_LSM_OUT_FORMAT_CFG: {
+ struct snd_lsm_output_format_cfg u_fmt_cfg;
+
+ if (!arg) {
+ dev_err(rtd->dev,
+ "%s: Invalid argument to ioctl %s\n",
+ __func__, "SNDRV_LSM_OUT_FORMAT_CFG");
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&u_fmt_cfg, arg,
+ sizeof(u_fmt_cfg))) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed for out_fmt_cfg\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ if (msm_cpe_lsm_validate_out_format(substream,
+ &u_fmt_cfg))
+ return -EINVAL;
+
+ session->out_fmt_cfg.format = u_fmt_cfg.format;
+ session->out_fmt_cfg.pack_mode = u_fmt_cfg.packing;
+ session->out_fmt_cfg.data_path_events =
+ u_fmt_cfg.events;
+ session->out_fmt_cfg.transfer_mode = u_fmt_cfg.mode;
+
+ rc = lsm_ops->lsm_set_fmt_cfg(cpe->core_handle,
+ session);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: lsm_set_fmt_cfg failed, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+ break;
+
+ case SNDRV_LSM_SET_PORT: {
+ u32 port_id = cpe->input_port_id;
+
+ dev_dbg(rtd->dev, "%s: %s\n", __func__, "SNDRV_LSM_SET_PORT");
+ rc = lsm_ops->lsm_set_port(cpe->core_handle, session, &port_id);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: lsm_set_port failed, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+ break;
+
+ default:
+ dev_dbg(rtd->dev,
+ "%s: Default snd_lib_ioctl cmd 0x%x\n",
+ __func__, cmd);
+ rc = snd_pcm_lib_ioctl(substream, cmd, arg);
+ }
+
+ return rc;
+}
+
+static int msm_cpe_lsm_lab_start(struct snd_pcm_substream *substream,
+ u16 event_det_status)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct cpe_lsm_data *lsm_d = NULL;
+ struct cpe_priv *cpe = NULL;
+ struct cpe_lsm_session *session = NULL;
+ struct cpe_lsm_lab *lab_d = NULL;
+ struct cpe_hw_params *hw_params;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ struct wcd_cpe_afe_ops *afe_ops;
+ struct wcd_cpe_afe_port_cfg *out_port;
+ int rc;
+
+ if (!substream || !substream->private_data) {
+ pr_err("%s: invalid substream (%pK)\n",
+ __func__, substream);
+ return -EINVAL;
+ }
+
+ rtd = substream->private_data;
+ lsm_d = cpe_get_lsm_data(substream);
+ cpe = cpe_get_private_data(substream);
+
+ if (!cpe || !cpe->core_handle) {
+ dev_err(rtd->dev,
+ "%s: Invalid private data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!lsm_d || !lsm_d->lsm_session) {
+ dev_err(rtd->dev,
+ "%s: Invalid session data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+ lab_d = &lsm_d->lab;
+ afe_ops = &cpe->afe_ops;
+ hw_params = &lsm_d->hw_params;
+
+ if (!session->started) {
+ dev_dbg(rtd->dev,
+ "%s: Session is stopped, cannot start LAB\n",
+ __func__);
+ return 0;
+ }
+
+ reinit_completion(&lab_d->thread_complete);
+
+ if (session->lab_enable &&
+ event_det_status ==
+ LSM_VOICE_WAKEUP_STATUS_DETECTED) {
+ out_port = &session->afe_out_port_cfg;
+ out_port->port_id = session->afe_out_port_id;
+ out_port->bit_width = hw_params->sample_size;
+ out_port->num_channels = hw_params->channels;
+ out_port->sample_rate = hw_params->sample_rate;
+ dev_dbg(rtd->dev, "%s: port_id= %u, bit_width= %u, rate= %u\n",
+ __func__, out_port->port_id, out_port->bit_width,
+ out_port->sample_rate);
+
+ rc = afe_ops->afe_port_cmd_cfg(cpe->core_handle,
+ out_port);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: Failed afe generic config v2, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ atomic_set(&lab_d->abort_read, 0);
+ dev_dbg(rtd->dev,
+ "%s: KW detected, scheduling LAB thread\n",
+ __func__);
+
+ /*
+ * Even though thread might be only scheduled and
+ * not currently running, mark the internal driver
+ * status to running so driver can cancel this thread
+ * if it needs to before the thread gets chance to run.
+ */
+ lab_d->thread_status = MSM_LSM_LAB_THREAD_RUNNING;
+ session->lsm_lab_thread = kthread_run(
+ msm_cpe_lab_thread,
+ lsm_d,
+ "lab_thread");
+ }
+
+ return 0;
+}
+
+static bool msm_cpe_lsm_is_valid_stream(struct snd_pcm_substream *substream,
+ const char *func)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct cpe_lsm_data *lsm_d = NULL;
+ struct cpe_priv *cpe = NULL;
+ struct cpe_lsm_session *session = NULL;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+
+ if (!substream || !substream->private_data) {
+ pr_err("%s: invalid substream (%pK)\n",
+ func, substream);
+ return false;
+ }
+
+ rtd = substream->private_data;
+ lsm_d = cpe_get_lsm_data(substream);
+ cpe = cpe_get_private_data(substream);
+
+ if (!cpe || !cpe->core_handle) {
+ dev_err(rtd->dev,
+ "%s: Invalid private data\n",
+ func);
+ return false;
+ }
+
+ if (!lsm_d || !lsm_d->lsm_session) {
+ dev_err(rtd->dev,
+ "%s: Invalid session data\n",
+ func);
+ return false;
+ }
+
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+
+ if (!lsm_ops) {
+ dev_err(rtd->dev,
+ "%s: Invalid lsm_ops\n", func);
+ return false;
+ }
+
+ return true;
+}
+
+static int msm_cpe_lsm_set_epd(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct cpe_lsm_data *lsm_d = NULL;
+ struct cpe_priv *cpe = NULL;
+ struct cpe_lsm_session *session = NULL;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ struct snd_lsm_ep_det_thres epd_thres;
+ int rc;
+
+ if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+ return -EINVAL;
+
+ rtd = substream->private_data;
+ lsm_d = cpe_get_lsm_data(substream);
+ cpe = cpe_get_private_data(substream);
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+
+ if (p_info->param_size != sizeof(epd_thres)) {
+ dev_err(rtd->dev,
+ "%s: Invalid param_size %d\n",
+ __func__, p_info->param_size);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&epd_thres, p_info->param_data,
+ p_info->param_size)) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed, size = %d\n",
+ __func__, p_info->param_size);
+ rc = -EFAULT;
+ goto done;
+ }
+
+ rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+ session, p_info, &epd_thres,
+ LSM_ENDPOINT_DETECT_THRESHOLD);
+ if (unlikely(rc))
+ dev_err(rtd->dev,
+ "%s: set_one_param(epd_threshold) failed, rc %d\n",
+ __func__, rc);
+done:
+ return rc;
+}
+
+static int msm_cpe_lsm_set_mode(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct cpe_lsm_data *lsm_d = NULL;
+ struct cpe_priv *cpe = NULL;
+ struct cpe_lsm_session *session = NULL;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ struct snd_lsm_detect_mode det_mode;
+ int rc;
+
+ if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+ return -EINVAL;
+
+ rtd = substream->private_data;
+ lsm_d = cpe_get_lsm_data(substream);
+ cpe = cpe_get_private_data(substream);
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+
+ if (p_info->param_size != sizeof(det_mode)) {
+ dev_err(rtd->dev,
+ "%s: Invalid param_size %d\n",
+ __func__, p_info->param_size);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&det_mode, p_info->param_data,
+ p_info->param_size)) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed, size = %d\n",
+ __func__, p_info->param_size);
+ rc = -EFAULT;
+ goto done;
+ }
+
+ rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+ session, p_info, &det_mode,
+ LSM_OPERATION_MODE);
+ if (unlikely(rc))
+ dev_err(rtd->dev,
+ "%s: set_one_param(epd_threshold) failed, rc %d\n",
+ __func__, rc);
+done:
+ return rc;
+}
+
+static int msm_cpe_lsm_set_gain(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct cpe_lsm_data *lsm_d = NULL;
+ struct cpe_priv *cpe = NULL;
+ struct cpe_lsm_session *session = NULL;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ struct snd_lsm_gain gain;
+ int rc;
+
+ if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+ return -EINVAL;
+
+ rtd = substream->private_data;
+ lsm_d = cpe_get_lsm_data(substream);
+ cpe = cpe_get_private_data(substream);
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+
+ if (p_info->param_size != sizeof(gain)) {
+ dev_err(rtd->dev,
+ "%s: Invalid param_size %d\n",
+ __func__, p_info->param_size);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&gain, p_info->param_data,
+ p_info->param_size)) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed, size = %d\n",
+ __func__, p_info->param_size);
+ rc = -EFAULT;
+ goto done;
+ }
+
+ rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+ session, p_info, &gain,
+ LSM_GAIN);
+ if (unlikely(rc))
+ dev_err(rtd->dev,
+ "%s: set_one_param(epd_threshold) failed, rc %d\n",
+ __func__, rc);
+done:
+ return rc;
+
+}
+
+static int msm_cpe_lsm_set_conf(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct cpe_lsm_data *lsm_d = NULL;
+ struct cpe_priv *cpe = NULL;
+ struct cpe_lsm_session *session = NULL;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ int rc;
+
+ if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+ return -EINVAL;
+
+ rtd = substream->private_data;
+ lsm_d = cpe_get_lsm_data(substream);
+ cpe = cpe_get_private_data(substream);
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+
+ session->num_confidence_levels =
+ p_info->param_size;
+ rc = msm_cpe_lsm_get_conf_levels(session,
+ p_info->param_data);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: get_conf_levels failed, err = %d\n",
+ __func__, rc);
+ goto done;
+ }
+
+ rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+ session, p_info, NULL,
+ LSM_MIN_CONFIDENCE_LEVELS);
+ if (unlikely(rc))
+ dev_err(rtd->dev,
+ "%s: set_one_param(conf_levels) failed, rc %d\n",
+ __func__, rc);
+done:
+ return rc;
+}
+
+static int msm_cpe_lsm_reg_model(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct cpe_lsm_data *lsm_d = NULL;
+ struct cpe_priv *cpe = NULL;
+ struct cpe_lsm_session *session = NULL;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ int rc;
+ size_t offset;
+ u8 *snd_model_ptr;
+
+ if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+ return -EINVAL;
+
+ rtd = substream->private_data;
+ lsm_d = cpe_get_lsm_data(substream);
+ cpe = cpe_get_private_data(substream);
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+
+ lsm_ops->lsm_get_snd_model_offset(cpe->core_handle,
+ session, &offset);
+ /* Check if 'p_info->param_size + offset' crosses U32_MAX. */
+ if (p_info->param_size > U32_MAX - offset) {
+ dev_err(rtd->dev,
+ "%s: Invalid param_size %d\n",
+ __func__, p_info->param_size);
+ return -EINVAL;
+ }
+ session->snd_model_size = p_info->param_size + offset;
+
+ session->snd_model_data = vzalloc(session->snd_model_size);
+ if (!session->snd_model_data)
+ return -ENOMEM;
+ snd_model_ptr = ((u8 *) session->snd_model_data) + offset;
+
+ if (copy_from_user(snd_model_ptr,
+ p_info->param_data, p_info->param_size)) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user for snd_model failed\n",
+ __func__);
+ rc = -EFAULT;
+ goto free_snd_model_data;
+ }
+
+ rc = lsm_ops->lsm_shmem_alloc(cpe->core_handle, session,
+ session->snd_model_size);
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: shared memory allocation failed, err = %d\n",
+ __func__, rc);
+ rc = -EINVAL;
+ goto free_snd_model_data;
+ }
+
+ rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+ session, p_info, NULL,
+ LSM_REG_SND_MODEL);
+ if (unlikely(rc)) {
+ dev_err(rtd->dev,
+ "%s: set_one_param(snd_model) failed, rc %d\n",
+ __func__, rc);
+ goto dealloc_shmem;
+ }
+ return 0;
+
+dealloc_shmem:
+ lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session);
+
+free_snd_model_data:
+ vfree(session->snd_model_data);
+ return rc;
+}
+
+static int msm_cpe_lsm_dereg_model(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct cpe_lsm_data *lsm_d = NULL;
+ struct cpe_priv *cpe = NULL;
+ struct cpe_lsm_session *session = NULL;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ int rc;
+
+ if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+ return -EINVAL;
+
+ rtd = substream->private_data;
+ lsm_d = cpe_get_lsm_data(substream);
+ cpe = cpe_get_private_data(substream);
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+
+ rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+ session, p_info, NULL,
+ LSM_DEREG_SND_MODEL);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: dereg_snd_model failed\n",
+ __func__);
+ return lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session);
+}
+
+static int msm_cpe_lsm_set_custom(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct cpe_lsm_data *lsm_d = NULL;
+ struct cpe_priv *cpe = NULL;
+ struct cpe_lsm_session *session = NULL;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+ u8 *data;
+ int rc;
+
+ if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+ return -EINVAL;
+
+ rtd = substream->private_data;
+ lsm_d = cpe_get_lsm_data(substream);
+ cpe = cpe_get_private_data(substream);
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+
+ if (p_info->param_size > MSM_CPE_MAX_CUSTOM_PARAM_SIZE) {
+ dev_err(rtd->dev,
+ "%s: invalid size %d, max allowed %d\n",
+ __func__, p_info->param_size,
+ MSM_CPE_MAX_CUSTOM_PARAM_SIZE);
+ return -EINVAL;
+ }
+
+ data = kzalloc(p_info->param_size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (copy_from_user(data, p_info->param_data,
+ p_info->param_size)) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed for custom params, size = %d\n",
+ __func__, p_info->param_size);
+ rc = -EFAULT;
+ goto err_ret;
+ }
+
+ rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+ session, p_info, data,
+ LSM_CUSTOM_PARAMS);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: custom_params failed, err = %d\n",
+ __func__, rc);
+err_ret:
+ kfree(data);
+ return rc;
+}
+
+static int msm_cpe_lsm_process_params(struct snd_pcm_substream *substream,
+ struct snd_lsm_module_params *p_data,
+ void *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct lsm_params_info *p_info;
+ int i;
+ int rc = 0;
+
+ p_info = (struct lsm_params_info *) params;
+
+ for (i = 0; i < p_data->num_params; i++) {
+ dev_dbg(rtd->dev,
+ "%s: param (%d), module_id = 0x%x, param_id = 0x%x, param_size = 0x%x, param_type = 0x%x\n",
+ __func__, i, p_info->module_id,
+ p_info->param_id, p_info->param_size,
+ p_info->param_type);
+
+ switch (p_info->param_type) {
+ case LSM_ENDPOINT_DETECT_THRESHOLD:
+ rc = msm_cpe_lsm_set_epd(substream, p_info);
+ break;
+ case LSM_OPERATION_MODE:
+ rc = msm_cpe_lsm_set_mode(substream, p_info);
+ break;
+ case LSM_GAIN:
+ rc = msm_cpe_lsm_set_gain(substream, p_info);
+ break;
+ case LSM_MIN_CONFIDENCE_LEVELS:
+ rc = msm_cpe_lsm_set_conf(substream, p_info);
+ break;
+ case LSM_REG_SND_MODEL:
+ rc = msm_cpe_lsm_reg_model(substream, p_info);
+ break;
+ case LSM_DEREG_SND_MODEL:
+ rc = msm_cpe_lsm_dereg_model(substream, p_info);
+ break;
+ case LSM_CUSTOM_PARAMS:
+ rc = msm_cpe_lsm_set_custom(substream, p_info);
+ break;
+ default:
+ dev_err(rtd->dev,
+ "%s: Invalid param_type %d\n",
+ __func__, p_info->param_type);
+ rc = -EINVAL;
+ break;
+ }
+ if (rc) {
+ pr_err("%s: set_param fail for param_type %d\n",
+ __func__, p_info->param_type);
+ return rc;
+ }
+
+ p_info++;
+ }
+
+ return rc;
+}
+
+static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ int err = 0;
+ struct snd_soc_pcm_runtime *rtd;
+ struct cpe_priv *cpe = NULL;
+ struct cpe_lsm_data *lsm_d = NULL;
+ struct cpe_lsm_session *session = NULL;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+
+ if (!substream || !substream->private_data) {
+ pr_err("%s: invalid substream (%pK)\n",
+ __func__, substream);
+ return -EINVAL;
+ }
+
+ rtd = substream->private_data;
+ lsm_d = cpe_get_lsm_data(substream);
+ cpe = cpe_get_private_data(substream);
+
+ if (!cpe || !cpe->core_handle) {
+ dev_err(rtd->dev,
+ "%s: Invalid private data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!lsm_d || !lsm_d->lsm_session) {
+ dev_err(rtd->dev,
+ "%s: Invalid session data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock,
+ "lsm_api_lock");
+
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+
+ switch (cmd) {
+ case SNDRV_LSM_REG_SND_MODEL_V2: {
+ struct snd_lsm_sound_model_v2 snd_model;
+
+ if (session->is_topology_used) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if using topology\n",
+ __func__, "LSM_REG_SND_MODEL_V2");
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&snd_model, (void *)arg,
+ sizeof(struct snd_lsm_sound_model_v2))) {
+ dev_err(rtd->dev,
+ "%s: copy from user failed, size %zd\n",
+ __func__,
+ sizeof(struct snd_lsm_sound_model_v2));
+ err = -EFAULT;
+ goto done;
+ }
+
+ err = msm_cpe_lsm_ioctl_shared(substream, cmd,
+ &snd_model);
+ }
+ break;
+ case SNDRV_LSM_EVENT_STATUS: {
+ struct snd_lsm_event_status u_event_status;
+ struct snd_lsm_event_status *event_status = NULL;
+ int u_pld_size = 0;
+
+ if (copy_from_user(&u_event_status, (void *)arg,
+ sizeof(struct snd_lsm_event_status))) {
+ dev_err(rtd->dev,
+ "%s: event status copy from user failed, size %zd\n",
+ __func__,
+ sizeof(struct snd_lsm_event_status));
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (u_event_status.payload_size >
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+ dev_err(rtd->dev,
+ "%s: payload_size %d is invalid, max allowed = %d\n",
+ __func__, u_event_status.payload_size,
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+ err = -EINVAL;
+ goto done;
+ }
+
+ u_pld_size = sizeof(struct snd_lsm_event_status) +
+ u_event_status.payload_size;
+
+ event_status = kzalloc(u_pld_size, GFP_KERNEL);
+ if (!event_status) {
+ dev_err(rtd->dev,
+ "%s: No memory for event status\n",
+ __func__);
+ err = -ENOMEM;
+ goto done;
+ } else {
+ event_status->payload_size =
+ u_event_status.payload_size;
+ err = msm_cpe_lsm_ioctl_shared(substream,
+ cmd, event_status);
+ }
+
+ if (!err && copy_to_user(arg, event_status, u_pld_size)) {
+ dev_err(rtd->dev,
+ "%s: copy to user failed\n",
+ __func__);
+ kfree(event_status);
+ err = -EFAULT;
+ goto done;
+ }
+
+ msm_cpe_lsm_lab_start(substream, event_status->status);
+ msm_cpe_process_event_status_done(lsm_d);
+ kfree(event_status);
+ }
+ break;
+ case SNDRV_LSM_EVENT_STATUS_V3: {
+ struct snd_lsm_event_status_v3 u_event_status;
+ struct snd_lsm_event_status_v3 *event_status = NULL;
+ int u_pld_size = 0;
+
+ if (copy_from_user(&u_event_status, (void *)arg,
+ sizeof(struct snd_lsm_event_status_v3))) {
+ dev_err(rtd->dev,
+ "%s: event status copy from user failed, size %zd\n",
+ __func__,
+ sizeof(struct snd_lsm_event_status_v3));
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (u_event_status.payload_size >
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+ dev_err(rtd->dev,
+ "%s: payload_size %d is invalid, max allowed = %d\n",
+ __func__, u_event_status.payload_size,
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+ err = -EINVAL;
+ goto done;
+ }
+
+ u_pld_size = sizeof(struct snd_lsm_event_status_v3) +
+ u_event_status.payload_size;
+
+ event_status = kzalloc(u_pld_size, GFP_KERNEL);
+ if (!event_status) {
+ err = -ENOMEM;
+ goto done;
+ } else {
+ event_status->payload_size =
+ u_event_status.payload_size;
+ err = msm_cpe_lsm_ioctl_shared(substream,
+ cmd, event_status);
+ }
+
+ if (!err && copy_to_user(arg, event_status, u_pld_size)) {
+ dev_err(rtd->dev,
+ "%s: copy to user failed\n",
+ __func__);
+ kfree(event_status);
+ err = -EFAULT;
+ goto done;
+ }
+
+ msm_cpe_lsm_lab_start(substream, event_status->status);
+ msm_cpe_process_event_status_done(lsm_d);
+ kfree(event_status);
+ }
+ break;
+ case SNDRV_LSM_SET_PARAMS: {
+ struct snd_lsm_detection_params det_params;
+
+ if (session->is_topology_used) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if using topology\n",
+ __func__, "SNDRV_LSM_SET_PARAMS");
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&det_params, (void *) arg,
+ sizeof(det_params))) {
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size = %zd\n",
+ __func__, "SNDRV_LSM_SET_PARAMS",
+ sizeof(det_params));
+ err = -EFAULT;
+ goto done;
+ }
+
+ err = msm_cpe_lsm_ioctl_shared(substream, cmd,
+ &det_params);
+ }
+ break;
+
+ case SNDRV_LSM_SET_MODULE_PARAMS: {
+ struct snd_lsm_module_params p_data;
+ size_t p_size;
+ u8 *params;
+
+ if (!session->is_topology_used) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if not using topology\n",
+ __func__, "SET_MODULE_PARAMS");
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (!arg) {
+ dev_err(rtd->dev,
+ "%s: %s: No Param data to set\n",
+ __func__, "SET_MODULE_PARAMS");
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&p_data, arg,
+ sizeof(p_data))) {
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size = %zd\n",
+ __func__, "p_data", sizeof(p_data));
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (p_data.num_params > LSM_PARAMS_MAX) {
+ dev_err(rtd->dev,
+ "%s: %s: Invalid num_params %d\n",
+ __func__, "SET_MODULE_PARAMS",
+ p_data.num_params);
+ err = -EINVAL;
+ goto done;
+ }
+
+ p_size = p_data.num_params *
+ sizeof(struct lsm_params_info);
+
+ if (p_data.data_size != p_size) {
+ dev_err(rtd->dev,
+ "%s: %s: Invalid size %zd\n",
+ __func__, "SET_MODULE_PARAMS", p_size);
+
+ err = -EFAULT;
+ goto done;
+ }
+
+ params = kzalloc(p_size, GFP_KERNEL);
+ if (!params) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(params, p_data.params,
+ p_data.data_size)) {
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size = %d\n",
+ __func__, "params", p_data.data_size);
+ kfree(params);
+ err = -EFAULT;
+ goto done;
+ }
+
+ err = msm_cpe_lsm_process_params(substream, &p_data, params);
+ if (err)
+ dev_err(rtd->dev,
+ "%s: %s: Failed to set params, err = %d\n",
+ __func__, "SET_MODULE_PARAMS", err);
+ kfree(params);
+ break;
+ }
+ default:
+ err = msm_cpe_lsm_ioctl_shared(substream, cmd, arg);
+ break;
+ }
+
+done:
+ MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock,
+ "lsm_api_lock");
+ return err;
+}
+
+#ifdef CONFIG_COMPAT
+struct snd_lsm_sound_model_v2_32 {
+ compat_uptr_t data;
+ compat_uptr_t confidence_level;
+ u32 data_size;
+ enum lsm_detection_mode detection_mode;
+ u8 num_confidence_levels;
+ bool detect_failure;
+};
+
+struct snd_lsm_detection_params_32 {
+ compat_uptr_t conf_level;
+ enum lsm_detection_mode detect_mode;
+ u8 num_confidence_levels;
+ bool detect_failure;
+};
+
+struct lsm_params_info_32 {
+ u32 module_id;
+ u32 param_id;
+ u32 param_size;
+ compat_uptr_t param_data;
+ uint32_t param_type;
+};
+
+struct snd_lsm_module_params_32 {
+ compat_uptr_t params;
+ u32 num_params;
+ u32 data_size;
+};
+
+enum {
+ SNDRV_LSM_REG_SND_MODEL_V2_32 =
+ _IOW('U', 0x07, struct snd_lsm_sound_model_v2_32),
+ SNDRV_LSM_SET_PARAMS32 =
+ _IOW('U', 0x0A, struct snd_lsm_detection_params_32),
+ SNDRV_LSM_SET_MODULE_PARAMS_32 =
+ _IOW('U', 0x0B, struct snd_lsm_module_params_32),
+};
+
+static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ int err = 0;
+ struct snd_soc_pcm_runtime *rtd;
+ struct cpe_priv *cpe = NULL;
+ struct cpe_lsm_data *lsm_d = NULL;
+ struct cpe_lsm_session *session = NULL;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+
+ if (!substream || !substream->private_data) {
+ pr_err("%s: invalid substream (%pK)\n",
+ __func__, substream);
+ return -EINVAL;
+ }
+
+ rtd = substream->private_data;
+ lsm_d = cpe_get_lsm_data(substream);
+ cpe = cpe_get_private_data(substream);
+
+ if (!cpe || !cpe->core_handle) {
+ dev_err(rtd->dev,
+ "%s: Invalid private data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!lsm_d || !lsm_d->lsm_session) {
+ dev_err(rtd->dev,
+ "%s: Invalid session data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock,
+ "lsm_api_lock");
+
+ session = lsm_d->lsm_session;
+ lsm_ops = &cpe->lsm_ops;
+
+ switch (cmd) {
+ case SNDRV_LSM_REG_SND_MODEL_V2_32: {
+ struct snd_lsm_sound_model_v2 snd_model;
+ struct snd_lsm_sound_model_v2_32 snd_model32;
+
+ if (session->is_topology_used) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if using topology\n",
+ __func__, "LSM_REG_SND_MODEL_V2_32");
+ err = -EINVAL;
+ goto done;
+ }
+
+ dev_dbg(rtd->dev,
+ "%s: ioctl %s\n", __func__,
+ "SNDRV_LSM_REG_SND_MODEL_V2_32");
+
+ if (copy_from_user(&snd_model32, (void *)arg,
+ sizeof(snd_model32))) {
+ dev_err(rtd->dev,
+ "%s: copy from user failed, size %zd\n",
+ __func__,
+ sizeof(snd_model32));
+ err = -EFAULT;
+ goto done;
+ }
+
+ snd_model.data = compat_ptr(snd_model32.data);
+ snd_model.confidence_level =
+ compat_ptr(snd_model32.confidence_level);
+ snd_model.data_size = snd_model32.data_size;
+ snd_model.detect_failure = snd_model32.detect_failure;
+ snd_model.num_confidence_levels =
+ snd_model32.num_confidence_levels;
+ snd_model.detection_mode = snd_model32.detection_mode;
+
+ cmd = SNDRV_LSM_REG_SND_MODEL_V2;
+ err = msm_cpe_lsm_ioctl_shared(substream, cmd, &snd_model);
+ if (err)
+ dev_err(rtd->dev,
+ "%s: %s failed, error = %d\n",
+ __func__,
+ "SNDRV_LSM_REG_SND_MODEL_V2_32",
+ err);
+ }
+ break;
+ case SNDRV_LSM_EVENT_STATUS: {
+ struct snd_lsm_event_status *event_status = NULL;
+ struct snd_lsm_event_status u_event_status32;
+ struct snd_lsm_event_status *udata_32 = NULL;
+ int u_pld_size = 0;
+
+ dev_dbg(rtd->dev,
+ "%s: ioctl %s\n", __func__,
+ "SNDRV_LSM_EVENT_STATUS32");
+
+ if (copy_from_user(&u_event_status32, (void *)arg,
+ sizeof(struct snd_lsm_event_status))) {
+ dev_err(rtd->dev,
+ "%s: event status copy from user failed, size %zd\n",
+ __func__,
+ sizeof(struct snd_lsm_event_status));
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (u_event_status32.payload_size >
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+ dev_err(rtd->dev,
+ "%s: payload_size %d is invalid, max allowed = %d\n",
+ __func__, u_event_status32.payload_size,
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+ err = -EINVAL;
+ goto done;
+ }
+
+ u_pld_size = sizeof(struct snd_lsm_event_status) +
+ u_event_status32.payload_size;
+ event_status = kzalloc(u_pld_size, GFP_KERNEL);
+ if (!event_status) {
+ dev_err(rtd->dev,
+ "%s: No memory for event status\n",
+ __func__);
+ err = -ENOMEM;
+ goto done;
+ } else {
+ event_status->payload_size =
+ u_event_status32.payload_size;
+ err = msm_cpe_lsm_ioctl_shared(substream,
+ cmd, event_status);
+ if (err)
+ dev_err(rtd->dev,
+ "%s: %s failed, error = %d\n",
+ __func__,
+ "SNDRV_LSM_EVENT_STATUS32",
+ err);
+ }
+
+ if (!err) {
+ udata_32 = kzalloc(u_pld_size, GFP_KERNEL);
+ if (!udata_32) {
+ dev_err(rtd->dev,
+ "%s: nomem for udata\n",
+ __func__);
+ err = -EFAULT;
+ } else {
+ udata_32->status = event_status->status;
+ udata_32->payload_size =
+ event_status->payload_size;
+ memcpy(udata_32->payload,
+ event_status->payload,
+ u_pld_size);
+ }
+ }
+
+ if (!err && copy_to_user(arg, udata_32,
+ u_pld_size)) {
+ dev_err(rtd->dev,
+ "%s: copy to user failed\n",
+ __func__);
+ kfree(event_status);
+ kfree(udata_32);
+ err = -EFAULT;
+ goto done;
+ }
+
+ msm_cpe_lsm_lab_start(substream, event_status->status);
+ msm_cpe_process_event_status_done(lsm_d);
+ kfree(event_status);
+ kfree(udata_32);
+ }
+ break;
+ case SNDRV_LSM_EVENT_STATUS_V3: {
+ struct snd_lsm_event_status_v3 *event_status = NULL;
+ struct snd_lsm_event_status_v3 u_event_status32;
+ struct snd_lsm_event_status_v3 *udata_32 = NULL;
+ int u_pld_size = 0;
+
+ dev_dbg(rtd->dev,
+ "%s: ioctl %s\n", __func__,
+ "SNDRV_LSM_EVENT_STATUS_V3_32");
+
+ if (copy_from_user(&u_event_status32, (void *)arg,
+ sizeof(struct snd_lsm_event_status_v3))) {
+ dev_err(rtd->dev,
+ "%s: event status copy from user failed, size %zd\n",
+ __func__,
+ sizeof(struct snd_lsm_event_status_v3));
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (u_event_status32.payload_size >
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+ dev_err(rtd->dev,
+ "%s: payload_size %d is invalid, max allowed = %d\n",
+ __func__, u_event_status32.payload_size,
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+ err = -EINVAL;
+ goto done;
+ }
+
+ u_pld_size = sizeof(struct snd_lsm_event_status_v3) +
+ u_event_status32.payload_size;
+ event_status = kzalloc(u_pld_size, GFP_KERNEL);
+ if (!event_status) {
+ dev_err(rtd->dev,
+ "%s: No memory for event status\n",
+ __func__);
+ err = -ENOMEM;
+ goto done;
+ } else {
+ event_status->payload_size =
+ u_event_status32.payload_size;
+ err = msm_cpe_lsm_ioctl_shared(substream,
+ cmd, event_status);
+ if (err) {
+ dev_err(rtd->dev,
+ "%s: %s failed, error = %d\n",
+ __func__,
+ "SNDRV_LSM_EVENT_STATUS_V3_32",
+ err);
+ kfree(event_status);
+ goto done;
+ }
+ }
+
+ if (!err) {
+ udata_32 = kzalloc(u_pld_size, GFP_KERNEL);
+ if (!udata_32) {
+ dev_err(rtd->dev,
+ "%s: nomem for udata\n",
+ __func__);
+ err = -EFAULT;
+ } else {
+ udata_32->timestamp_lsw =
+ event_status->timestamp_lsw;
+ udata_32->timestamp_msw =
+ event_status->timestamp_msw;
+ udata_32->status = event_status->status;
+ udata_32->payload_size =
+ event_status->payload_size;
+ memcpy(udata_32->payload,
+ event_status->payload,
+ event_status->payload_size);
+ }
+ }
+
+ if (!err && copy_to_user(arg, udata_32,
+ u_pld_size)) {
+ dev_err(rtd->dev,
+ "%s: copy to user failed\n",
+ __func__);
+ kfree(event_status);
+ kfree(udata_32);
+ err = -EFAULT;
+ goto done;
+ }
+
+ msm_cpe_lsm_lab_start(substream, event_status->status);
+ msm_cpe_process_event_status_done(lsm_d);
+ kfree(event_status);
+ kfree(udata_32);
+ }
+ break;
+ case SNDRV_LSM_SET_PARAMS32: {
+ struct snd_lsm_detection_params_32 det_params32;
+ struct snd_lsm_detection_params det_params;
+
+ if (session->is_topology_used) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if using topology\n",
+ __func__, "SNDRV_LSM_SET_PARAMS32");
+
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&det_params32, arg,
+ sizeof(det_params32))) {
+ err = -EFAULT;
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size = %zd\n",
+ __func__, "SNDRV_LSM_SET_PARAMS_32",
+ sizeof(det_params32));
+ } else {
+ det_params.conf_level =
+ compat_ptr(det_params32.conf_level);
+ det_params.detect_mode =
+ det_params32.detect_mode;
+ det_params.num_confidence_levels =
+ det_params32.num_confidence_levels;
+ det_params.detect_failure =
+ det_params32.detect_failure;
+ cmd = SNDRV_LSM_SET_PARAMS;
+ err = msm_cpe_lsm_ioctl_shared(substream, cmd,
+ &det_params);
+ if (err)
+ dev_err(rtd->dev,
+ "%s: ioctl %s failed\n", __func__,
+ "SNDRV_LSM_SET_PARAMS");
+ }
+
+ break;
+ }
+
+ case SNDRV_LSM_SET_MODULE_PARAMS_32: {
+ struct snd_lsm_module_params_32 p_data_32;
+ struct snd_lsm_module_params p_data;
+ u8 *params, *params32;
+ size_t p_size;
+ struct lsm_params_info_32 *p_info_32;
+ struct lsm_params_info *p_info;
+ int i;
+
+ if (!session->is_topology_used) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if not using topology\n",
+ __func__, "SET_MODULE_PARAMS_32");
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&p_data_32, arg,
+ sizeof(p_data_32))) {
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size = %zd\n",
+ __func__, "SET_MODULE_PARAMS_32",
+ sizeof(p_data_32));
+ err = -EFAULT;
+ goto done;
+ }
+
+ p_data.params = compat_ptr(p_data_32.params);
+ p_data.num_params = p_data_32.num_params;
+ p_data.data_size = p_data_32.data_size;
+
+ if (p_data.num_params > LSM_PARAMS_MAX) {
+ dev_err(rtd->dev,
+ "%s: %s: Invalid num_params %d\n",
+ __func__, "SET_MODULE_PARAMS_32",
+ p_data.num_params);
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (p_data.data_size !=
+ (p_data.num_params * sizeof(struct lsm_params_info_32))) {
+ dev_err(rtd->dev,
+ "%s: %s: Invalid size %d\n",
+ __func__, "SET_MODULE_PARAMS_32",
+ p_data.data_size);
+ err = -EINVAL;
+ goto done;
+ }
+
+ p_size = sizeof(struct lsm_params_info_32) *
+ p_data.num_params;
+
+ params32 = kzalloc(p_size, GFP_KERNEL);
+ if (!params32) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ p_size = sizeof(struct lsm_params_info) * p_data.num_params;
+ params = kzalloc(p_size, GFP_KERNEL);
+ if (!params) {
+ kfree(params32);
+ err = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(params32, p_data.params,
+ p_data.data_size)) {
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size = %d\n",
+ __func__, "params32", p_data.data_size);
+ kfree(params32);
+ kfree(params);
+ err = -EFAULT;
+ goto done;
+ }
+
+ p_info_32 = (struct lsm_params_info_32 *) params32;
+ p_info = (struct lsm_params_info *) params;
+ for (i = 0; i < p_data.num_params; i++) {
+ p_info->module_id = p_info_32->module_id;
+ p_info->param_id = p_info_32->param_id;
+ p_info->param_size = p_info_32->param_size;
+ p_info->param_data = compat_ptr(p_info_32->param_data);
+ p_info->param_type = p_info_32->param_type;
+
+ p_info_32++;
+ p_info++;
+ }
+
+ err = msm_cpe_lsm_process_params(substream,
+ &p_data, params);
+ if (err)
+ dev_err(rtd->dev,
+ "%s: Failed to process params, err = %d\n",
+ __func__, err);
+ kfree(params);
+ kfree(params32);
+ break;
+ }
+ case SNDRV_LSM_REG_SND_MODEL_V2:
+ case SNDRV_LSM_SET_PARAMS:
+ case SNDRV_LSM_SET_MODULE_PARAMS:
+ /*
+ * In ideal cases, the compat_ioctl should never be called
+ * with the above unlocked ioctl commands. Print error
+ * and return error if it does.
+ */
+ dev_err(rtd->dev,
+ "%s: Invalid cmd for compat_ioctl\n",
+ __func__);
+ err = -EINVAL;
+ break;
+ default:
+ err = msm_cpe_lsm_ioctl_shared(substream, cmd, arg);
+ break;
+ }
+done:
+ MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock,
+ "lsm_api_lock");
+ return err;
+}
+
+#else
+#define msm_cpe_lsm_ioctl_compat NULL
+#endif
+
+/*
+ * msm_cpe_lsm_prepare: prepare call from ASoC core for this platform
+ * @substream: ASoC substream for which the operation is invoked
+ *
+ * start the AFE port on CPE associated for this listen session
+ */
+static int msm_cpe_lsm_prepare(struct snd_pcm_substream *substream)
+{
+ int rc = 0;
+ struct cpe_priv *cpe = cpe_get_private_data(substream);
+ struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct wcd_cpe_afe_ops *afe_ops;
+ struct wcd_cpe_afe_port_cfg *afe_cfg;
+ struct cpe_lsm_session *lsm_session;
+ struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_hw_params lsm_param;
+ struct wcd_cpe_lsm_ops *lsm_ops;
+
+ if (!cpe || !cpe->core_handle) {
+ dev_err(rtd->dev,
+ "%s: Invalid private data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!lsm_d || !lsm_d->lsm_session) {
+ dev_err(rtd->dev,
+ "%s: Invalid session data\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->status->state == SNDRV_PCM_STATE_PREPARED) {
+ pr_err("%s: XRUN ignore for now\n", __func__);
+ return 0;
+ }
+
+ lsm_session = lsm_d->lsm_session;
+ lab_d->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+
+ dev_dbg(rtd->dev,
+ "%s: pcm_size 0x%x", __func__, lab_d->pcm_size);
+
+ if (lsm_d->cpe_prepared) {
+ dev_dbg(rtd->dev, "%s: CPE is alredy prepared\n",
+ __func__);
+ return 0;
+ }
+
+ lsm_ops = &cpe->lsm_ops;
+ afe_ops = &cpe->afe_ops;
+ afe_cfg = &(lsm_d->lsm_session->afe_port_cfg);
+
+ switch (cpe->input_port_id) {
+ case AFE_PORT_ID_3:
+ afe_cfg->port_id = AFE_PORT_ID_3;
+ afe_cfg->bit_width = 16;
+ afe_cfg->num_channels = 1;
+ afe_cfg->sample_rate = SAMPLE_RATE_48KHZ;
+ rc = afe_ops->afe_port_cmd_cfg(cpe->core_handle, afe_cfg);
+ break;
+ case AFE_PORT_ID_1:
+ default:
+ afe_cfg->port_id = AFE_PORT_ID_1;
+ afe_cfg->bit_width = 16;
+ afe_cfg->num_channels = 1;
+ afe_cfg->sample_rate = SAMPLE_RATE_16KHZ;
+ rc = afe_ops->afe_set_params(cpe->core_handle,
+ afe_cfg, cpe->afe_mad_ctl);
+ break;
+ }
+
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: cpe afe params failed for port = %d, err = %d\n",
+ __func__, afe_cfg->port_id, rc);
+ return rc;
+ }
+ lsm_param.sample_rate = afe_cfg->sample_rate;
+ lsm_param.num_chs = afe_cfg->num_channels;
+ lsm_param.bit_width = afe_cfg->bit_width;
+ rc = lsm_ops->lsm_set_media_fmt_params(cpe->core_handle, lsm_session,
+ &lsm_param);
+ if (rc)
+ dev_dbg(rtd->dev,
+ "%s: failed to set lsm media fmt params, err = %d\n",
+ __func__, rc);
+
+ /* Send connect to port (input) */
+ rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session,
+ &cpe->input_port_id);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: Failed to set connect input port, err=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ if (cpe->input_port_id != 3) {
+ rc = lsm_ops->lsm_get_afe_out_port_id(cpe->core_handle,
+ lsm_session);
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: failed to get port id, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ /* Send connect to port (output) */
+ rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session,
+ &lsm_session->afe_out_port_id);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: Failed to set connect output port, err=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+ rc = msm_cpe_afe_port_cntl(substream,
+ cpe->core_handle,
+ afe_ops, afe_cfg,
+ AFE_CMD_PORT_START);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: cpe_afe_port start failed, err = %d\n",
+ __func__, rc);
+ else
+ lsm_d->cpe_prepared = true;
+
+ return rc;
+}
+
+/*
+ * msm_cpe_lsm_trigger: trigger call from ASoC core for this platform
+ * @substream: ASoC substream for which the operation is invoked
+ * @cmd: the trigger command from framework
+ *
+ * suspend/resume the AFE port on CPE associated with listen session
+ */
+static int msm_cpe_lsm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct cpe_priv *cpe = cpe_get_private_data(substream);
+ struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+ struct wcd_cpe_afe_ops *afe_ops;
+ struct wcd_cpe_afe_port_cfg *afe_cfg;
+ int afe_cmd = AFE_CMD_INVALID;
+ int rc = 0;
+
+ if (!cpe || !cpe->core_handle) {
+ dev_err(rtd->dev,
+ "%s: Invalid private data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!lsm_d || !lsm_d->lsm_session) {
+ dev_err(rtd->dev,
+ "%s: Invalid session data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ afe_ops = &cpe->afe_ops;
+ afe_cfg = &(lsm_d->lsm_session->afe_port_cfg);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ afe_cmd = AFE_CMD_PORT_SUSPEND;
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ afe_cmd = AFE_CMD_PORT_RESUME;
+ break;
+
+ default:
+ afe_cmd = AFE_CMD_INVALID;
+ dev_dbg(rtd->dev,
+ "%s: unhandled trigger cmd %d\n",
+ __func__, cmd);
+ break;
+ }
+
+ if (afe_cmd != AFE_CMD_INVALID)
+ rc = msm_cpe_afe_port_cntl(substream,
+ cpe->core_handle,
+ afe_ops, afe_cfg,
+ afe_cmd);
+
+ return rc;
+}
+
+static int msm_cpe_lsm_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+ struct cpe_priv *cpe = cpe_get_private_data(substream);
+ struct cpe_lsm_session *session = NULL;
+ struct cpe_hw_params *hw_params = NULL;
+
+ if (!cpe || !cpe->core_handle) {
+ dev_err(rtd->dev,
+ "%s: Invalid %s\n",
+ __func__,
+ (!cpe) ? "cpe" : "core");
+ return -EINVAL;
+ }
+
+ if (!lsm_d || !lsm_d->lsm_session) {
+ dev_err(rtd->dev,
+ "%s: Invalid %s\n",
+ __func__,
+ (!lsm_d) ? "priv_data" : "session");
+ return -EINVAL;
+ }
+
+ session = lsm_d->lsm_session;
+ hw_params = &lsm_d->hw_params;
+ hw_params->buf_sz = (params_buffer_bytes(params)
+ / params_periods(params));
+ hw_params->period_count = params_periods(params);
+ hw_params->channels = params_channels(params);
+ hw_params->sample_rate = params_rate(params);
+
+ if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
+ hw_params->sample_size = 16;
+ else if (params_format(params) ==
+ SNDRV_PCM_FORMAT_S24_LE)
+ hw_params->sample_size = 24;
+ else if (params_format(params) ==
+ SNDRV_PCM_FORMAT_S32_LE)
+ hw_params->sample_size = 32;
+ else {
+ dev_err(rtd->dev,
+ "%s: Invalid Format 0x%x\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ dev_dbg(rtd->dev,
+ "%s: Format %d buffer size(bytes) %d period count %d\n"
+ " Channel %d period in bytes 0x%x Period Size 0x%x rate = %d\n",
+ __func__, params_format(params), params_buffer_bytes(params),
+ params_periods(params), params_channels(params),
+ params_period_bytes(params), params_period_size(params),
+ params_rate(params));
+
+ return 0;
+}
+
+static snd_pcm_uframes_t msm_cpe_lsm_pointer(
+ struct snd_pcm_substream *substream)
+{
+
+ struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct cpe_lsm_session *session;
+ struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+
+ session = lsm_d->lsm_session;
+ if (lab_d->dma_write >= lab_d->pcm_size)
+ lab_d->dma_write = 0;
+ dev_dbg(rtd->dev,
+ "%s:pcm_dma_pos = %d\n",
+ __func__, lab_d->dma_write);
+
+ return bytes_to_frames(runtime, (lab_d->dma_write));
+}
+
+static int msm_cpe_lsm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct cpe_lsm_session *session;
+ struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+ char *pcm_buf;
+ int fbytes = 0;
+ int rc = 0;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->status->state == SNDRV_PCM_STATE_PREPARED) {
+ pr_err("%s: XRUN ignore for now\n", __func__);
+ return 0;
+ }
+ session = lsm_d->lsm_session;
+
+ /* Check if buffer reading is already in error state */
+ if (lab_d->thread_status == MSM_LSM_LAB_THREAD_ERROR) {
+ dev_err(rtd->dev,
+ "%s: Bufferring is in error state\n",
+ __func__);
+ /*
+ * Advance the period so there is no wait in case
+ * read is invoked even after error is propogated
+ */
+ atomic_inc(&lab_d->in_count);
+ lab_d->dma_write += snd_pcm_lib_period_bytes(substream);
+ snd_pcm_period_elapsed(substream);
+ return -ENETRESET;
+ } else if (lab_d->thread_status == MSM_LSM_LAB_THREAD_STOP) {
+ dev_err(rtd->dev,
+ "%s: Buferring is in stopped\n",
+ __func__);
+ return -EIO;
+ }
+
+ rc = wait_event_timeout(lab_d->period_wait,
+ (atomic_read(&lab_d->in_count) ||
+ atomic_read(&lab_d->abort_read)),
+ (2 * HZ));
+ if (atomic_read(&lab_d->abort_read)) {
+ pr_debug("%s: LSM LAB Abort read\n", __func__);
+ return -EIO;
+ }
+ if (lab_d->thread_status != MSM_LSM_LAB_THREAD_RUNNING) {
+ pr_err("%s: Lab stopped\n", __func__);
+ return -EIO;
+ }
+ if (!rc) {
+ pr_err("%s:LAB err wait_event_timeout\n", __func__);
+ rc = -EAGAIN;
+ goto fail;
+ }
+ if (lab_d->buf_idx >= (lsm_d->hw_params.period_count))
+ lab_d->buf_idx = 0;
+ pcm_buf = (lab_d->pcm_buf[lab_d->buf_idx].mem);
+ pr_debug("%s: Buf IDX = 0x%x pcm_buf %pK\n",
+ __func__, lab_d->buf_idx, pcm_buf);
+ if (pcm_buf) {
+ if (copy_to_user(buf, pcm_buf, fbytes)) {
+ pr_err("Failed to copy buf to user\n");
+ rc = -EFAULT;
+ goto fail;
+ }
+ }
+ lab_d->buf_idx++;
+ atomic_dec(&lab_d->in_count);
+ return 0;
+fail:
+ return rc;
+}
+
+/*
+ * msm_asoc_cpe_lsm_probe: ASoC framework for lsm platform driver
+ * @platform: platform registered with ASoC core
+ *
+ * Allocate the private data for this platform and obtain the ops for
+ * lsm and afe modules from underlying driver. Also find the codec
+ * for this platform as specified by machine driver for ASoC framework.
+ */
+static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform)
+{
+ struct snd_soc_card *card;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_codec *codec;
+ struct cpe_priv *cpe_priv;
+ const struct snd_kcontrol_new *kcontrol;
+ bool found_runtime = false;
+ const char *cpe_dev_id = "qcom,msm-cpe-lsm-id";
+ u32 port_id = 0;
+ int ret = 0;
+ int i;
+
+ if (!platform || !platform->component.card) {
+ pr_err("%s: Invalid platform or card\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ card = platform->component.card;
+
+ /* Match platform to codec */
+ for (i = 0; i < card->num_links; i++) {
+ rtd = &card->rtd[i];
+ if (!rtd->platform)
+ continue;
+ if (!strcmp(rtd->platform->component.name,
+ platform->component.name)) {
+ found_runtime = true;
+ break;
+ }
+ }
+
+ if (!found_runtime) {
+ dev_err(platform->dev,
+ "%s: Failed to find runtime for platform\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(platform->dev->of_node, cpe_dev_id,
+ &port_id);
+ if (ret) {
+ dev_dbg(platform->dev,
+ "%s: missing 0x%x in dt node\n", __func__, port_id);
+ port_id = 1;
+ }
+
+ codec = rtd->codec;
+
+ cpe_priv = kzalloc(sizeof(struct cpe_priv),
+ GFP_KERNEL);
+ if (!cpe_priv) {
+ dev_err(platform->dev,
+ "%s: no memory for priv data, size = %zd\n",
+ __func__, sizeof(struct cpe_priv));
+ return -ENOMEM;
+ }
+
+ cpe_priv->codec = codec;
+ cpe_priv->input_port_id = port_id;
+ wcd_cpe_get_lsm_ops(&cpe_priv->lsm_ops);
+ wcd_cpe_get_afe_ops(&cpe_priv->afe_ops);
+
+ snd_soc_platform_set_drvdata(platform, cpe_priv);
+ kcontrol = &msm_cpe_kcontrols[0];
+ snd_ctl_add(card->snd_card, snd_ctl_new1(kcontrol, cpe_priv));
+ return 0;
+}
+
+static struct snd_pcm_ops msm_cpe_lsm_ops = {
+ .open = msm_cpe_lsm_open,
+ .close = msm_cpe_lsm_close,
+ .ioctl = msm_cpe_lsm_ioctl,
+ .prepare = msm_cpe_lsm_prepare,
+ .trigger = msm_cpe_lsm_trigger,
+ .pointer = msm_cpe_lsm_pointer,
+ .copy = msm_cpe_lsm_copy,
+ .hw_params = msm_cpe_lsm_hwparams,
+ .compat_ioctl = msm_cpe_lsm_ioctl_compat,
+};
+
+static struct snd_soc_platform_driver msm_soc_cpe_platform = {
+ .ops = &msm_cpe_lsm_ops,
+ .probe = msm_asoc_cpe_lsm_probe,
+};
+
+/*
+ * msm_cpe_lsm_probe: platform driver probe
+ * @pdev: platform device
+ *
+ * Register the ASoC platform driver with ASoC core
+ */
+static int msm_cpe_lsm_probe(struct platform_device *pdev)
+{
+
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_cpe_platform);
+}
+
+/*
+ * msm_cpe_lsm_remove: platform driver remove
+ * @pdev: platform device
+ *
+ * Deregister the ASoC platform driver
+ */
+static int msm_cpe_lsm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_cpe_lsm_dt_match[] = {
+ {.compatible = "qcom,msm-cpe-lsm" },
+ { }
+};
+
+static struct platform_driver msm_cpe_lsm_driver = {
+ .driver = {
+ .name = "msm-cpe-lsm",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(msm_cpe_lsm_dt_match),
+ },
+ .probe = msm_cpe_lsm_probe,
+ .remove = msm_cpe_lsm_remove,
+};
+module_platform_driver(msm_cpe_lsm_driver);
+
+MODULE_DESCRIPTION("CPE LSM platform driver");
+MODULE_DEVICE_TABLE(of, msm_cpe_lsm_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
new file mode 100644
index 000000000000..a3ffda22b456
--- /dev/null
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -0,0 +1,3024 @@
+/* Copyright (c) 2012-2017, 2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+static struct snd_soc_dai_ops msm_fe_dai_ops = {};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+ 88200, 96000, 176400, 192000, 352800, 384000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static int multimedia_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ return 0;
+}
+
+static int fe_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_dapm_route intercon;
+ struct snd_soc_dapm_context *dapm;
+
+ if (!dai || !dai->driver) {
+ pr_err("%s invalid params\n", __func__);
+ return -EINVAL;
+ }
+ dapm = snd_soc_component_get_dapm(dai->component);
+ memset(&intercon, 0 , sizeof(intercon));
+ if (dai->driver->playback.stream_name &&
+ dai->driver->playback.aif_name) {
+ dev_dbg(dai->dev, "%s add route for widget %s",
+ __func__, dai->driver->playback.stream_name);
+ intercon.source = dai->driver->playback.stream_name;
+ intercon.sink = dai->driver->playback.aif_name;
+ dev_dbg(dai->dev, "%s src %s sink %s\n",
+ __func__, intercon.source, intercon.sink);
+ snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ snd_soc_dapm_ignore_suspend(dapm, intercon.source);
+ }
+ if (dai->driver->capture.stream_name &&
+ dai->driver->capture.aif_name) {
+ dev_dbg(dai->dev, "%s add route for widget %s",
+ __func__, dai->driver->capture.stream_name);
+ intercon.sink = dai->driver->capture.stream_name;
+ intercon.source = dai->driver->capture.aif_name;
+ dev_dbg(dai->dev, "%s src %s sink %s\n",
+ __func__, intercon.source, intercon.sink);
+ snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ snd_soc_dapm_ignore_suspend(dapm, intercon.sink);
+ }
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_fe_Multimedia_dai_ops = {
+ .startup = multimedia_startup,
+};
+
+static const struct snd_soc_component_driver msm_fe_dai_component = {
+ .name = "msm-dai-fe",
+};
+
+static struct snd_soc_dai_driver msm_fe_dais[] = {
+ {
+ .playback = {
+ .stream_name = "MultiMedia1 Playback",
+ .aif_name = "MM_DL1",
+ .rates = (SNDRV_PCM_RATE_8000_384000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia1 Capture",
+ .aif_name = "MM_UL1",
+ .rates = (SNDRV_PCM_RATE_8000_384000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia1",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia2 Playback",
+ .aif_name = "MM_DL2",
+ .rates = (SNDRV_PCM_RATE_8000_384000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia2 Capture",
+ .aif_name = "MM_UL2",
+ .rates = (SNDRV_PCM_RATE_8000_384000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia2",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "CS-VOICE Playback",
+ .aif_name = "CS-VOICE_DL1",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "CS-VOICE Capture",
+ .aif_name = "CS-VOICE_UL1",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "CS-VOICE",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "VoIP Playback",
+ .aif_name = "VOIP_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_SPECIAL,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "VoIP Capture",
+ .aif_name = "VOIP_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_SPECIAL,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "VoIP",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia3 Playback",
+ .aif_name = "MM_DL3",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia3 Capture",
+ .aif_name = "MM_UL3",
+ .rates = (SNDRV_PCM_RATE_8000_384000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia3",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia4 Playback",
+ .aif_name = "MM_DL4",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia4",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia5 Playback",
+ .aif_name = "MM_DL5",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia5 Capture",
+ .aif_name = "MM_UL5",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia5",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia6 Playback",
+ .aif_name = "MM_DL6",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia6 Capture",
+ .aif_name = "MM_UL6",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia6",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia7 Playback",
+ .aif_name = "MM_DL7",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia7",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia8 Playback",
+ .aif_name = "MM_DL8",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia8 Capture",
+ .aif_name = "MM_UL8",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia8",
+ .probe = fe_dai_probe,
+ },
+ /* FE DAIs created for hostless operation purpose */
+ {
+ .playback = {
+ .stream_name = "SLIMBUS0_HOSTLESS Playback",
+ .aif_name = "SLIM0_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "SLIMBUS0_HOSTLESS Capture",
+ .aif_name = "SLIM0_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SLIMBUS0_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "SLIMBUS1_HOSTLESS Playback",
+ .aif_name = "SLIM1_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "SLIMBUS1_HOSTLESS Capture",
+ .aif_name = "SLIM1_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SLIMBUS1_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "SLIMBUS3_HOSTLESS Playback",
+ .aif_name = "SLIM3_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "SLIMBUS3_HOSTLESS Capture",
+ .aif_name = "SLIM3_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SLIMBUS3_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "SLIMBUS4_HOSTLESS Playback",
+ .aif_name = "SLIM4_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "SLIMBUS4_HOSTLESS Capture",
+ .aif_name = "SLIM4_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SLIMBUS4_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "SLIMBUS6_HOSTLESS Playback",
+ .aif_name = "SLIM6_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SLIMBUS6_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "SLIMBUS7_HOSTLESS Playback",
+ .aif_name = "SLIM7_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "SLIMBUS7_HOSTLESS Capture",
+ .aif_name = "SLIM7_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SLIMBUS7_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "SLIMBUS8_HOSTLESS Playback",
+ .aif_name = "SLIM8_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "SLIMBUS8_HOSTLESS Capture",
+ .aif_name = "SLIM8_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SLIMBUS8_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "INT_FM_HOSTLESS Playback",
+ .aif_name = "INTFM_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "INT_FM_HOSTLESS Capture",
+ .aif_name = "INTFM_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "INT_FM_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "INT_HFP_BT Hostless Playback",
+ .aif_name = "INTHFP_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ },
+ .capture = {
+ .stream_name = "INT_HFP_BT Hostless Capture",
+ .aif_name = "INTHFP_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "INT_HFP_BT_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "USBAUDIO_HOSTLESS Playback",
+ .aif_name = "USBAUDIO_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 |
+ SNDRV_PCM_RATE_384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "USBAUDIO_HOSTLESS Capture",
+ .aif_name = "USBAUDIO_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 |
+ SNDRV_PCM_RATE_384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "USBAUDIO_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "AFE Playback",
+ .aif_name = "PCM_RX",
+ .rates = (SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "AFE Capture",
+ .aif_name = "PCM_TX",
+ .rates = (SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "AFE-PROXY",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "HDMI_HOSTLESS Playback",
+ .aif_name = "HDMI_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "HDMI_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "AUXPCM_HOSTLESS Playback",
+ .aif_name = "AUXPCM_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ },
+ .capture = {
+ .stream_name = "AUXPCM_HOSTLESS Capture",
+ .aif_name = "AUXPCM_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "AUXPCM_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "VOICE_STUB Playback",
+ .aif_name = "VOICE_STUB_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "VOICE_STUB Capture",
+ .aif_name = "VOICE_STUB_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "VOICE_STUB",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "VoLTE Playback",
+ .aif_name = "VoLTE_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "VoLTE Capture",
+ .aif_name = "VoLTE_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "VoLTE",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MI2S_RX_HOSTLESS Playback",
+ .aif_name = "MI2S_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "MI2S_TX_HOSTLESS Capture",
+ .aif_name = "MI2S_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "MI2S_TX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "SEC_I2S_RX_HOSTLESS Playback",
+ .aif_name = "SEC_I2S_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_I2S_RX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary MI2S_TX Hostless Capture",
+ .aif_name = "PRI_MI2S_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_MI2S_TX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary MI2S_RX Hostless Playback",
+ .aif_name = "PRI_MI2S_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_MI2S_RX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary MI2S_TX Hostless Capture",
+ .aif_name = "SEC_MI2S_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_MI2S_TX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary MI2S_RX Hostless Playback",
+ .aif_name = "SEC_MI2S_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_MI2S_RX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary MI2S_TX Hostless Capture",
+ .aif_name = "TERT_MI2S_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_MI2S_TX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary MI2S_RX Hostless Playback",
+ .aif_name = "TERT_MI2S_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_MI2S_RX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary MI2S_TX Hostless Capture",
+ .aif_name = "QUAT_MI2S_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_MI2S_TX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary MI2S_RX Hostless Playback",
+ .aif_name = "QUAT_MI2S_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_MI2S_RX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "INT0 MI2S_RX Hostless Playback",
+ .aif_name = "INT0_MI2S_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "INT0_MI2S_RX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "INT4 MI2S_RX Hostless Playback",
+ .aif_name = "INT4_MI2S_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "INT4_MI2S_RX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "INT3 MI2S_TX Hostless Capture",
+ .aif_name = "INT3_MI2S_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "INT3_MI2S_TX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ /* TDM Hostless */
+ {
+ .capture = {
+ .stream_name = "Primary TDM0 Hostless Capture",
+ .aif_name = "PRI_TDM_TX_0_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_TX_0_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM0 Hostless Playback",
+ .aif_name = "PRI_TDM_RX_0_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_RX_0_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM1 Hostless Capture",
+ .aif_name = "PRI_TDM_TX_1_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_TX_1_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM1 Hostless Playback",
+ .aif_name = "PRI_TDM_RX_1_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_RX_1_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM2 Hostless Capture",
+ .aif_name = "PRI_TDM_TX_2_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_TX_2_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM2 Hostless Playback",
+ .aif_name = "PRI_TDM_RX_2_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_RX_2_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM3 Hostless Capture",
+ .aif_name = "PRI_TDM_TX_3_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_TX_3_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM3 Hostless Playback",
+ .aif_name = "PRI_TDM_RX_3_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_RX_3_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM4 Hostless Capture",
+ .aif_name = "PRI_TDM_TX_4_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_TX_4_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM4 Hostless Playback",
+ .aif_name = "PRI_TDM_RX_4_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_RX_4_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM5 Hostless Capture",
+ .aif_name = "PRI_TDM_TX_5_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_TX_5_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM5 Hostless Playback",
+ .aif_name = "PRI_TDM_RX_5_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_RX_5_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM6 Hostless Capture",
+ .aif_name = "PRI_TDM_TX_6_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_TX_6_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM6 Hostless Playback",
+ .aif_name = "PRI_TDM_RX_6_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_RX_6_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM7 Hostless Capture",
+ .aif_name = "PRI_TDM_TX_7_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_TX_7_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM7 Hostless Playback",
+ .aif_name = "PRI_TDM_RX_7_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "PRI_TDM_RX_7_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM0 Hostless Capture",
+ .aif_name = "SEC_TDM_TX_0_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_TX_0_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM0 Hostless Playback",
+ .aif_name = "SEC_TDM_RX_0_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_RX_0_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM1 Hostless Capture",
+ .aif_name = "SEC_TDM_TX_1_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_TX_1_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM1 Hostless Playback",
+ .aif_name = "SEC_TDM_RX_1_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_RX_1_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM2 Hostless Capture",
+ .aif_name = "SEC_TDM_TX_2_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_TX_2_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM2 Hostless Playback",
+ .aif_name = "SEC_TDM_RX_2_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_RX_2_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM3 Hostless Capture",
+ .aif_name = "SEC_TDM_TX_3_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_TX_3_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM3 Hostless Playback",
+ .aif_name = "SEC_TDM_RX_3_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_RX_3_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM4 Hostless Capture",
+ .aif_name = "SEC_TDM_TX_4_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_TX_4_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM4 Hostless Playback",
+ .aif_name = "SEC_TDM_RX_4_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_RX_4_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM5 Hostless Capture",
+ .aif_name = "SEC_TDM_TX_5_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_TX_5_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM5 Hostless Playback",
+ .aif_name = "SEC_TDM_RX_5_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_RX_5_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM6 Hostless Capture",
+ .aif_name = "SEC_TDM_TX_6_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_TX_6_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM6 Hostless Playback",
+ .aif_name = "SEC_TDM_RX_6_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_RX_6_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM7 Hostless Capture",
+ .aif_name = "SEC_TDM_TX_7_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_TX_7_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM7 Hostless Playback",
+ .aif_name = "SEC_TDM_RX_7_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SEC_TDM_RX_7_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM0 Hostless Capture",
+ .aif_name = "TERT_TDM_TX_0_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_TX_0_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM0 Hostless Playback",
+ .aif_name = "TERT_TDM_RX_0_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_RX_0_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM1 Hostless Capture",
+ .aif_name = "TERT_TDM_TX_1_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_TX_1_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM1 Hostless Playback",
+ .aif_name = "TERT_TDM_RX_1_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_RX_1_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM2 Hostless Capture",
+ .aif_name = "TERT_TDM_TX_2_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_TX_2_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM2 Hostless Playback",
+ .aif_name = "TERT_TDM_RX_2_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_RX_2_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM3 Hostless Capture",
+ .aif_name = "TERT_TDM_TX_3_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_TX_3_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM3 Hostless Playback",
+ .aif_name = "TERT_TDM_RX_3_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_RX_3_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM4 Hostless Capture",
+ .aif_name = "TERT_TDM_TX_4_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_TX_4_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM4 Hostless Playback",
+ .aif_name = "TERT_TDM_RX_4_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_RX_4_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM5 Hostless Capture",
+ .aif_name = "TERT_TDM_TX_5_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_TX_5_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM5 Hostless Playback",
+ .aif_name = "TERT_TDM_RX_5_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_RX_5_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM6 Hostless Capture",
+ .aif_name = "TERT_TDM_TX_6_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_TX_6_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM6 Hostless Playback",
+ .aif_name = "TERT_TDM_RX_6_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_RX_6_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM7 Hostless Capture",
+ .aif_name = "TERT_TDM_TX_7_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_TX_7_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM7 Hostless Playback",
+ .aif_name = "TERT_TDM_RX_7_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "TERT_TDM_RX_7_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM0 Hostless Capture",
+ .aif_name = "QUAT_TDM_TX_0_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_TX_0_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM0 Hostless Playback",
+ .aif_name = "QUAT_TDM_RX_0_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_RX_0_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM1 Hostless Capture",
+ .aif_name = "QUAT_TDM_TX_1_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_TX_1_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM1 Hostless Playback",
+ .aif_name = "QUAT_TDM_RX_1_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_RX_1_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM2 Hostless Capture",
+ .aif_name = "QUAT_TDM_TX_2_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_TX_2_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM2 Hostless Playback",
+ .aif_name = "QUAT_TDM_RX_2_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_RX_2_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM3 Hostless Capture",
+ .aif_name = "QUAT_TDM_TX_3_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_TX_3_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM3 Hostless Playback",
+ .aif_name = "QUAT_TDM_RX_3_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_RX_3_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM4 Hostless Capture",
+ .aif_name = "QUAT_TDM_TX_4_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_TX_4_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM4 Hostless Playback",
+ .aif_name = "QUAT_TDM_RX_4_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_RX_4_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM5 Hostless Capture",
+ .aif_name = "QUAT_TDM_TX_5_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_TX_5_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM5 Hostless Playback",
+ .aif_name = "QUAT_TDM_RX_5_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_RX_5_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM6 Hostless Capture",
+ .aif_name = "QUAT_TDM_TX_6_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_TX_6_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM6 Hostless Playback",
+ .aif_name = "QUAT_TDM_RX_6_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_RX_6_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM7 Hostless Capture",
+ .aif_name = "QUAT_TDM_TX_7_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_TX_7_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM7 Hostless Playback",
+ .aif_name = "QUAT_TDM_RX_7_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QUAT_TDM_RX_7_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Voice2 Playback",
+ .aif_name = "VOICE2_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "Voice2 Capture",
+ .aif_name = "VOICE2_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "Voice2",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "Pseudo Playback",
+ .aif_name = "MM_DL9",
+ .rates = (SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "Pseudo Capture",
+ .aif_name = "MM_UL9",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "Pseudo",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "DTMF_RX_HOSTLESS Playback",
+ .aif_name = "DTMF_DL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "DTMF_RX_HOSTLESS",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "CPE Listen Audio capture",
+ .aif_name = "CPE_LSM_UL_HL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "CPE_LSM_NOHOST",
+ },
+ {
+ .playback = {
+ .stream_name = "VOLTE_STUB Playback",
+ .aif_name = "VOLTE_STUB_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "VOLTE_STUB Capture",
+ .aif_name = "VOLTE_STUB_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "VOLTE_STUB",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "VOICE2_STUB Playback",
+ .aif_name = "VOICE2_STUB_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "VOICE2_STUB Capture",
+ .aif_name = "VOICE2_STUB_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "VOICE2_STUB",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia9 Playback",
+ .aif_name = "MM_DL9",
+ .rates = (SNDRV_PCM_RATE_8000_384000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia9 Capture",
+ .aif_name = "MM_UL9",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE|
+ SNDRV_PCM_FMTBIT_S24_LE|
+ SNDRV_PCM_FMTBIT_S24_3LE|
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia9",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "QCHAT Playback",
+ .aif_name = "QCHAT_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "QCHAT Capture",
+ .aif_name = "QCHAT_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "QCHAT",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Listen 1 Audio Service Capture",
+ .aif_name = "LSM1_UL_HL",
+ .rates = (SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 16000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "LSM1",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Listen 2 Audio Service Capture",
+ .aif_name = "LSM2_UL_HL",
+ .rates = (SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 16000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "LSM2",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Listen 3 Audio Service Capture",
+ .aif_name = "LSM3_UL_HL",
+ .rates = (SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 16000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "LSM3",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Listen 4 Audio Service Capture",
+ .aif_name = "LSM4_UL_HL",
+ .rates = (SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 16000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "LSM4",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Listen 5 Audio Service Capture",
+ .aif_name = "LSM5_UL_HL",
+ .rates = (SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 16000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "LSM5",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Listen 6 Audio Service Capture",
+ .aif_name = "LSM6_UL_HL",
+ .rates = (SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 16000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "LSM6",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Listen 7 Audio Service Capture",
+ .aif_name = "LSM7_UL_HL",
+ .rates = (SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 16000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "LSM7",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "Listen 8 Audio Service Capture",
+ .aif_name = "LSM8_UL_HL",
+ .rates = (SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 16000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "LSM8",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "VoWLAN Playback",
+ .aif_name = "VoWLAN_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "VoWLAN Capture",
+ .aif_name = "VoWLAN_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "VoWLAN",
+ .probe = fe_dai_probe,
+ },
+ /* FE DAIs created for multiple instances of offload playback */
+ {
+ .playback = {
+ .stream_name = "MultiMedia10 Playback",
+ .aif_name = "MM_DL10",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia10",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia11 Playback",
+ .aif_name = "MM_DL11",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia11",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia12 Playback",
+ .aif_name = "MM_DL12",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia12",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia13 Playback",
+ .aif_name = "MM_DL13",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia13",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia14 Playback",
+ .aif_name = "MM_DL14",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia14",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia15 Playback",
+ .aif_name = "MM_DL15",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia15",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia16 Playback",
+ .aif_name = "MM_DL16",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia16 Capture",
+ .aif_name = "MM_UL16",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia16",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "VoiceMMode1 Playback",
+ .aif_name = "VOICEMMODE1_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "VoiceMMode1 Capture",
+ .aif_name = "VOICEMMODE1_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "VoiceMMode1",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "VoiceMMode2 Playback",
+ .aif_name = "VOICEMMODE2_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "VoiceMMode2 Capture",
+ .aif_name = "VOICEMMODE2_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "VoiceMMode2",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "MultiMedia17 Capture",
+ .aif_name = "MM_UL17",
+ .rates = (SNDRV_PCM_RATE_8000_192000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia17",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "MultiMedia18 Capture",
+ .aif_name = "MM_UL18",
+ .rates = (SNDRV_PCM_RATE_8000_192000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia18",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "MultiMedia19 Capture",
+ .aif_name = "MM_UL19",
+ .rates = (SNDRV_PCM_RATE_8000_192000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia19",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia20 Playback",
+ .aif_name = "MM_DL20",
+ .rates = (SNDRV_PCM_RATE_8000_384000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia20 Capture",
+ .aif_name = "MM_UL20",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia20",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia21 Playback",
+ .aif_name = "MM_DL21",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia21 Capture",
+ .aif_name = "MM_UL21",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia21",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia22 Playback",
+ .aif_name = "MM_DL22",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia22",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia23 Playback",
+ .aif_name = "MM_DL23",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia23",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia24 Playback",
+ .aif_name = "MM_DL24",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia24",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia25 Playback",
+ .aif_name = "MM_DL25",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia25",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia26 Playback",
+ .aif_name = "MM_DL26",
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia26",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "MultiMedia27 Capture",
+ .aif_name = "MM_UL27",
+ .rates = (SNDRV_PCM_RATE_8000_192000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia27",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "MultiMedia28 Capture",
+ .aif_name = "MM_UL28",
+ .rates = (SNDRV_PCM_RATE_8000_192000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia28",
+ .probe = fe_dai_probe,
+ },
+ {
+ .capture = {
+ .stream_name = "MultiMedia29 Capture",
+ .aif_name = "MM_UL29",
+ .rates = (SNDRV_PCM_RATE_8000_192000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .compress_new = snd_soc_new_compress,
+ .name = "MultiMedia29",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia30 Playback",
+ .aif_name = "MM_DL30",
+ .rates = (SNDRV_PCM_RATE_8000_384000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia30 Capture",
+ .aif_name = "MM_UL30",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia30",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia31 Playback",
+ .aif_name = "MM_DL31",
+ .rates = (SNDRV_PCM_RATE_8000_384000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia31 Capture",
+ .aif_name = "MM_UL31",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia31",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia32 Playback",
+ .aif_name = "MM_DL32",
+ .rates = (SNDRV_PCM_RATE_8000_384000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia32 Capture",
+ .aif_name = "MM_UL32",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia32",
+ .probe = fe_dai_probe,
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia33 Playback",
+ .aif_name = "MM_DL33",
+ .rates = (SNDRV_PCM_RATE_8000_384000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .capture = {
+ .stream_name = "MultiMedia33 Capture",
+ .aif_name = "MM_UL33",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ .name = "MultiMedia33",
+ .probe = fe_dai_probe,
+ },
+};
+
+static int msm_fe_dai_dev_probe(struct platform_device *pdev)
+{
+
+ dev_dbg(&pdev->dev, "%s: dev name %s\n", __func__,
+ dev_name(&pdev->dev));
+ return snd_soc_register_component(&pdev->dev, &msm_fe_dai_component,
+ msm_fe_dais, ARRAY_SIZE(msm_fe_dais));
+}
+
+static int msm_fe_dai_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_dai_fe_dt_match[] = {
+ {.compatible = "qcom,msm-dai-fe"},
+ {}
+};
+
+static struct platform_driver msm_fe_dai_driver = {
+ .probe = msm_fe_dai_dev_probe,
+ .remove = msm_fe_dai_dev_remove,
+ .driver = {
+ .name = "msm-dai-fe",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_fe_dt_match,
+ },
+};
+
+static int __init msm_fe_dai_init(void)
+{
+ return platform_driver_register(&msm_fe_dai_driver);
+}
+module_init(msm_fe_dai_init);
+
+static void __exit msm_fe_dai_exit(void)
+{
+ platform_driver_unregister(&msm_fe_dai_driver);
+}
+module_exit(msm_fe_dai_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM Frontend DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-hostless.c b/sound/soc/msm/msm-pcm-hostless.c
new file mode 100644
index 000000000000..51b0a7208462
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-hostless.c
@@ -0,0 +1,84 @@
+/* Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+
+
+static int msm_pcm_hostless_prepare(struct snd_pcm_substream *substream)
+{
+ if (!substream) {
+ pr_err("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
+
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_hostless_ops = {
+ .prepare = msm_pcm_hostless_prepare
+};
+
+static struct snd_soc_platform_driver msm_soc_hostless_platform = {
+ .ops = &msm_pcm_hostless_ops,
+};
+
+static int msm_pcm_hostless_probe(struct platform_device *pdev)
+{
+
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_hostless_platform);
+}
+
+static int msm_pcm_hostless_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_pcm_hostless_dt_match[] = {
+ {.compatible = "qcom,msm-pcm-hostless"},
+ {}
+};
+
+static struct platform_driver msm_pcm_hostless_driver = {
+ .driver = {
+ .name = "msm-pcm-hostless",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_pcm_hostless_dt_match,
+ },
+ .probe = msm_pcm_hostless_probe,
+ .remove = msm_pcm_hostless_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_pcm_hostless_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_hostless_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("Hostless platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm8996.c b/sound/soc/msm/msm8996.c
new file mode 100644
index 000000000000..47cd28af1b84
--- /dev/null
+++ b/sound/soc/msm/msm8996.c
@@ -0,0 +1,8926 @@
+/*
+ * Copyright (c) 2014-2017, 2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6core.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <device_event.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "../codecs/wcd9xxx-common.h"
+#include "../codecs/wcd9330.h"
+#include "../codecs/wcd9335.h"
+#include "../codecs/wsa881x.h"
+
+#define DRV_NAME "msm8996-asoc-snd"
+
+#define SAMPLING_RATE_8KHZ 8000
+#define SAMPLING_RATE_11P025KHZ 11025
+#define SAMPLING_RATE_16KHZ 16000
+#define SAMPLING_RATE_22P05KHZ 22050
+#define SAMPLING_RATE_32KHZ 32000
+#define SAMPLING_RATE_44P1KHZ 44100
+#define SAMPLING_RATE_48KHZ 48000
+#define SAMPLING_RATE_88P2KHZ 88200
+#define SAMPLING_RATE_96KHZ 96000
+#define SAMPLING_RATE_176P4KHZ 176400
+#define SAMPLING_RATE_192KHZ 192000
+
+#define MSM8996_SPK_ON 1
+#define MSM8996_HIFI_ON 1
+
+#define WCD9XXX_MBHC_DEF_BUTTONS 8
+#define WCD9XXX_MBHC_DEF_RLOADS 5
+#define CODEC_EXT_CLK_RATE 9600000
+#define ADSP_STATE_READY_TIMEOUT_MS 3000
+#define DEV_NAME_STR_LEN 32
+
+#define WSA8810_NAME_1 "wsa881x.20170211"
+#define WSA8810_NAME_2 "wsa881x.20170212"
+
+static int slim0_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int slim0_tx_sample_rate = SAMPLING_RATE_48KHZ;
+static int slim1_tx_sample_rate = SAMPLING_RATE_48KHZ;
+static int slim0_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int slim0_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int slim1_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm8996_auxpcm_rate = SAMPLING_RATE_8KHZ;
+static int slim5_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int slim5_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int slim6_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int slim6_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+/* TDM default channels */
+static int msm_pri_tdm_tx_0_ch = 2;
+static int msm_pri_tdm_tx_1_ch = 2;
+static int msm_pri_tdm_tx_2_ch = 2;
+static int msm_pri_tdm_tx_3_ch = 2;
+
+static int msm_pri_tdm_rx_0_ch = 2;
+static int msm_pri_tdm_rx_1_ch = 2;
+static int msm_pri_tdm_rx_2_ch = 2;
+static int msm_pri_tdm_rx_3_ch = 2;
+
+static int msm_sec_tdm_tx_0_ch = 2;
+static int msm_sec_tdm_tx_1_ch = 2;
+static int msm_sec_tdm_tx_2_ch = 2;
+static int msm_sec_tdm_tx_3_ch = 2;
+
+static int msm_sec_tdm_rx_0_ch = 2;
+static int msm_sec_tdm_rx_1_ch = 2;
+static int msm_sec_tdm_rx_2_ch = 2;
+static int msm_sec_tdm_rx_3_ch = 2;
+
+static int msm_tert_tdm_rx_0_ch = 2;
+static int msm_tert_tdm_rx_1_ch = 2;
+static int msm_tert_tdm_rx_2_ch = 2;
+static int msm_tert_tdm_rx_3_ch = 2;
+
+static int msm_tert_tdm_tx_0_ch = 2;
+static int msm_tert_tdm_tx_1_ch = 2;
+static int msm_tert_tdm_tx_2_ch = 2;
+static int msm_tert_tdm_tx_3_ch = 2;
+
+static int msm_quat_tdm_rx_0_ch = 2;
+static int msm_quat_tdm_rx_1_ch = 2;
+static int msm_quat_tdm_rx_2_ch = 2;
+static int msm_quat_tdm_rx_3_ch = 2;
+
+static int msm_quat_tdm_tx_0_ch = 2;
+static int msm_quat_tdm_tx_1_ch = 2;
+static int msm_quat_tdm_tx_2_ch = 2;
+static int msm_quat_tdm_tx_3_ch = 2;
+
+/* TDM default bit format */
+static int msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_pri_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_sec_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_pri_tdm_rate = SAMPLING_RATE_48KHZ;
+static int msm_pri_tdm_slot_width = 32;
+static int msm_pri_tdm_slot_num = 8;
+static int msm_sec_tdm_rate = SAMPLING_RATE_48KHZ;
+static int msm_sec_tdm_slot_width = 32;
+static int msm_sec_tdm_slot_num = 8;
+static int msm_quat_tdm_rate = SAMPLING_RATE_48KHZ;
+static int msm_quat_tdm_slot_width = 32;
+static int msm_quat_tdm_slot_num = 8;
+static int msm_tert_tdm_rate = SAMPLING_RATE_48KHZ;
+static int msm_tert_tdm_slot_width = 32;
+static int msm_tert_tdm_slot_num = 8;
+
+static int msm_tdm_slot_width = 32;
+static int msm_tdm_num_slots = 8;
+
+enum {
+ QUATERNARY_TDM_RX_0,
+ QUATERNARY_TDM_RX_1,
+ QUATERNARY_TDM_RX_2,
+ QUATERNARY_TDM_RX_3,
+ QUATERNARY_TDM_RX_4,
+ QUATERNARY_TDM_RX_5,
+ QUATERNARY_TDM_RX_6,
+ QUATERNARY_TDM_RX_7,
+ QUATERNARY_TDM_TX_0,
+ QUATERNARY_TDM_TX_1,
+ QUATERNARY_TDM_TX_2,
+ QUATERNARY_TDM_TX_3,
+ QUATERNARY_TDM_TX_4,
+ QUATERNARY_TDM_TX_5,
+ QUATERNARY_TDM_TX_6,
+ QUATERNARY_TDM_TX_7,
+ TERTIARY_TDM_RX_0,
+ TERTIARY_TDM_RX_1,
+ TERTIARY_TDM_RX_2,
+ TERTIARY_TDM_RX_3,
+ TERTIARY_TDM_RX_4,
+ TERTIARY_TDM_RX_5,
+ TERTIARY_TDM_RX_6,
+ TERTIARY_TDM_RX_7,
+ TERTIARY_TDM_TX_0,
+ TERTIARY_TDM_TX_1,
+ TERTIARY_TDM_TX_2,
+ TERTIARY_TDM_TX_3,
+ TERTIARY_TDM_TX_4,
+ TERTIARY_TDM_TX_5,
+ TERTIARY_TDM_TX_6,
+ TERTIARY_TDM_TX_7,
+ SECONDARY_TDM_RX_0,
+ SECONDARY_TDM_RX_1,
+ SECONDARY_TDM_RX_2,
+ SECONDARY_TDM_RX_3,
+ SECONDARY_TDM_RX_4,
+ SECONDARY_TDM_RX_5,
+ SECONDARY_TDM_RX_6,
+ SECONDARY_TDM_RX_7,
+ SECONDARY_TDM_TX_0,
+ SECONDARY_TDM_TX_1,
+ SECONDARY_TDM_TX_2,
+ SECONDARY_TDM_TX_3,
+ SECONDARY_TDM_TX_4,
+ SECONDARY_TDM_TX_5,
+ SECONDARY_TDM_TX_6,
+ SECONDARY_TDM_TX_7,
+ PRIMARY_TDM_RX_0,
+ PRIMARY_TDM_RX_1,
+ PRIMARY_TDM_RX_2,
+ PRIMARY_TDM_RX_3,
+ PRIMARY_TDM_RX_4,
+ PRIMARY_TDM_RX_5,
+ PRIMARY_TDM_RX_6,
+ PRIMARY_TDM_RX_7,
+ PRIMARY_TDM_TX_0,
+ PRIMARY_TDM_TX_1,
+ PRIMARY_TDM_TX_2,
+ PRIMARY_TDM_TX_3,
+ PRIMARY_TDM_TX_4,
+ PRIMARY_TDM_TX_5,
+ PRIMARY_TDM_TX_6,
+ PRIMARY_TDM_TX_7,
+ TDM_MAX,
+};
+
+#define TDM_SLOT_OFFSET_MAX 8
+/* TDM default offset */
+static unsigned int tdm_slot_offset[TDM_MAX][TDM_SLOT_OFFSET_MAX] = {
+ /* QUAT_TDM_RX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* QUAT_TDM_TX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_RX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_TX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_RX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_TX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* PRI_TDM_RX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* PRI_TDM_TX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+};
+
+static struct platform_device *spdev;
+static int ext_us_amp_gpio = -1;
+static int msm8996_spk_control = 1;
+static int msm_slim_0_rx_ch = 1;
+static int msm_slim_0_tx_ch = 1;
+static int msm_slim_1_tx_ch = 1;
+static int msm_slim_5_rx_ch = 1;
+static int msm_slim_6_rx_ch = 1;
+static int msm_hifi_control;
+static int msm_vi_feed_tx_ch = 2;
+
+static int msm_hdmi_rx_ch = 2;
+static int msm_proxy_rx_ch = 2;
+static int hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int msm_tert_mi2s_tx_ch = 2;
+
+static bool codec_reg_done;
+
+static const char *const hifi_function[] = {"Off", "On"};
+static const char *const pin_states[] = {"Disable", "active"};
+static const char *const spk_function[] = {"Off", "On"};
+static const char *const slim0_rx_ch_text[] = {"One", "Two"};
+static const char *const slim5_rx_ch_text[] = {"One", "Two"};
+static const char *const slim6_rx_ch_text[] = {"One", "Two"};
+static const char *const slim0_tx_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven",
+ "Eight"};
+static const char *const vi_feed_ch_text[] = {"One", "Two"};
+static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five",
+ "Six", "Seven", "Eight"};
+static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"};
+static char const *usb_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"};
+static char const *slim5_rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"};
+static char const *slim6_rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"};
+static char const *slim0_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+ "KHZ_192", "KHZ_44P1", "KHZ_8",
+ "KHZ_16", "KHZ_32"};
+static char const *slim5_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+ "KHZ_192", "KHZ_44P1"};
+static char const *slim6_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+ "KHZ_192", "KHZ_44P1"};
+static const char *const proxy_rx_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight"};
+
+static char const *hdmi_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+ "KHZ_192"};
+static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight"};
+
+static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE"};
+static const char *const tdm_rate_text[] = {"8000", "16000", "48000"};
+
+static const char *const tdm_slot_num_text[] = {"One", "Two", "Four",
+ "Eight", "Sixteen", "Thirtytwo"};
+
+
+static const char *const tdm_slot_width_text[] = {"16", "24", "32"};
+
+static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight"};
+static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025",
+ "KHZ_16", "KHZ_22P05", "KHZ_32",
+ "KHZ_44P1", "KHZ_48", "KHZ_88P2",
+ "KHZ_96", "KHZ_176P4", "KHZ_192"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, usb_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, usb_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text);
+
+static const char *const auxpcm_rate_text[] = {"8000", "16000"};
+static const struct soc_enum msm8996_auxpcm_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text),
+};
+
+struct usb_be_config {
+ u32 sample_rate;
+ u32 bit_format;
+ u32 channels;
+};
+
+static struct usb_be_config usb_rx_cfg = {
+ .sample_rate = SAMPLING_RATE_48KHZ,
+ .bit_format = SNDRV_PCM_FORMAT_S16_LE,
+ .channels = 2,
+};
+
+static struct usb_be_config usb_tx_cfg = {
+ .sample_rate = SAMPLING_RATE_48KHZ,
+ .bit_format = SNDRV_PCM_FORMAT_S16_LE,
+ .channels = 2,
+};
+
+static struct afe_clk_set mi2s_tx_clk = {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+struct msm8996_wsa881x_dev_info {
+ struct device_node *of_node;
+ u32 index;
+};
+
+static struct snd_soc_aux_dev *msm8996_aux_dev;
+static struct snd_soc_codec_conf *msm8996_codec_conf;
+
+struct msm8996_asoc_mach_data {
+ u32 mclk_freq;
+ int us_euro_gpio;
+ int hph_en1_gpio;
+ int hph_en0_gpio;
+ struct snd_info_entry *codec_root;
+};
+
+struct msm8996_asoc_wcd93xx_codec {
+ void* (*get_afe_config_fn)(struct snd_soc_codec *codec,
+ enum afe_config_type config_type);
+ void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec);
+};
+
+static struct msm8996_asoc_wcd93xx_codec msm8996_codec_fn;
+
+struct msm8996_liquid_dock_dev {
+ int dock_plug_gpio;
+ int dock_plug_irq;
+ int dock_plug_det;
+ struct work_struct irq_work;
+ struct switch_dev audio_sdev;
+};
+static struct msm8996_liquid_dock_dev *msm8996_liquid_dock_dev;
+
+static void *adsp_state_notifier;
+static void *def_tasha_mbhc_cal(void);
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
+ int enable, bool dapm);
+static int msm8996_wsa881x_init(struct snd_soc_component *component);
+
+/*
+ * Need to report LINEIN
+ * if R/L channel impedance is larger than 5K ohm
+ */
+static struct wcd_mbhc_config wcd_mbhc_cfg = {
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .detect_extn_cable = true,
+ .mono_stero_detection = false,
+ .swap_gnd_mic = NULL,
+ .hs_ext_micbias = true,
+ .key_code[0] = KEY_MEDIA,
+ .key_code[1] = KEY_VOICECOMMAND,
+ .key_code[2] = KEY_VOLUMEUP,
+ .key_code[3] = KEY_VOLUMEDOWN,
+ .key_code[4] = 0,
+ .key_code[5] = 0,
+ .key_code[6] = 0,
+ .key_code[7] = 0,
+ .linein_th = 5000,
+ .moisture_en = true,
+ .mbhc_micbias = MIC_BIAS_2,
+ .anc_micbias = MIC_BIAS_2,
+ .enable_anc_mic_detect = false,
+};
+
+static inline int param_is_mask(int p)
+{
+ return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p,
+ int n)
+{
+ return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
+{
+ if (bit >= SNDRV_MASK_MAX)
+ return;
+ if (param_is_mask(n)) {
+ struct snd_mask *m = param_to_mask(p, n);
+ m->bits[0] = 0;
+ m->bits[1] = 0;
+ m->bits[bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
+static void msm8996_liquid_docking_irq_work(struct work_struct *work)
+{
+ struct msm8996_liquid_dock_dev *dock_dev =
+ container_of(work, struct msm8996_liquid_dock_dev,
+ irq_work);
+
+ dock_dev->dock_plug_det =
+ gpio_get_value(dock_dev->dock_plug_gpio);
+
+ switch_set_state(&dock_dev->audio_sdev, dock_dev->dock_plug_det);
+ /* notify to audio deamon */
+ sysfs_notify(&dock_dev->audio_sdev.dev->kobj, NULL, "state");
+}
+
+static irqreturn_t msm8996_liquid_docking_irq_handler(int irq, void *dev)
+{
+ struct msm8996_liquid_dock_dev *dock_dev = dev;
+
+ /* switch speakers should not run in interrupt context */
+ schedule_work(&dock_dev->irq_work);
+ return IRQ_HANDLED;
+}
+
+static int msm8996_liquid_init_docking(void)
+{
+ int ret = 0;
+ int dock_plug_gpio = 0;
+
+ /* plug in docking speaker+plug in device OR unplug one of them */
+ u32 dock_plug_irq_flags = IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_SHARED;
+
+ dock_plug_gpio = of_get_named_gpio(spdev->dev.of_node,
+ "qcom,dock-plug-det-irq", 0);
+
+ if (dock_plug_gpio >= 0) {
+ msm8996_liquid_dock_dev =
+ kzalloc(sizeof(*msm8996_liquid_dock_dev), GFP_KERNEL);
+ if (!msm8996_liquid_dock_dev) {
+ pr_err("msm8996_liquid_dock_dev alloc fail.\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ msm8996_liquid_dock_dev->dock_plug_gpio = dock_plug_gpio;
+
+ ret = gpio_request(msm8996_liquid_dock_dev->dock_plug_gpio,
+ "dock-plug-det-irq");
+ if (ret) {
+ pr_err("%s:failed request msm8996_liquid_dock_plug_gpio err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto fail_dock_gpio;
+ }
+
+ msm8996_liquid_dock_dev->dock_plug_det =
+ gpio_get_value(
+ msm8996_liquid_dock_dev->dock_plug_gpio);
+ msm8996_liquid_dock_dev->dock_plug_irq =
+ gpio_to_irq(
+ msm8996_liquid_dock_dev->dock_plug_gpio);
+
+ ret = request_irq(msm8996_liquid_dock_dev->dock_plug_irq,
+ msm8996_liquid_docking_irq_handler,
+ dock_plug_irq_flags,
+ "liquid_dock_plug_irq",
+ msm8996_liquid_dock_dev);
+ if (ret < 0) {
+ pr_err("%s: Request Irq Failed err = %d\n",
+ __func__, ret);
+ goto fail_dock_gpio;
+ }
+
+ msm8996_liquid_dock_dev->audio_sdev.name =
+ QC_AUDIO_EXTERNAL_SPK_1_EVENT;
+
+ if (switch_dev_register(
+ &msm8996_liquid_dock_dev->audio_sdev) < 0) {
+ pr_err("%s: dock device register in switch diretory failed\n",
+ __func__);
+ goto fail_switch_dev;
+ }
+
+ INIT_WORK(
+ &msm8996_liquid_dock_dev->irq_work,
+ msm8996_liquid_docking_irq_work);
+ }
+ return 0;
+
+fail_switch_dev:
+ free_irq(msm8996_liquid_dock_dev->dock_plug_irq,
+ msm8996_liquid_dock_dev);
+fail_dock_gpio:
+ gpio_free(msm8996_liquid_dock_dev->dock_plug_gpio);
+exit:
+ kfree(msm8996_liquid_dock_dev);
+ msm8996_liquid_dock_dev = NULL;
+ return ret;
+}
+
+static void msm8996_ext_control(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ pr_debug("%s: msm8996_spk_control = %d", __func__,
+ msm8996_spk_control);
+ if (msm8996_spk_control == MSM8996_SPK_ON) {
+ snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_disable_pin(dapm, "Lineout_2 amp");
+ }
+ snd_soc_dapm_sync(dapm);
+}
+
+static int msm8996_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm8996_spk_control = %d\n",
+ __func__, msm8996_spk_control);
+ ucontrol->value.integer.value[0] = msm8996_spk_control;
+ return 0;
+}
+
+static int msm8996_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ if (msm8996_spk_control == ucontrol->value.integer.value[0])
+ return 0;
+
+ msm8996_spk_control = ucontrol->value.integer.value[0];
+ msm8996_ext_control(codec);
+ return 1;
+}
+
+static int msm8996_hifi_ctrl(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_card *card = codec->component.card;
+ struct msm8996_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+
+ pr_debug("%s: msm_hifi_control = %d", __func__,
+ msm_hifi_control);
+ if (pdata->hph_en1_gpio < 0) {
+ pr_err("%s: hph_en1_gpio is invalid\n", __func__);
+ return -EINVAL;
+ }
+ if (msm_hifi_control == MSM8996_HIFI_ON) {
+ gpio_direction_output(pdata->hph_en1_gpio, 1);
+ /* 5msec delay needed as per HW requirement */
+ usleep_range(5000, 5010);
+ } else {
+ gpio_direction_output(pdata->hph_en1_gpio, 0);
+ }
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static int msm8996_hifi_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_hifi_control = %d\n",
+ __func__, msm_hifi_control);
+ ucontrol->value.integer.value[0] = msm_hifi_control;
+ return 0;
+}
+
+static int msm8996_hifi_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ msm_hifi_control = ucontrol->value.integer.value[0];
+ msm8996_hifi_ctrl(codec);
+ return 1;
+}
+
+static int msm8996_ext_us_amp_init(void)
+{
+ int ret = 0;
+
+ ext_us_amp_gpio = of_get_named_gpio(spdev->dev.of_node,
+ "qcom,ext-ult-spk-amp-gpio", 0);
+ if (ext_us_amp_gpio >= 0) {
+ ret = gpio_request(ext_us_amp_gpio, "ext_us_amp_gpio");
+ if (ret) {
+ pr_err("%s: ext_us_amp_gpio request failed, ret:%d\n",
+ __func__, ret);
+ return ret;
+ }
+ gpio_direction_output(ext_us_amp_gpio, 0);
+ }
+ return ret;
+}
+
+static void msm8996_ext_us_amp_enable(u32 on)
+{
+ if (on)
+ gpio_direction_output(ext_us_amp_gpio, 1);
+ else
+ gpio_direction_output(ext_us_amp_gpio, 0);
+
+ pr_debug("%s: US Emitter GPIO enable:%s\n", __func__,
+ on ? "Enable" : "Disable");
+}
+
+static int msm_ext_ultrasound_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ pr_debug("%s()\n", __func__);
+ if (strcmp(w->name, "ultrasound amp")) {
+ if (!gpio_is_valid(ext_us_amp_gpio)) {
+ pr_err("%s: ext_us_amp_gpio isn't configured\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ msm8996_ext_us_amp_enable(1);
+ else
+ msm8996_ext_us_amp_enable(0);
+ } else {
+ pr_err("%s() Invalid Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
+ int enable, bool dapm)
+{
+ if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+ return tasha_cdc_mclk_enable(codec, enable, dapm);
+ else {
+ dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+ __func__);
+ return -EINVAL;
+ }
+}
+
+static int msm8996_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return msm_snd_enable_codec_ext_clk(codec, 1, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return msm_snd_enable_codec_ext_clk(codec, 0, true);
+ }
+ return 0;
+}
+
+static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec,
+ int enable, bool dapm)
+{
+ int ret = 0;
+
+ if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+ ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm);
+ else {
+ dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+ __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int msm8996_mclk_tx_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return msm_snd_enable_codec_ext_tx_clk(codec, 1, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return msm_snd_enable_codec_ext_tx_clk(codec, 0, true);
+ }
+ return 0;
+}
+
+static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_card *card = codec->component.card;
+ struct msm8996_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ pr_debug("%s: msm_hifi_control = %d", __func__,
+ msm_hifi_control);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (msm_hifi_control == MSM8996_HIFI_ON) {
+ if (pdata->hph_en0_gpio < 0) {
+ pr_err("%s: hph_en0_gpio is invalid\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ gpio_direction_output(pdata->hph_en0_gpio, 1);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (msm_hifi_control == MSM8996_HIFI_ON) {
+ if (pdata->hph_en0_gpio < 0) {
+ pr_err("%s: hph_en0_gpio is invalid\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ gpio_direction_output(pdata->hph_en0_gpio, 0);
+ }
+ break;
+ }
+err:
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget msm8996_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
+ msm8996_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0,
+ msm8996_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SPK("Lineout_1 amp", NULL),
+ SND_SOC_DAPM_SPK("Lineout_3 amp", NULL),
+ SND_SOC_DAPM_SPK("Lineout_2 amp", NULL),
+ SND_SOC_DAPM_SPK("Lineout_4 amp", NULL),
+ SND_SOC_DAPM_SPK("ultrasound amp", msm_ext_ultrasound_event),
+ SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event),
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic4", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic6", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic7", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic8", NULL),
+
+ SND_SOC_DAPM_MIC("Digital Mic0", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic6", NULL),
+};
+
+static struct snd_soc_dapm_route wcd9335_audio_paths[] = {
+ {"MIC BIAS1", NULL, "MCLK TX"},
+ {"MIC BIAS2", NULL, "MCLK TX"},
+ {"MIC BIAS3", NULL, "MCLK TX"},
+ {"MIC BIAS4", NULL, "MCLK TX"},
+};
+
+static int slim5_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val = 0;
+
+ switch (slim5_rx_sample_rate) {
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 3;
+ break;
+
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 2;
+ break;
+
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 1;
+ break;
+
+ case SAMPLING_RATE_48KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: slim5_rx_sample_rate = %d\n", __func__,
+ slim5_rx_sample_rate);
+
+ return 0;
+}
+
+static int slim5_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: ucontrol value = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 3:
+ slim5_rx_sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 2:
+ slim5_rx_sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 1:
+ slim5_rx_sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 0:
+ default:
+ slim5_rx_sample_rate = SAMPLING_RATE_48KHZ;
+ }
+
+ pr_debug("%s: slim5_rx_sample_rate = %d\n", __func__,
+ slim5_rx_sample_rate);
+
+ return 0;
+}
+
+static int slim6_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val = 0;
+
+ switch (slim6_rx_sample_rate) {
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 3;
+ break;
+
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 2;
+ break;
+
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 1;
+ break;
+
+ case SAMPLING_RATE_48KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: slim6_rx_sample_rate = %d\n", __func__,
+ slim6_rx_sample_rate);
+
+ return 0;
+}
+
+static int slim6_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 3:
+ slim6_rx_sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 2:
+ slim6_rx_sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 1:
+ slim6_rx_sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 0:
+ default:
+ slim6_rx_sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+
+ pr_debug("%s: ucontrol value = %ld, slim6_rx_sample_rate = %d\n",
+ __func__, ucontrol->value.integer.value[0],
+ slim6_rx_sample_rate);
+
+ return 0;
+}
+
+static int slim0_tx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (slim0_tx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: slim0_tx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, slim0_tx_bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int slim0_tx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ slim0_tx_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 1:
+ slim0_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ slim0_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ default:
+ pr_err("%s: invalid value %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ rc = -EINVAL;
+ break;
+ }
+
+ pr_debug("%s: ucontrol value = %ld, slim0_tx_bit_format = %d\n",
+ __func__, ucontrol->value.integer.value[0],
+ slim0_tx_bit_format);
+
+ return rc;
+}
+
+static int slim0_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val = 0;
+
+ switch (slim0_rx_sample_rate) {
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 6;
+ break;
+
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 5;
+ break;
+
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 4;
+ break;
+
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 3;
+ break;
+
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 2;
+ break;
+
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 1;
+ break;
+
+ case SAMPLING_RATE_48KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: slim0_rx_sample_rate = %d\n", __func__,
+ slim0_rx_sample_rate);
+
+ return 0;
+}
+
+static int slim0_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: ucontrol value = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 6:
+ slim0_rx_sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 5:
+ slim0_rx_sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 4:
+ slim0_rx_sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 3:
+ slim0_rx_sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 2:
+ slim0_rx_sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 1:
+ slim0_rx_sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 0:
+ default:
+ slim0_rx_sample_rate = SAMPLING_RATE_48KHZ;
+ }
+
+ pr_debug("%s: slim0_rx_sample_rate = %d\n", __func__,
+ slim0_rx_sample_rate);
+
+ return 0;
+}
+
+static int slim0_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val = 0;
+
+ switch (slim0_tx_sample_rate) {
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: slim0_tx_sample_rate = %d\n", __func__,
+ slim0_tx_sample_rate);
+ return 0;
+}
+
+static int slim0_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+
+ pr_debug("%s: ucontrol value = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ slim0_tx_sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 1:
+ slim0_tx_sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 0:
+ slim0_tx_sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ default:
+ rc = -EINVAL;
+ pr_err("%s: invalid sample rate being passed\n", __func__);
+ break;
+ }
+
+ pr_debug("%s: slim0_tx_sample_rate = %d\n", __func__,
+ slim0_tx_sample_rate);
+ return rc;
+}
+
+static int slim5_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ switch (slim5_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: slim5_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, slim5_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int slim5_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ slim5_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 1:
+ slim5_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ slim5_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return 0;
+}
+
+static int slim6_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ switch (slim6_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: slim6_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, slim6_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int slim6_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ slim6_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 1:
+ slim6_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ slim6_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return 0;
+}
+
+static int slim0_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ switch (slim0_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: slim0_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, slim0_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int slim0_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ slim0_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 1:
+ slim0_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ slim0_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return 0;
+}
+
+static int msm_slim_5_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_5_rx_ch = %d\n", __func__,
+ msm_slim_5_rx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_5_rx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_5_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_5_rx_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_slim_5_rx_ch = %d\n", __func__,
+ msm_slim_5_rx_ch);
+ return 1;
+}
+
+static int msm_slim_6_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_6_rx_ch = %d\n", __func__,
+ msm_slim_6_rx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_6_rx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_6_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_6_rx_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_slim_6_rx_ch = %d\n", __func__,
+ msm_slim_6_rx_ch);
+ return 1;
+}
+
+static int msm_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
+ msm_slim_0_rx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_0_rx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
+ msm_slim_0_rx_ch);
+ return 1;
+}
+
+static int msm_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
+ msm_slim_0_tx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_0_tx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__, msm_slim_0_tx_ch);
+ return 1;
+}
+
+static int msm_slim_1_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_1_tx_ch = %d\n", __func__,
+ msm_slim_1_tx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_1_tx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_1_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_1_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_slim_1_tx_ch = %d\n", __func__, msm_slim_1_tx_ch);
+ return 1;
+}
+
+static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1;
+ pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch);
+ return 1;
+}
+
+static int hdmi_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ switch (hdmi_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, hdmi_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int hdmi_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 1:
+ hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, hdmi_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_hdmi_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__,
+ msm_hdmi_rx_ch);
+ ucontrol->value.integer.value[0] = msm_hdmi_rx_ch - 2;
+
+ return 0;
+}
+
+static int msm_hdmi_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_hdmi_rx_ch = ucontrol->value.integer.value[0] + 2;
+ if (msm_hdmi_rx_ch > 8) {
+ pr_err("%s: channels %d exceeded 8.Limiting to max chs-8\n",
+ __func__, msm_hdmi_rx_ch);
+ msm_hdmi_rx_ch = 8;
+ }
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__, msm_hdmi_rx_ch);
+
+ return 1;
+}
+
+static int hdmi_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val = 0;
+
+ switch (hdmi_rx_sample_rate) {
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 2;
+ break;
+
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 1;
+ break;
+
+ case SAMPLING_RATE_48KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__,
+ hdmi_rx_sample_rate);
+
+ return 0;
+}
+
+static int hdmi_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: ucontrol value = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ hdmi_rx_sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 1:
+ hdmi_rx_sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 0:
+ default:
+ hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ;
+ }
+
+ pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__,
+ hdmi_rx_sample_rate);
+
+ return 0;
+}
+
+static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: usb_audio_rx_ch = %d\n", __func__,
+ usb_rx_cfg.channels);
+ ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1;
+ return 0;
+}
+
+static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels);
+ return 1;
+}
+
+static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val = 0;
+
+ switch (usb_rx_cfg.sample_rate) {
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 10;
+ break;
+ case SAMPLING_RATE_176P4KHZ:
+ sample_rate_val = 9;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 8;
+ break;
+ case SAMPLING_RATE_88P2KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_22P05KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_11P025KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__,
+ usb_rx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 10:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 9:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ;
+ break;
+ case 8:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 7:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ;
+ break;
+ case 6:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+ break;
+ case 2:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 1:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+ break;
+ case 0:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ default:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+
+ pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n",
+ __func__, ucontrol->value.integer.value[0],
+ usb_rx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (usb_rx_cfg.bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_rx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 1:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_rx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return rc;
+}
+
+static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: usb_audio_tx_ch = %d\n", __func__,
+ usb_tx_cfg.channels);
+ ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1;
+ return 0;
+}
+
+static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels);
+ return 1;
+}
+
+static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val = 0;
+
+ switch (usb_tx_cfg.sample_rate) {
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 10;
+ break;
+ case SAMPLING_RATE_176P4KHZ:
+ sample_rate_val = 9;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 8;
+ break;
+ case SAMPLING_RATE_88P2KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_22P05KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_11P025KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 0;
+ break;
+ default:
+ sample_rate_val = 6;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__,
+ usb_tx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 10:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 9:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ;
+ break;
+ case 8:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 7:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ;
+ break;
+ case 6:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+ break;
+ case 2:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 1:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+ break;
+ case 0:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ default:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+
+ pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n",
+ __func__, ucontrol->value.integer.value[0],
+ usb_tx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (usb_tx_cfg.bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_tx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 1:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_tx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return rc;
+}
+
+static int msm8996_auxpcm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm8996_auxpcm_rate;
+ return 0;
+}
+
+static int msm8996_auxpcm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm8996_auxpcm_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ msm8996_auxpcm_rate = SAMPLING_RATE_16KHZ;
+ break;
+ default:
+ msm8996_auxpcm_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ return 0;
+}
+
+static int msm_proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch);
+ ucontrol->value.integer.value[0] = msm_proxy_rx_ch - 1;
+ return 0;
+}
+
+static int msm_proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_proxy_rx_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch);
+ return 1;
+}
+
+static int msm_pri_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_tx_0_ch = %d\n", __func__,
+ msm_pri_tdm_tx_0_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_tx_0_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_tx_0_ch = %d\n", __func__,
+ msm_pri_tdm_tx_0_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: pri_tdm_tx_1_ch = %d\n", __func__,
+ msm_pri_tdm_tx_1_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_tx_1_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_tx_1_ch = %d\n", __func__,
+ msm_pri_tdm_tx_1_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_tx_2_ch = %d\n", __func__,
+ msm_pri_tdm_tx_2_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_tx_2_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_tx_2_ch = %d\n", __func__,
+ msm_pri_tdm_tx_2_ch);
+ return 0;
+}
+static int msm_pri_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_tx_3_ch = %d\n", __func__,
+ msm_pri_tdm_tx_3_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_tx_3_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_tx_3_ch = %d\n", __func__,
+ msm_pri_tdm_tx_3_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_rx_0_ch = %d\n", __func__,
+ msm_pri_tdm_rx_0_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_rx_0_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_rx_0_ch = %d\n", __func__,
+ msm_pri_tdm_rx_0_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_rx_1_ch = %d\n", __func__,
+ msm_pri_tdm_rx_1_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_rx_1_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_rx_1_ch = %d\n", __func__,
+ msm_pri_tdm_rx_1_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_rx_2_ch = %d\n", __func__,
+ msm_pri_tdm_rx_2_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_rx_2_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_rx_2_ch = %d\n", __func__,
+ msm_pri_tdm_rx_2_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_pri_tdm_rx_3_ch = %d\n", __func__,
+ msm_pri_tdm_rx_3_ch);
+ ucontrol->value.integer.value[0] = msm_pri_tdm_rx_3_ch - 1;
+ return 0;
+}
+
+static int msm_pri_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_pri_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_pri_tdm_rx_3_ch = %d\n", __func__,
+ msm_pri_tdm_rx_3_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_pri_tdm_rate;
+ pr_debug("%s: msm_pri_tdm_rate = %d\n", __func__, msm_pri_tdm_rate);
+ return 0;
+}
+static int msm_pri_tdm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_pri_tdm_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ msm_pri_tdm_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ default:
+ msm_pri_tdm_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rate = %d\n",
+ __func__, msm_pri_tdm_rate);
+ return 0;
+}
+
+static int msm_sec_tdm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_sec_tdm_rate;
+ pr_debug("%s: msm_sec_tdm_rate = %d\n", __func__, msm_sec_tdm_rate);
+ return 0;
+}
+
+static int msm_sec_tdm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_sec_tdm_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ msm_sec_tdm_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ default:
+ msm_sec_tdm_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rate = %d\n",
+ __func__, msm_sec_tdm_rate);
+ return 0;
+}
+
+static int msm_tert_tdm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rate;
+ pr_debug("%s: msm_tert_tdm_rate = %d\n", __func__, msm_tert_tdm_rate);
+ return 0;
+}
+
+static int msm_tert_tdm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_tert_tdm_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ msm_tert_tdm_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ default:
+ msm_tert_tdm_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rate = %d\n",
+ __func__, msm_tert_tdm_rate);
+ return 0;
+}
+
+static int msm_quat_tdm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rate;
+ pr_debug("%s: msm_quat_tdm_rate = %d\n", __func__, msm_quat_tdm_rate);
+ return 0;
+}
+static int msm_quat_tdm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_quat_tdm_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ msm_quat_tdm_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ default:
+ msm_quat_tdm_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rate = %d\n",
+ __func__, msm_quat_tdm_rate);
+ return 0;
+}
+
+static int msm_pri_tdm_slot_width_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_pri_tdm_slot_width;
+ pr_debug("%s: msm_pri_tdm_slot_width = %d\n",
+ __func__, msm_pri_tdm_slot_width);
+ return 0;
+}
+
+static int msm_pri_tdm_slot_width_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_pri_tdm_slot_width = 16;
+ break;
+ case 1:
+ msm_pri_tdm_slot_width = 24;
+ break;
+ case 2:
+ default:
+ msm_pri_tdm_slot_width = 32;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_slot_width= %d\n",
+ __func__, msm_pri_tdm_slot_width);
+ return 0;
+}
+
+static int msm_sec_tdm_slot_width_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_sec_tdm_slot_width;
+ pr_debug("%s: msm_sec_tdm_slot_width = %d\n",
+ __func__, msm_sec_tdm_slot_width);
+ return 0;
+}
+
+static int msm_sec_tdm_slot_width_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_sec_tdm_slot_width = 16;
+ break;
+ case 1:
+ msm_sec_tdm_slot_width = 24;
+ break;
+ case 2:
+ default:
+ msm_sec_tdm_slot_width = 32;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_slot_width= %d\n",
+ __func__, msm_sec_tdm_slot_width);
+ return 0;
+}
+
+static int msm_tert_tdm_slot_width_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_tert_tdm_slot_width;
+ pr_debug("%s: msm_tert_tdm_slot_width = %d\n",
+ __func__, msm_tert_tdm_slot_width);
+ return 0;
+}
+
+static int msm_tert_tdm_slot_width_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_tert_tdm_slot_width = 16;
+ break;
+ case 1:
+ msm_tert_tdm_slot_width = 24;
+ break;
+ case 2:
+ default:
+ msm_tert_tdm_slot_width = 32;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_slot_width= %d\n",
+ __func__, msm_tert_tdm_slot_width);
+ return 0;
+}
+
+static int msm_quat_tdm_slot_width_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_quat_tdm_slot_width;
+ pr_debug("%s: msm_quat_tdm_slot_width = %d\n",
+ __func__, msm_quat_tdm_slot_width);
+ return 0;
+}
+
+static int msm_quat_tdm_slot_width_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_quat_tdm_slot_width = 16;
+ break;
+ case 1:
+ msm_quat_tdm_slot_width = 24;
+ break;
+ case 2:
+ default:
+ msm_quat_tdm_slot_width = 32;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_slot_width= %d\n",
+ __func__, msm_quat_tdm_slot_width);
+ return 0;
+}
+
+
+static int msm_pri_tdm_slot_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_slot_num) {
+ case 1:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ case 2:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case 4:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case 8:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case 16:
+ ucontrol->value.integer.value[0] = 4;
+ break;
+ case 32:
+ default:
+ ucontrol->value.integer.value[0] = 5;
+ break;
+ }
+
+ pr_debug("%s: msm_pri_tdm_slot_num = %d\n",
+ __func__, msm_pri_tdm_slot_num);
+ return 0;
+}
+
+static int msm_pri_tdm_slot_num_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_pri_tdm_slot_num = 1;
+ break;
+ case 1:
+ msm_pri_tdm_slot_num = 2;
+ break;
+ case 2:
+ msm_pri_tdm_slot_num = 4;
+ break;
+ case 3:
+ msm_pri_tdm_slot_num = 8;
+ break;
+ case 4:
+ msm_pri_tdm_slot_num = 16;
+ break;
+ case 5:
+ msm_pri_tdm_slot_num = 32;
+ break;
+ default:
+ msm_pri_tdm_slot_num = 8;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_slot_num = %d\n",
+ __func__, msm_pri_tdm_slot_num);
+ return 0;
+}
+
+static int msm_sec_tdm_slot_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_slot_num) {
+ case 1:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ case 2:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case 4:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case 8:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case 16:
+ ucontrol->value.integer.value[0] = 4;
+ break;
+ case 32:
+ default:
+ ucontrol->value.integer.value[0] = 5;
+ break;
+ }
+
+ pr_debug("%s: msm_sec_tdm_slot_num = %d\n",
+ __func__, msm_sec_tdm_slot_num);
+ return 0;
+}
+
+static int msm_sec_tdm_slot_num_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_sec_tdm_slot_num = 1;
+ break;
+ case 1:
+ msm_sec_tdm_slot_num = 2;
+ break;
+ case 2:
+ msm_sec_tdm_slot_num = 4;
+ break;
+ case 3:
+ msm_sec_tdm_slot_num = 8;
+ break;
+ case 4:
+ msm_sec_tdm_slot_num = 16;
+ break;
+ case 5:
+ msm_sec_tdm_slot_num = 32;
+ break;
+ default:
+ msm_sec_tdm_slot_num = 8;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_slot_num = %d\n",
+ __func__, msm_sec_tdm_slot_num);
+ return 0;
+}
+
+static int msm_tert_tdm_slot_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_slot_num) {
+ case 1:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ case 2:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case 4:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case 8:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case 16:
+ ucontrol->value.integer.value[0] = 4;
+ break;
+ case 32:
+ default:
+ ucontrol->value.integer.value[0] = 5;
+ break;
+ }
+
+ pr_debug("%s: msm_tert_tdm_slot_num = %d\n",
+ __func__, msm_tert_tdm_slot_num);
+ return 0;
+}
+
+static int msm_tert_tdm_slot_num_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_tert_tdm_slot_num = 1;
+ break;
+ case 1:
+ msm_tert_tdm_slot_num = 2;
+ break;
+ case 2:
+ msm_tert_tdm_slot_num = 4;
+ break;
+ case 3:
+ msm_tert_tdm_slot_num = 8;
+ break;
+ case 4:
+ msm_tert_tdm_slot_num = 16;
+ break;
+ case 5:
+ msm_tert_tdm_slot_num = 32;
+ break;
+ default:
+ msm_tert_tdm_slot_num = 8;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_slot_num = %d\n",
+ __func__, msm_tert_tdm_slot_num);
+ return 0;
+}
+
+static int msm_quat_tdm_slot_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_slot_num) {
+ case 1:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ case 2:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case 4:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case 8:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case 16:
+ ucontrol->value.integer.value[0] = 4;
+ break;
+ case 32:
+ default:
+ ucontrol->value.integer.value[0] = 5;
+ break;
+ }
+
+ pr_debug("%s: msm_quat_tdm_slot_num = %d\n",
+ __func__, msm_quat_tdm_slot_num);
+ return 0;
+}
+static int msm_quat_tdm_slot_num_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_quat_tdm_slot_num = 1;
+ break;
+ case 1:
+ msm_quat_tdm_slot_num = 2;
+ break;
+ case 2:
+ msm_quat_tdm_slot_num = 4;
+ break;
+ case 3:
+ msm_quat_tdm_slot_num = 8;
+ break;
+ case 4:
+ msm_quat_tdm_slot_num = 16;
+ break;
+ case 5:
+ msm_quat_tdm_slot_num = 32;
+ break;
+ default:
+ msm_quat_tdm_slot_num = 8;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_slot_num = %d\n",
+ __func__, msm_quat_tdm_slot_num);
+ return 0;
+}
+
+static int msm_tdm_slot_mapping_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_multi_mixer_control *mc =
+ (struct soc_multi_mixer_control *)kcontrol->private_value;
+ unsigned int *slot_offset;
+ int i;
+
+ if (mc->shift >= TDM_MAX) {
+ pr_err("%s invalid port index %d\n", __func__, mc->shift);
+ return -EINVAL;
+ }
+
+ slot_offset = tdm_slot_offset[mc->shift];
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ ucontrol->value.integer.value[i] = slot_offset[i];
+ pr_debug("%s port index %d offset %d value %d\n",
+ __func__, mc->shift, i, slot_offset[i]);
+ }
+
+ return 0;
+}
+
+static int msm_tdm_slot_mapping_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_multi_mixer_control *mc =
+ (struct soc_multi_mixer_control *)kcontrol->private_value;
+ unsigned int *slot_offset;
+ int i;
+
+ if (mc->shift >= TDM_MAX) {
+ pr_err("%s invalid port index %d\n", __func__, mc->shift);
+ return -EINVAL;
+ }
+
+ slot_offset = tdm_slot_offset[mc->shift];
+
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ slot_offset[i] = ucontrol->value.integer.value[i];
+ pr_debug("%s port index %d offset %d value %d\n",
+ __func__, mc->shift, i, slot_offset[i]);
+ }
+
+ return 0;
+}
+
+static int msm_sec_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_rx_0_ch = %d\n", __func__,
+ msm_sec_tdm_rx_0_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_rx_0_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_rx_0_ch = %d\n", __func__,
+ msm_sec_tdm_rx_0_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_rx_1_ch = %d\n", __func__,
+ msm_sec_tdm_rx_1_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_rx_1_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_rx_1_ch = %d\n", __func__,
+ msm_sec_tdm_rx_1_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_rx_2_ch = %d\n", __func__,
+ msm_sec_tdm_rx_2_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_rx_2_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_rx_2_ch = %d\n", __func__,
+ msm_sec_tdm_rx_2_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_rx_3_ch = %d\n", __func__,
+ msm_sec_tdm_rx_3_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_rx_3_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_rx_3_ch = %d\n", __func__,
+ msm_sec_tdm_rx_3_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_0_ch = %d\n", __func__,
+ msm_sec_tdm_tx_0_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_0_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_0_ch = %d\n", __func__,
+ msm_sec_tdm_tx_0_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_1_ch = %d\n", __func__,
+ msm_sec_tdm_tx_1_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_1_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_1_ch = %d\n", __func__,
+ msm_sec_tdm_tx_1_ch);
+ return 0;
+}
+static int msm_sec_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_2_ch = %d\n", __func__,
+ msm_sec_tdm_tx_2_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_2_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_2_ch = %d\n", __func__,
+ msm_sec_tdm_tx_2_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_3_ch = %d\n", __func__,
+ msm_sec_tdm_tx_3_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_3_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_3_ch = %d\n", __func__,
+ msm_sec_tdm_tx_3_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_0_ch = %d\n", __func__,
+ msm_tert_tdm_rx_0_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_0_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_0_ch = %d\n", __func__,
+ msm_tert_tdm_rx_0_ch);
+ return 0;
+}
+static int msm_tert_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_1_ch = %d\n", __func__,
+ msm_tert_tdm_rx_1_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_1_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_1_ch = %d\n", __func__,
+ msm_tert_tdm_rx_1_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_2_ch = %d\n", __func__,
+ msm_tert_tdm_rx_2_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_2_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_2_ch = %d\n", __func__,
+ msm_tert_tdm_rx_2_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_3_ch = %d\n", __func__,
+ msm_tert_tdm_rx_3_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_3_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_3_ch = %d\n", __func__,
+ msm_tert_tdm_rx_3_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_0_ch = %d\n", __func__,
+ msm_tert_tdm_tx_0_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_0_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_0_ch = %d\n", __func__,
+ msm_tert_tdm_tx_0_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_1_ch = %d\n", __func__,
+ msm_tert_tdm_tx_1_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_1_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_1_ch = %d\n", __func__,
+ msm_tert_tdm_tx_1_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_2_ch = %d\n", __func__,
+ msm_tert_tdm_tx_2_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_2_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_2_ch = %d\n", __func__,
+ msm_tert_tdm_tx_2_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_3_ch = %d\n", __func__,
+ msm_tert_tdm_tx_3_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_3_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_3_ch = %d\n", __func__,
+ msm_tert_tdm_tx_3_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_0_ch = %d\n", __func__,
+ msm_quat_tdm_rx_0_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_0_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_0_ch = %d\n", __func__,
+ msm_quat_tdm_rx_0_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_1_ch = %d\n", __func__,
+ msm_quat_tdm_rx_1_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_1_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_1_ch = %d\n", __func__,
+ msm_quat_tdm_rx_1_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_2_ch = %d\n", __func__,
+ msm_quat_tdm_rx_2_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_2_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_2_ch = %d\n", __func__,
+ msm_quat_tdm_rx_2_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_3_ch = %d\n", __func__,
+ msm_quat_tdm_rx_3_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_3_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_3_ch = %d\n", __func__,
+ msm_quat_tdm_rx_3_ch);
+ return 0;
+}
+static int msm_quat_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_0_ch = %d\n", __func__,
+ msm_quat_tdm_tx_0_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_0_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_0_ch = %d\n", __func__,
+ msm_quat_tdm_tx_0_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_1_ch = %d\n", __func__,
+ msm_quat_tdm_tx_1_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_1_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_1_ch = %d\n", __func__,
+ msm_quat_tdm_tx_1_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_2_ch = %d\n", __func__,
+ msm_quat_tdm_tx_2_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_2_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_2_ch = %d\n", __func__,
+ msm_quat_tdm_tx_2_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_3_ch = %d\n", __func__,
+ msm_quat_tdm_tx_3_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_3_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_3_ch = %d\n", __func__,
+ msm_quat_tdm_tx_3_ch);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_tx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_0_bit_format = %d\n",
+ __func__, msm_pri_tdm_tx_0_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_tx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_1_bit_format = %d\n",
+ __func__, msm_pri_tdm_tx_1_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_tx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_2_bit_format = %d\n",
+ __func__, msm_pri_tdm_tx_2_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_tx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_tx_3_bit_format = %d\n",
+ __func__, msm_pri_tdm_tx_3_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_rx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_0_bit_format = %d\n",
+ __func__, msm_pri_tdm_rx_0_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_rx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_1_bit_format = %d\n",
+ __func__, msm_pri_tdm_rx_1_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_rx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_2_bit_format = %d\n",
+ __func__, msm_pri_tdm_rx_2_bit_format);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_pri_tdm_rx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_pri_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_pri_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_pri_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_pri_tdm_rx_3_bit_format = %d\n",
+ __func__, msm_pri_tdm_rx_3_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_rx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_0_bit_format = %d\n",
+ __func__, msm_sec_tdm_rx_0_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_rx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_1_bit_format = %d\n",
+ __func__, msm_sec_tdm_rx_1_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_rx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_2_bit_format = %d\n",
+ __func__, msm_sec_tdm_rx_2_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_rx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_rx_3_bit_format = %d\n",
+ __func__, msm_sec_tdm_rx_3_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_0_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_0_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_1_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_1_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+static int msm_sec_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_2_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_2_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_3_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_3_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_0_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_0_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_1_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_1_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_2_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_2_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_3_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_3_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_0_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_0_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_1_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_1_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_2_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_2_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_3_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_3_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_0_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_0_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+static int msm_quat_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_1_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_1_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_2_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_2_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_3_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_3_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+static int msm_quat_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_0_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_0_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_1_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_1_bit_format);
+ return 0;
+}
+static int msm_quat_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_2_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_2_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+static int msm_quat_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_3_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_3_bit_format);
+ return 0;
+}
+
+
+static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = msm8996_auxpcm_rate;
+ channels->min = channels->max = 1;
+
+ return 0;
+}
+
+static int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s: msm_proxy_rx_ch =%d\n", __func__, msm_proxy_rx_ch);
+
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
+ channels->min = channels->max = msm_proxy_rx_ch;
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ return 0;
+}
+
+static int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ return 0;
+}
+
+static int msm8996_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
+ channels->min, channels->max);
+
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ hdmi_rx_bit_format);
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
+ rate->min = rate->max = hdmi_rx_sample_rate;
+ channels->min = channels->max = msm_hdmi_rx_ch;
+
+ return 0;
+}
+
+static int msm_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s: channel:%d\n", __func__, msm_tert_mi2s_tx_ch);
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ channels->min = channels->max = msm_tert_mi2s_tx_ch;
+ return 0;
+}
+
+static int msm8996_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ pr_debug("%s: substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ mi2s_tx_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX,
+ &mi2s_tx_clk);
+ if (ret < 0) {
+ pr_err("%s: afe lpass clock failed, err:%d\n", __func__, ret);
+ goto err;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ pr_err("%s: set fmt cpu dai failed, err:%d\n", __func__, ret);
+err:
+ return ret;
+}
+
+static void msm8996_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ pr_debug("%s: substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ mi2s_tx_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX,
+ &mi2s_tx_clk);
+ if (ret < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n", __func__, ret);
+}
+
+static struct snd_soc_ops msm8996_mi2s_be_ops = {
+ .startup = msm8996_mi2s_snd_startup,
+ .shutdown = msm8996_mi2s_snd_shutdown,
+};
+
+static int msm_slim_5_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim5_rx_bit_format);
+ rate->min = rate->max = slim5_rx_sample_rate;
+ channels->min = channels->max = msm_slim_5_rx_ch;
+
+ pr_debug("%s: format = %d, rate = %d, channels = %d\n",
+ __func__, params_format(params), params_rate(params),
+ msm_slim_5_rx_ch);
+
+ return 0;
+}
+
+static int msm_slim_6_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim6_rx_bit_format);
+ rate->min = rate->max = slim6_rx_sample_rate;
+ channels->min = channels->max = msm_slim_6_rx_ch;
+
+ pr_debug("%s: format = %d, rate = %d, channels = %d\n",
+ __func__, params_format(params), params_rate(params),
+ msm_slim_6_rx_ch);
+
+ return 0;
+}
+
+static int msm_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim0_rx_bit_format);
+ rate->min = rate->max = slim0_rx_sample_rate;
+ channels->min = channels->max = msm_slim_0_rx_ch;
+
+ pr_debug("%s: format = %d, rate = %d, channels = %d\n",
+ __func__, params_format(params), params_rate(params),
+ msm_slim_0_rx_ch);
+
+ return 0;
+}
+
+static int msm_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, slim0_tx_bit_format);
+ rate->min = rate->max = slim0_tx_sample_rate;
+ channels->min = channels->max = msm_slim_0_tx_ch;
+
+ return 0;
+}
+
+static int msm_slim_1_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, slim1_tx_bit_format);
+ rate->min = rate->max = slim1_tx_sample_rate;
+ channels->min = channels->max = msm_slim_1_tx_ch;
+
+ return 0;
+}
+
+static int msm_slim_4_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_FORMAT_S32_LE);
+
+ rate->min = rate->max = SAMPLING_RATE_8KHZ;
+ channels->min = channels->max = msm_vi_feed_tx_ch;
+ pr_debug("%s: msm_vi_feed_tx_ch: %d\n", __func__, msm_vi_feed_tx_ch);
+
+ return 0;
+}
+
+static int msm_slim_5_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ int rc = 0;
+ void *config = NULL;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s: enter\n", __func__);
+ rate->min = rate->max = SAMPLING_RATE_16KHZ;
+ channels->min = channels->max = 1;
+
+ config = msm8996_codec_fn.get_afe_config_fn(codec,
+ AFE_SLIMBUS_SLAVE_PORT_CONFIG);
+ if (config) {
+ rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, config,
+ SLIMBUS_5_TX);
+ if (rc) {
+ pr_err("%s: Failed to set slimbus slave port config %d\n",
+ __func__, rc);
+ }
+ }
+
+ return rc;
+}
+
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s: format = %d, rate = %d\n",
+ __func__, params_format(params), params_rate(params));
+
+ switch (dai_link->be_id) {
+ case MSM_BACKEND_DAI_USB_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ usb_rx_cfg.bit_format);
+ rate->min = rate->max = usb_rx_cfg.sample_rate;
+ channels->min = channels->max = usb_rx_cfg.channels;
+ break;
+
+ case MSM_BACKEND_DAI_USB_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ usb_tx_cfg.bit_format);
+ rate->min = rate->max = usb_tx_cfg.sample_rate;
+ channels->min = channels->max = usb_tx_cfg.channels;
+ break;
+
+ default:
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return 0;
+}
+static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ channels->min = channels->max = msm_pri_tdm_tx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_tx_0_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ channels->min = channels->max = msm_pri_tdm_tx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_tx_1_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ channels->min = channels->max = msm_pri_tdm_tx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_tx_2_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ channels->min = channels->max = msm_pri_tdm_tx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_tx_3_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ channels->min = channels->max = msm_pri_tdm_rx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_rx_0_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ channels->min = channels->max = msm_pri_tdm_rx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_rx_1_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ channels->min = channels->max = msm_pri_tdm_rx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_rx_2_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ channels->min = channels->max = msm_pri_tdm_rx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_pri_tdm_rx_3_bit_format);
+ rate->min = rate->max = msm_pri_tdm_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ channels->min = channels->max = msm_sec_tdm_rx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_rx_0_bit_format);
+ rate->min = rate->max = msm_sec_tdm_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ channels->min = channels->max = msm_sec_tdm_rx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_rx_1_bit_format);
+ rate->min = rate->max = msm_sec_tdm_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ channels->min = channels->max = msm_sec_tdm_rx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_rx_2_bit_format);
+ rate->min = rate->max = msm_sec_tdm_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ channels->min = channels->max = msm_sec_tdm_rx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_rx_3_bit_format);
+ rate->min = rate->max = msm_sec_tdm_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ channels->min = channels->max = msm_sec_tdm_tx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_0_bit_format);
+ rate->min = rate->max = msm_sec_tdm_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ channels->min = channels->max = msm_sec_tdm_tx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_1_bit_format);
+ rate->min = rate->max = msm_sec_tdm_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ channels->min = channels->max = msm_sec_tdm_tx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_2_bit_format);
+ rate->min = rate->max = msm_sec_tdm_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ channels->min = channels->max = msm_sec_tdm_tx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_3_bit_format);
+ rate->min = rate->max = msm_sec_tdm_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ channels->min = channels->max = msm_tert_tdm_rx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_0_bit_format);
+ rate->min = rate->max = msm_tert_tdm_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ channels->min = channels->max = msm_tert_tdm_rx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_1_bit_format);
+ rate->min = rate->max = msm_tert_tdm_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ channels->min = channels->max = msm_tert_tdm_rx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_2_bit_format);
+ rate->min = rate->max = msm_tert_tdm_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ channels->min = channels->max = msm_tert_tdm_rx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_3_bit_format);
+ rate->min = rate->max = msm_tert_tdm_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ channels->min = channels->max = msm_tert_tdm_tx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_0_bit_format);
+ rate->min = rate->max = msm_tert_tdm_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ channels->min = channels->max = msm_tert_tdm_tx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_1_bit_format);
+ rate->min = rate->max = msm_tert_tdm_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ channels->min = channels->max = msm_tert_tdm_tx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_2_bit_format);
+ rate->min = rate->max = msm_tert_tdm_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ channels->min = channels->max = msm_tert_tdm_tx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_3_bit_format);
+ rate->min = rate->max = msm_tert_tdm_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ channels->min = channels->max = msm_quat_tdm_rx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_0_bit_format);
+ rate->min = rate->max = msm_quat_tdm_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ channels->min = channels->max = msm_quat_tdm_rx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_1_bit_format);
+ rate->min = rate->max = msm_quat_tdm_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ channels->min = channels->max = msm_quat_tdm_rx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_2_bit_format);
+ rate->min = rate->max = msm_quat_tdm_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ channels->min = channels->max = msm_quat_tdm_rx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_3_bit_format);
+ rate->min = rate->max = msm_quat_tdm_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ channels->min = channels->max = msm_quat_tdm_tx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_0_bit_format);
+ rate->min = rate->max = msm_quat_tdm_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ channels->min = channels->max = msm_quat_tdm_tx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_1_bit_format);
+ rate->min = rate->max = msm_quat_tdm_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ channels->min = channels->max = msm_quat_tdm_tx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_2_bit_format);
+ rate->min = rate->max = msm_quat_tdm_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ channels->min = channels->max = msm_quat_tdm_tx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_3_bit_format);
+ rate->min = rate->max = msm_quat_tdm_rate;
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n",
+ __func__, cpu_dai->id, channels->max, rate->max,
+ params_format(params));
+
+ return 0;
+}
+
+static unsigned int tdm_param_set_slot_mask(int slots)
+{
+ unsigned int slot_mask = 0;
+ unsigned int i = 0;
+
+ if ((slots != 16) && (slots != 8)) {
+ pr_err("%s: invalid slot number %d\n", __func__, slots);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < slots ; i++)
+ slot_mask |= 1 << i;
+ return slot_mask;
+}
+
+static int msm8996_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ int channels, slot_width, slots, rate;
+ unsigned int slot_mask;
+ unsigned int *slot_offset;
+ int offset_channels = 0;
+ int i;
+ int clk_freq;
+
+ pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+ rate = params_rate(params);
+ channels = params_channels(params);
+ if (channels < 1 || channels > 8) {
+ pr_err("%s: invalid param channels %d\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /*
+ * up to 8 channel HW configuration should
+ * use 32 bit slot width for max support of
+ * stream bit width. (slot_width > bit_width)
+ */
+ slot_width = msm_tdm_slot_width;
+ break;
+ default:
+ pr_err("%s: invalid param format 0x%x\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ slots = msm_tdm_num_slots;
+
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_0];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_1];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_2];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_3];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_4];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_5];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_6];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_7];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_0];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_1];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_2];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_3];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_4];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_5];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_6];
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ slots = msm_pri_tdm_slot_num;
+ slot_width = msm_pri_tdm_slot_width;
+ slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_7];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_0];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_1];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_2];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_3];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_4];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_5];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_6];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_7];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_0];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_1];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_2];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_3];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_4];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_5];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_6];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ slots = msm_sec_tdm_slot_num;
+ slot_width = msm_sec_tdm_slot_width;
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_7];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_0];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_1];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_2];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_3];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_4];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_5];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_6];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_7];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_0];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_1];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_2];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_3];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_4];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_5];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_6];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ slots = msm_tert_tdm_slot_num;
+ slot_width = msm_tert_tdm_slot_width;
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_7];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_0];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_1];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_2];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_3];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_4];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_5];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_6];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_7];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_0];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_1];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_2];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_3];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_4];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_5];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_6];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ slots = msm_quat_tdm_slot_num;
+ slot_width = msm_quat_tdm_slot_width;
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_7];
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ offset_channels++;
+ else
+ break;
+ }
+
+ if (offset_channels == 0) {
+ pr_err("%s: slot offset not supported, offset_channels %d\n",
+ __func__, offset_channels);
+ return -EINVAL;
+ }
+
+ if (channels > offset_channels) {
+ pr_err("%s: channels %d exceed offset_channels %d\n",
+ __func__, channels, offset_channels);
+ return -EINVAL;
+ }
+
+ slot_mask = tdm_param_set_slot_mask(slots);
+ if (!slot_mask) {
+ pr_err("%s: invalid slot_mask 0x%x\n",
+ __func__, slot_mask);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ 0, NULL, channels, slot_offset);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ channels, slot_offset, 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+
+ clk_freq = rate * slot_width * slots;
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm clk, err:%d\n",
+ __func__, ret);
+ }
+
+end:
+ return ret;
+}
+
+static struct snd_soc_ops msm8996_tdm_be_ops = {
+ .hw_params = msm8996_tdm_snd_hw_params,
+};
+
+
+static const struct soc_enum msm_snd_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+ SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(8, slim0_tx_ch_text),
+ SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_bit_format_text),
+ rx_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim0_rx_sample_rate_text),
+ slim0_rx_sample_rate_text),
+ SOC_ENUM_SINGLE_EXT(8, proxy_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(3, hdmi_rx_sample_rate_text),
+ SOC_ENUM_SINGLE_EXT(4, slim5_rx_sample_rate_text),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim5_rx_bit_format_text),
+ slim5_rx_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(2, slim5_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(2, hifi_function),
+ SOC_ENUM_SINGLE_EXT(2, vi_feed_ch_text),
+ SOC_ENUM_SINGLE_EXT(4, slim6_rx_sample_rate_text),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim6_rx_bit_format_text),
+ slim6_rx_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(2, slim6_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_ch_text), tdm_ch_text),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_bit_format_text),
+ tdm_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_rate_text), tdm_rate_text),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_slot_num_text),
+ tdm_slot_num_text),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_slot_width_text),
+ tdm_slot_width_text),
+};
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+ SOC_ENUM_EXT("Speaker Function", msm_snd_enum[0], msm8996_get_spk,
+ msm8996_set_spk),
+ SOC_ENUM_EXT("SLIM_0_RX Channels", msm_snd_enum[1],
+ msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_5_RX Channels", msm_snd_enum[10],
+ msm_slim_5_rx_ch_get, msm_slim_5_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_6_RX Channels", msm_snd_enum[15],
+ msm_slim_6_rx_ch_get, msm_slim_6_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_TX Channels", msm_snd_enum[2],
+ msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
+ SOC_ENUM_EXT("SLIM_1_TX Channels", msm_snd_enum[2],
+ msm_slim_1_tx_ch_get, msm_slim_1_tx_ch_put),
+ SOC_ENUM_EXT("AUX PCM SampleRate", msm8996_auxpcm_enum[0],
+ msm8996_auxpcm_rate_get,
+ msm8996_auxpcm_rate_put),
+ SOC_ENUM_EXT("HDMI_RX Channels", msm_snd_enum[3],
+ msm_hdmi_rx_ch_get, msm_hdmi_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_RX Format", msm_snd_enum[4],
+ slim0_rx_bit_format_get, slim0_rx_bit_format_put),
+ SOC_ENUM_EXT("SLIM_5_RX Format", msm_snd_enum[9],
+ slim5_rx_bit_format_get, slim5_rx_bit_format_put),
+ SOC_ENUM_EXT("SLIM_6_RX Format", msm_snd_enum[14],
+ slim6_rx_bit_format_get, slim6_rx_bit_format_put),
+ SOC_ENUM_EXT("SLIM_0_RX SampleRate", msm_snd_enum[5],
+ slim0_rx_sample_rate_get, slim0_rx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_5_RX SampleRate", msm_snd_enum[8],
+ slim5_rx_sample_rate_get, slim5_rx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_6_RX SampleRate", msm_snd_enum[13],
+ slim6_rx_sample_rate_get, slim6_rx_sample_rate_put),
+ SOC_ENUM_EXT("HDMI_RX Bit Format", msm_snd_enum[4],
+ hdmi_rx_bit_format_get, hdmi_rx_bit_format_put),
+ SOC_ENUM_EXT("PROXY_RX Channels", msm_snd_enum[6],
+ msm_proxy_rx_ch_get, msm_proxy_rx_ch_put),
+ SOC_ENUM_EXT("HDMI_RX SampleRate", msm_snd_enum[7],
+ hdmi_rx_sample_rate_get, hdmi_rx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_0_TX SampleRate", msm_snd_enum[5],
+ slim0_tx_sample_rate_get, slim0_tx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_0_TX Format", msm_snd_enum[4],
+ slim0_tx_bit_format_get, slim0_tx_bit_format_put),
+ SOC_ENUM_EXT("HiFi Function", msm_snd_enum[11], msm8996_hifi_get,
+ msm8996_hifi_put),
+ SOC_ENUM_EXT("VI_FEED_TX Channels", msm_snd_enum[12],
+ msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put),
+ SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs,
+ usb_audio_rx_ch_get, usb_audio_rx_ch_put),
+ SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs,
+ usb_audio_tx_ch_get, usb_audio_tx_ch_put),
+ SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate,
+ usb_audio_rx_sample_rate_get,
+ usb_audio_rx_sample_rate_put),
+ SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate,
+ usb_audio_tx_sample_rate_get,
+ usb_audio_tx_sample_rate_put),
+ SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format,
+ usb_audio_rx_format_get, usb_audio_rx_format_put),
+ SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format,
+ usb_audio_tx_format_get, usb_audio_tx_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", msm_snd_enum[16],
+ msm_pri_tdm_tx_0_ch_get, msm_pri_tdm_tx_0_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_1 Channels", msm_snd_enum[16],
+ msm_pri_tdm_tx_1_ch_get, msm_pri_tdm_tx_1_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_2 Channels", msm_snd_enum[16],
+ msm_pri_tdm_tx_2_ch_get, msm_pri_tdm_tx_2_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_3 Channels", msm_snd_enum[16],
+ msm_pri_tdm_tx_3_ch_get, msm_pri_tdm_tx_3_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", msm_snd_enum[16],
+ msm_pri_tdm_rx_0_ch_get, msm_pri_tdm_rx_0_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_1 Channels", msm_snd_enum[16],
+ msm_pri_tdm_rx_1_ch_get, msm_pri_tdm_rx_1_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_2 Channels", msm_snd_enum[16],
+ msm_pri_tdm_rx_2_ch_get, msm_pri_tdm_rx_2_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_3 Channels", msm_snd_enum[16],
+ msm_pri_tdm_rx_3_ch_get, msm_pri_tdm_rx_3_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", msm_snd_enum[16],
+ msm_sec_tdm_rx_0_ch_get, msm_sec_tdm_rx_0_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_1 Channels", msm_snd_enum[16],
+ msm_sec_tdm_rx_1_ch_get, msm_sec_tdm_rx_1_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_2 Channels", msm_snd_enum[16],
+ msm_sec_tdm_rx_2_ch_get, msm_sec_tdm_rx_2_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_3 Channels", msm_snd_enum[16],
+ msm_sec_tdm_rx_3_ch_get, msm_sec_tdm_rx_3_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", msm_snd_enum[16],
+ msm_sec_tdm_tx_0_ch_get, msm_sec_tdm_tx_0_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_1 Channels", msm_snd_enum[16],
+ msm_sec_tdm_tx_1_ch_get, msm_sec_tdm_tx_1_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_2 Channels", msm_snd_enum[16],
+ msm_sec_tdm_tx_2_ch_get, msm_sec_tdm_tx_2_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_3 Channels", msm_snd_enum[16],
+ msm_sec_tdm_tx_3_ch_get, msm_sec_tdm_tx_3_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", msm_snd_enum[16],
+ msm_tert_tdm_rx_0_ch_get, msm_tert_tdm_rx_0_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_1 Channels", msm_snd_enum[16],
+ msm_tert_tdm_rx_1_ch_get, msm_tert_tdm_rx_1_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_2 Channels", msm_snd_enum[16],
+ msm_tert_tdm_rx_2_ch_get, msm_tert_tdm_rx_2_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_3 Channels", msm_snd_enum[16],
+ msm_tert_tdm_rx_3_ch_get, msm_tert_tdm_rx_3_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", msm_snd_enum[16],
+ msm_tert_tdm_tx_0_ch_get, msm_tert_tdm_tx_0_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_1 Channels", msm_snd_enum[16],
+ msm_tert_tdm_tx_1_ch_get, msm_tert_tdm_tx_1_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_2 Channels", msm_snd_enum[16],
+ msm_tert_tdm_tx_2_ch_get, msm_tert_tdm_tx_2_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_3 Channels", msm_snd_enum[16],
+ msm_tert_tdm_tx_3_ch_get, msm_tert_tdm_tx_3_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", msm_snd_enum[16],
+ msm_quat_tdm_rx_0_ch_get, msm_quat_tdm_rx_0_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_1 Channels", msm_snd_enum[16],
+ msm_quat_tdm_rx_1_ch_get, msm_quat_tdm_rx_1_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_2 Channels", msm_snd_enum[16],
+ msm_quat_tdm_rx_2_ch_get, msm_quat_tdm_rx_2_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_3 Channels", msm_snd_enum[16],
+ msm_quat_tdm_rx_3_ch_get, msm_quat_tdm_rx_3_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", msm_snd_enum[16],
+ msm_quat_tdm_tx_0_ch_get, msm_quat_tdm_tx_0_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_1 Channels", msm_snd_enum[16],
+ msm_quat_tdm_tx_1_ch_get, msm_quat_tdm_tx_1_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_2 Channels", msm_snd_enum[16],
+ msm_quat_tdm_tx_2_ch_get, msm_quat_tdm_tx_2_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_3 Channels", msm_snd_enum[16],
+ msm_quat_tdm_tx_3_ch_get, msm_quat_tdm_tx_3_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 Bit Format", msm_snd_enum[17],
+ msm_pri_tdm_tx_0_bit_format_get,
+ msm_pri_tdm_tx_0_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_1 Bit Format", msm_snd_enum[17],
+ msm_pri_tdm_tx_1_bit_format_get,
+ msm_pri_tdm_tx_1_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_2 Bit Format", msm_snd_enum[17],
+ msm_pri_tdm_tx_2_bit_format_get,
+ msm_pri_tdm_tx_2_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_3 Bit Format", msm_snd_enum[17],
+ msm_pri_tdm_tx_3_bit_format_get,
+ msm_pri_tdm_tx_3_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_0 Bit Format", msm_snd_enum[17],
+ msm_pri_tdm_rx_0_bit_format_get,
+ msm_pri_tdm_rx_0_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_1 Bit Format", msm_snd_enum[17],
+ msm_pri_tdm_rx_1_bit_format_get,
+ msm_pri_tdm_rx_1_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_2 Bit Format", msm_snd_enum[17],
+ msm_pri_tdm_rx_2_bit_format_get,
+ msm_pri_tdm_rx_2_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_3 Bit Format", msm_snd_enum[17],
+ msm_pri_tdm_rx_3_bit_format_get,
+ msm_pri_tdm_rx_3_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 Bit Format", msm_snd_enum[17],
+ msm_sec_tdm_rx_0_bit_format_get,
+ msm_sec_tdm_rx_0_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_1 Bit Format", msm_snd_enum[17],
+ msm_sec_tdm_rx_1_bit_format_get,
+ msm_sec_tdm_rx_1_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_2 Bit Format", msm_snd_enum[17],
+ msm_sec_tdm_rx_2_bit_format_get,
+ msm_sec_tdm_rx_2_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_3 Bit Format", msm_snd_enum[17],
+ msm_sec_tdm_rx_3_bit_format_get,
+ msm_sec_tdm_rx_3_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Bit Format", msm_snd_enum[17],
+ msm_sec_tdm_tx_0_bit_format_get,
+ msm_sec_tdm_tx_0_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_1 Bit Format", msm_snd_enum[17],
+ msm_sec_tdm_tx_1_bit_format_get,
+ msm_sec_tdm_tx_1_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_2 Bit Format", msm_snd_enum[17],
+ msm_sec_tdm_tx_2_bit_format_get,
+ msm_sec_tdm_tx_2_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_3 Bit Format", msm_snd_enum[17],
+ msm_sec_tdm_tx_3_bit_format_get,
+ msm_sec_tdm_tx_3_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Bit Format", msm_snd_enum[17],
+ msm_tert_tdm_rx_0_bit_format_get,
+ msm_tert_tdm_rx_0_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_1 Bit Format", msm_snd_enum[17],
+ msm_tert_tdm_rx_1_bit_format_get,
+ msm_tert_tdm_rx_1_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_2 Bit Format", msm_snd_enum[17],
+ msm_tert_tdm_rx_2_bit_format_get,
+ msm_tert_tdm_rx_2_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_3 Bit Format", msm_snd_enum[17],
+ msm_tert_tdm_rx_3_bit_format_get,
+ msm_tert_tdm_rx_3_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Bit Format", msm_snd_enum[17],
+ msm_tert_tdm_tx_0_bit_format_get,
+ msm_tert_tdm_tx_0_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_1 Bit Format", msm_snd_enum[17],
+ msm_tert_tdm_tx_1_bit_format_get,
+ msm_tert_tdm_tx_1_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_2 Bit Format", msm_snd_enum[17],
+ msm_tert_tdm_tx_2_bit_format_get,
+ msm_tert_tdm_tx_2_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_3 Bit Format", msm_snd_enum[17],
+ msm_tert_tdm_tx_3_bit_format_get,
+ msm_tert_tdm_tx_3_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Bit Format", msm_snd_enum[17],
+ msm_quat_tdm_rx_0_bit_format_get,
+ msm_quat_tdm_rx_0_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_1 Bit Format", msm_snd_enum[17],
+ msm_quat_tdm_rx_1_bit_format_get,
+ msm_quat_tdm_rx_1_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_2 Bit Format", msm_snd_enum[17],
+ msm_quat_tdm_rx_2_bit_format_get,
+ msm_quat_tdm_rx_2_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_3 Bit Format", msm_snd_enum[17],
+ msm_quat_tdm_rx_3_bit_format_get,
+ msm_quat_tdm_rx_3_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Bit Format", msm_snd_enum[17],
+ msm_quat_tdm_tx_0_bit_format_get,
+ msm_quat_tdm_tx_0_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_1 Bit Format", msm_snd_enum[17],
+ msm_quat_tdm_tx_1_bit_format_get,
+ msm_quat_tdm_tx_1_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_2 Bit Format", msm_snd_enum[17],
+ msm_quat_tdm_tx_2_bit_format_get,
+ msm_quat_tdm_tx_2_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_3 Bit Format", msm_snd_enum[17],
+ msm_quat_tdm_tx_3_bit_format_get,
+ msm_quat_tdm_tx_3_bit_format_put),
+ SOC_ENUM_EXT("PRI_TDM SampleRate", msm_snd_enum[18],
+ msm_pri_tdm_rate_get, msm_pri_tdm_rate_put),
+ SOC_ENUM_EXT("PRI_TDM Slot Number", msm_snd_enum[19],
+ msm_pri_tdm_slot_num_get, msm_pri_tdm_slot_num_put),
+ SOC_ENUM_EXT("PRI_TDM Slot Width", msm_snd_enum[20],
+ msm_pri_tdm_slot_width_get, msm_pri_tdm_slot_width_put),
+ SOC_ENUM_EXT("SEC_TDM SampleRate", msm_snd_enum[18],
+ msm_sec_tdm_rate_get, msm_sec_tdm_rate_put),
+ SOC_ENUM_EXT("SEC_TDM Slot Number", msm_snd_enum[19],
+ msm_sec_tdm_slot_num_get, msm_sec_tdm_slot_num_put),
+ SOC_ENUM_EXT("SEC_TDM Slot Width", msm_snd_enum[20],
+ msm_sec_tdm_slot_width_get, msm_sec_tdm_slot_width_put),
+ SOC_ENUM_EXT("TERT_TDM SampleRate", msm_snd_enum[18],
+ msm_tert_tdm_rate_get, msm_tert_tdm_rate_put),
+ SOC_ENUM_EXT("TERT_TDM Slot Number", msm_snd_enum[19],
+ msm_tert_tdm_slot_num_get, msm_tert_tdm_slot_num_put),
+ SOC_ENUM_EXT("TERT_TDM Slot Width", msm_snd_enum[20],
+ msm_tert_tdm_slot_width_get,
+ msm_tert_tdm_slot_width_put),
+ SOC_ENUM_EXT("QUAT_TDM SampleRate", msm_snd_enum[18],
+ msm_quat_tdm_rate_get, msm_quat_tdm_rate_put),
+ SOC_ENUM_EXT("QUAT_TDM Slot Number", msm_snd_enum[19],
+ msm_quat_tdm_slot_num_get, msm_quat_tdm_slot_num_put),
+ SOC_ENUM_EXT("QUAT_TDM Slot Width", msm_snd_enum[20],
+ msm_quat_tdm_slot_width_get,
+ msm_quat_tdm_slot_width_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_0 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_0, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_1 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_1, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_2 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_2, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_3 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_3, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_4 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_4, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_5 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_5, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_6 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_6, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_7 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_RX_7, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_0 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_0, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_1 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_1, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_2 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_2, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_3 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_3, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_4 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_4, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_5 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_5, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_6 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_6, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_7 Slot Mapping", SND_SOC_NOPM,
+ PRIMARY_TDM_TX_7, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_0 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_0, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_1 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_1, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_2 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_2, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_3 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_3, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_4 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_4, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_5 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_5, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_6 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_6, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_7 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_RX_7, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_0 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_0, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_1 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_1, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_2 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_2, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_3 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_3, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_4 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_4, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_5 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_5, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_6 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_6, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_7 Slot Mapping", SND_SOC_NOPM,
+ SECONDARY_TDM_TX_7, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_0 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_0, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_1 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_1, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_2 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_2, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_3 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_3, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_4 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_4, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_5 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_5, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_6 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_6, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_7 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_RX_7, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_0 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_0, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_1 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_1, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_2 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_2, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_3 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_3, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_4 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_4, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_5 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_5, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_6 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_6, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_7 Slot Mapping", SND_SOC_NOPM,
+ TERTIARY_TDM_TX_7, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_0 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_0, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_1 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_1, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_2 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_2, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_3 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_3, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_4 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_4, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_5 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_5, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_6 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_6, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_7 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_RX_7, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_0 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_0, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_1 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_1, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_2 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_2, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_3 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_3, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_4 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_4, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_5 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_5, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_6 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_6, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_7 Slot Mapping", SND_SOC_NOPM,
+ QUATERNARY_TDM_TX_7, 0xFFFF,
+ 0, 8, msm_tdm_slot_mapping_get,
+ msm_tdm_slot_mapping_put),
+};
+
+static bool msm8996_swap_gnd_mic(struct snd_soc_codec *codec)
+{
+ struct snd_soc_card *card = codec->component.card;
+ struct msm8996_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+ int value = gpio_get_value_cansleep(pdata->us_euro_gpio);
+
+ pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value);
+ gpio_set_value_cansleep(pdata->us_euro_gpio, !value);
+ return true;
+}
+
+static int msm_afe_set_config(struct snd_soc_codec *codec)
+{
+ int rc;
+ void *config_data = NULL;
+
+ pr_debug("%s: enter\n", __func__);
+
+ if (!msm8996_codec_fn.get_afe_config_fn) {
+ dev_err(codec->dev, "%s: codec get afe config not init'ed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+ AFE_CDC_REGISTERS_CONFIG);
+ if (config_data) {
+ rc = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0);
+ if (rc) {
+ pr_err("%s: Failed to set codec registers config %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+ AFE_CDC_REGISTER_PAGE_CONFIG);
+ if (config_data) {
+ rc = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data,
+ 0);
+ if (rc)
+ pr_err("%s: Failed to set cdc register page config\n",
+ __func__);
+ }
+
+ config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+ AFE_SLIMBUS_SLAVE_CONFIG);
+ if (config_data) {
+ rc = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0);
+ if (rc) {
+ pr_err("%s: Failed to set slimbus slave config %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static void msm_afe_clear_config(void)
+{
+ afe_clear_config(AFE_CDC_REGISTERS_CONFIG);
+ afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG);
+}
+
+static int msm8996_adsp_state_callback(struct notifier_block *nb,
+ unsigned long value, void *priv)
+{
+ if (value == SUBSYS_BEFORE_SHUTDOWN) {
+ pr_debug("%s: ADSP is about to shutdown. Clearing AFE config\n",
+ __func__);
+ msm_afe_clear_config();
+ } else if (value == SUBSYS_AFTER_POWERUP) {
+ pr_debug("%s: ADSP is up\n", __func__);
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block adsp_state_notifier_block = {
+ .notifier_call = msm8996_adsp_state_callback,
+ .priority = -INT_MAX,
+};
+
+static int msm8996_wcd93xx_codec_up(struct snd_soc_codec *codec)
+{
+ int err;
+ unsigned long timeout;
+ int adsp_ready = 0;
+
+ timeout = jiffies +
+ msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+
+ do {
+ if (!q6core_is_adsp_ready()) {
+ pr_err_ratelimited("%s: ADSP Audio isn't ready\n",
+ __func__);
+ /*
+ * ADSP will be coming up after subsystem restart and
+ * it might not be fully up when the control reaches
+ * here. So, wait for 50msec before checking ADSP state
+ */
+ msleep(50);
+ } else {
+ pr_debug("%s: ADSP Audio is ready\n", __func__);
+ adsp_ready = 1;
+ break;
+ }
+ } while (time_after(timeout, jiffies));
+
+ if (!adsp_ready) {
+ pr_err("%s: timed out waiting for ADSP Audio\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ err = msm_afe_set_config(codec);
+ if (err)
+ pr_err("%s: Failed to set AFE config. err %d\n",
+ __func__, err);
+ return err;
+}
+
+static int msm8996_tasha_codec_event_cb(struct snd_soc_codec *codec,
+ enum wcd9335_codec_event codec_event)
+{
+ switch (codec_event) {
+ case WCD9335_CODEC_EVENT_CODEC_UP:
+ return msm8996_wcd93xx_codec_up(codec);
+ default:
+ pr_err("%s: UnSupported codec event %d\n",
+ __func__, codec_event);
+ return -EINVAL;
+ }
+}
+
+static int msm8996_config_hph_en0_gpio(struct snd_soc_codec *codec, bool high)
+{
+ struct snd_soc_card *card = codec->component.card;
+ struct msm8996_asoc_mach_data *pdata;
+ int val;
+
+ if (!card)
+ return 0;
+
+ pdata = snd_soc_card_get_drvdata(card);
+ if (!pdata || !gpio_is_valid(pdata->hph_en0_gpio))
+ return 0;
+
+ val = gpio_get_value_cansleep(pdata->hph_en0_gpio);
+ if ((!!val) == high)
+ return 0;
+
+ gpio_direction_output(pdata->hph_en0_gpio, (int)high);
+
+ return 1;
+}
+
+static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int err;
+ void *config_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux;
+ void *mbhc_calibration;
+ struct snd_card *card;
+ struct snd_info_entry *entry;
+ struct msm8996_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(rtd->card);
+
+ /* Codec SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156};
+ unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143};
+
+ pr_info("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+ rtd->pmdown_time = 0;
+
+ err = snd_soc_add_codec_controls(codec, msm_snd_controls,
+ ARRAY_SIZE(msm_snd_controls));
+ if (err < 0) {
+ pr_err("%s: add_codec_controls failed, err %d\n",
+ __func__, err);
+ return err;
+ }
+
+ err = msm8996_liquid_init_docking();
+ if (err) {
+ pr_err("%s: 8996 init Docking stat IRQ failed (%d)\n",
+ __func__, err);
+ return err;
+ }
+
+ err = msm8996_ext_us_amp_init();
+ if (err) {
+ pr_err("%s: 8996 US Emitter GPIO init failed (%d)\n",
+ __func__, err);
+ return err;
+ }
+
+ snd_soc_dapm_new_controls(dapm, msm8996_dapm_widgets,
+ ARRAY_SIZE(msm8996_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, wcd9335_audio_paths,
+ ARRAY_SIZE(wcd9335_audio_paths));
+ snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp");
+
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_1 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_3 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_2 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_4 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "ultrasound amp");
+ snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic7");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic8");
+ snd_soc_dapm_ignore_suspend(dapm, "MADINPUT");
+ snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT");
+ snd_soc_dapm_ignore_suspend(dapm, "EAR");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC EAR");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC1");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC2");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC3");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC4");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC5");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC6");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC1");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC2");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC3");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC4");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC5");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC0");
+ snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "HPHL");
+ snd_soc_dapm_ignore_suspend(dapm, "HPHR");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI");
+ snd_soc_dapm_ignore_suspend(dapm, "VIINPUT");
+
+ snd_soc_dapm_sync(dapm);
+
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+ msm8996_codec_fn.get_afe_config_fn = tasha_get_afe_config;
+ msm8996_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit;
+
+ err = msm_afe_set_config(codec);
+ if (err) {
+ pr_err("%s: Failed to set AFE config %d\n", __func__, err);
+ goto out;
+ }
+
+ config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+ AFE_AANC_VERSION);
+ if (config_data) {
+ err = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set aanc version %d\n",
+ __func__, err);
+ goto out;
+ }
+ }
+ config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+ AFE_CDC_CLIP_REGISTERS_CONFIG);
+ if (config_data) {
+ err = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG,
+ config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set clip registers %d\n",
+ __func__, err);
+ goto out;
+ }
+ }
+ config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+ AFE_CLIP_BANK_SEL);
+ if (config_data) {
+ err = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set AFE bank selection %d\n",
+ __func__, err);
+ goto out;
+ }
+ }
+ /* Start mbhc */
+ tasha_mbhc_zdet_gpio_ctrl(msm8996_config_hph_en0_gpio, rtd->codec);
+ mbhc_calibration = def_tasha_mbhc_cal();
+ if (mbhc_calibration) {
+ wcd_mbhc_cfg.calibration = mbhc_calibration;
+ err = tasha_mbhc_hs_detect(codec, &wcd_mbhc_cfg);
+ if (err) {
+ pr_err("%s: mbhc hs detect failed, err:%d\n",
+ __func__, err);
+ goto out;
+ }
+ } else {
+ pr_err("%s: mbhc_cfg calibration is NULL\n", __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+ adsp_state_notifier = subsys_notif_register_notifier("adsp",
+ &adsp_state_notifier_block);
+ if (!adsp_state_notifier) {
+ pr_err("%s: Failed to register adsp state notifier\n",
+ __func__);
+ err = -EFAULT;
+ msm8996_codec_fn.mbhc_hs_detect_exit(codec);
+ goto out;
+ }
+
+ tasha_event_register(msm8996_tasha_codec_event_cb, rtd->codec);
+
+ /*
+ * Send speaker configuration only for WSA8810.
+ * Defalut configuration is for WSA8815.
+ */
+ if (rtd_aux && rtd_aux->component)
+ if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
+ !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
+ tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1);
+ tasha_set_spkr_gain_offset(rtd->codec,
+ RX_GAIN_OFFSET_M1P5_DB);
+ }
+ codec_reg_done = true;
+
+ card = rtd->card->snd_card;
+ entry = snd_register_module_info(card->module, "codecs",
+ card->proc_root);
+ if (!entry) {
+ pr_debug("%s: Cannot create codecs module entry\n",
+ __func__);
+ err = 0;
+ goto out;
+ }
+ pdata->codec_root = entry;
+ tasha_codec_info_create_codec_entry(pdata->codec_root, codec);
+
+ return 0;
+out:
+ return err;
+}
+
+static void *def_tasha_mbhc_cal(void)
+{
+ void *tasha_wcd_cal;
+ struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+ u16 *btn_high;
+
+ tasha_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS,
+ WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL);
+ if (!tasha_wcd_cal)
+ return NULL;
+
+#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tasha_wcd_cal)->X) = (Y))
+ S(v_hs_max, 1500);
+#undef S
+#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal)->X) = (Y))
+ S(num_btn, WCD_MBHC_DEF_BUTTONS);
+#undef S
+
+ btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal);
+ btn_high = ((void *)&btn_cfg->_v_btn_low) +
+ (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn);
+
+ btn_high[0] = 75;
+ btn_high[1] = 150;
+ btn_high[2] = 237;
+ btn_high[3] = 500;
+ btn_high[4] = 500;
+ btn_high[5] = 500;
+ btn_high[6] = 500;
+ btn_high[7] = 500;
+
+ return tasha_wcd_cal;
+}
+
+static int msm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ int ret = 0;
+ u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+ u32 user_set_tx_ch = 0;
+ u32 rx_ch_count;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_5_RX) {
+ pr_debug("%s: rx_5_ch=%d\n", __func__,
+ msm_slim_5_rx_ch);
+ rx_ch_count = msm_slim_5_rx_ch;
+ } else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_6_RX) {
+ pr_debug("%s: rx_6_ch=%d\n", __func__,
+ msm_slim_6_rx_ch);
+ rx_ch_count = msm_slim_6_rx_ch;
+ } else {
+ pr_debug("%s: rx_0_ch=%d\n", __func__,
+ msm_slim_0_rx_ch);
+ rx_ch_count = msm_slim_0_rx_ch;
+ }
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ rx_ch_count, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+
+ pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
+ codec_dai->name, codec_dai->id, user_set_tx_ch);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map\n, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ /* For <codec>_tx1 case */
+ if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_0_TX)
+ user_set_tx_ch = msm_slim_0_tx_ch;
+ /* For <codec>_tx3 case */
+ else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_1_TX)
+ user_set_tx_ch = msm_slim_1_tx_ch;
+ else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_3_TX)
+ /* DAI 5 is used for external EC reference from codec.
+ * Since Rx is fed as reference for EC, the config of
+ * this DAI is based on that of the Rx path.
+ */
+ user_set_tx_ch = msm_slim_0_rx_ch;
+ else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_4_TX)
+ user_set_tx_ch = msm_vi_feed_tx_ch;
+ else
+ user_set_tx_ch = tx_ch_cnt;
+
+ pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), be_id (%d)\n",
+ __func__, msm_slim_0_tx_ch, user_set_tx_ch,
+ tx_ch_cnt, dai_link->be_id);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ user_set_tx_ch,
+ tx_ch, 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+end:
+ return ret;
+}
+
+static int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ int ret = 0;
+ u32 tx_ch[SLIM_MAX_TX_PORTS];
+ u32 tx_ch_cnt = 0;
+ u32 user_set_tx_ch = 0;
+
+ if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) {
+ pr_err("%s: Invalid stream type %d\n",
+ __func__, substream->stream);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ pr_debug("%s: %s_tx_dai_id_%d\n", __func__,
+ codec_dai->name, codec_dai->id);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, NULL, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map\n, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ user_set_tx_ch = tx_ch_cnt;
+
+ pr_debug("%s: tx_ch_cnt(%d) be_id %d\n",
+ __func__, tx_ch_cnt, dai_link->be_id);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ user_set_tx_ch, tx_ch, 0, NULL);
+ if (ret < 0)
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+end:
+ return ret;
+}
+
+static struct snd_soc_ops msm8996_be_ops = {
+ .hw_params = msm_snd_hw_params,
+};
+
+static struct snd_soc_ops msm8996_cpe_ops = {
+ .hw_params = msm_snd_cpe_hw_params,
+};
+
+static int msm8996_slimbus_2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
+ unsigned int num_tx_ch = 0;
+ unsigned int num_rx_ch = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ num_rx_ch = params_channels(params);
+ pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__,
+ codec_dai->name, codec_dai->id, num_rx_ch);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ num_rx_ch, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+ num_tx_ch = params_channels(params);
+ pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__,
+ codec_dai->name, codec_dai->id, num_tx_ch);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ num_tx_ch, tx_ch, 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+end:
+ return ret;
+}
+
+static struct snd_soc_ops msm8996_slimbus_2_be_ops = {
+ .hw_params = msm8996_slimbus_2_hw_params,
+};
+
+static int msm8996_get_ll_qos_val(struct snd_pcm_runtime *runtime)
+{
+ int usecs;
+
+ /* take 10% of period time as the deadline */
+ usecs = (100000 / runtime->rate) * runtime->period_size;
+ usecs += ((100000 % runtime->rate) * runtime->period_size) /
+ runtime->rate;
+
+ return usecs;
+}
+
+static int msm8996_mm5_prepare(struct snd_pcm_substream *substream)
+{
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
+ pm_qos_add_request(&substream->latency_pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY,
+ msm8996_get_ll_qos_val(substream->runtime));
+ return 0;
+}
+
+static struct snd_soc_ops msm8996_mm5_ops = {
+ .prepare = msm8996_mm5_prepare,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm8996_common_dai_links[] = {
+ /* FrontEnd DAI Links */
+ {
+ .name = "MSM8996 Media1",
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+ {
+ .name = "MSM8996 Media2",
+ .stream_name = "MultiMedia2",
+ .cpu_dai_name = "MultiMedia2",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+ },
+ {
+ .name = "VoiceMMode1",
+ .stream_name = "VoiceMMode1",
+ .cpu_dai_name = "VoiceMMode1",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE1,
+ },
+ {
+ .name = "MSM VoIP",
+ .stream_name = "VoIP",
+ .cpu_dai_name = "VoIP",
+ .platform_name = "msm-voip-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_VOIP,
+ },
+ {
+ .name = "MSM8996 ULL",
+ .stream_name = "MultiMedia3",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-dsp.2",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+ /* Hostless PCM purpose */
+ {
+ .name = "SLIMBUS_0 Hostless",
+ .stream_name = "SLIMBUS_0 Hostless",
+ .cpu_dai_name = "SLIMBUS0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary MI2S TX_Hostless",
+ .stream_name = "Tertiary MI2S_TX Hostless Capture",
+ .cpu_dai_name = "TERT_MI2S_TX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "MSM AFE-PCM RX",
+ .stream_name = "AFE-PROXY RX",
+ .cpu_dai_name = "msm-dai-q6-dev.241",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = "MSM AFE-PCM TX",
+ .stream_name = "AFE-PROXY TX",
+ .cpu_dai_name = "msm-dai-q6-dev.240",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "MSM8996 Compress1",
+ .stream_name = "Compress1",
+ .cpu_dai_name = "MultiMedia4",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+ },
+ {
+ .name = "AUXPCM Hostless",
+ .stream_name = "AUXPCM Hostless",
+ .cpu_dai_name = "AUXPCM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_1 Hostless",
+ .stream_name = "SLIMBUS_1 Hostless",
+ .cpu_dai_name = "SLIMBUS1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_3 Hostless",
+ .stream_name = "SLIMBUS_3 Hostless",
+ .cpu_dai_name = "SLIMBUS3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_4 Hostless",
+ .stream_name = "SLIMBUS_4 Hostless",
+ .cpu_dai_name = "SLIMBUS4_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "VoLTE",
+ .stream_name = "VoLTE",
+ .cpu_dai_name = "VoLTE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOLTE,
+ },
+ {
+ .name = "MSM8996 LowLatency",
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+ .ops = &msm8996_mm5_ops,
+ },
+ {
+ .name = "Listen 1 Audio Service",
+ .stream_name = "Listen 1 Audio Service",
+ .cpu_dai_name = "LSM1",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM1,
+ },
+ /* Multiple Tunnel instances */
+ {
+ .name = "MSM8996 Compress2",
+ .stream_name = "Compress2",
+ .cpu_dai_name = "MultiMedia7",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+ },
+ {
+ .name = "MSM8996 Compress3",
+ .stream_name = "Compress3",
+ .cpu_dai_name = "MultiMedia10",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10,
+ },
+ {
+ .name = "MSM8996 ULL NOIRQ",
+ .stream_name = "MM_NOIRQ",
+ .cpu_dai_name = "MultiMedia8",
+ .platform_name = "msm-pcm-dsp-noirq",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+ },
+ {
+ .name = "QCHAT",
+ .stream_name = "QCHAT",
+ .cpu_dai_name = "QCHAT",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_QCHAT,
+ },
+ /* HDMI Hostless */
+ {
+ .name = "HDMI_RX_HOSTLESS",
+ .stream_name = "HDMI_RX_HOSTLESS",
+ .cpu_dai_name = "HDMI_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "VoiceMMode2",
+ .stream_name = "VoiceMMode2",
+ .cpu_dai_name = "VoiceMMode2",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE2,
+ },
+ {
+ .name = "INT_HFP_BT Hostless",
+ .stream_name = "INT_HFP_BT Hostless",
+ .cpu_dai_name = "INT_HFP_BT_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "MSM8996 HFP TX",
+ .stream_name = "MultiMedia6",
+ .cpu_dai_name = "MultiMedia6",
+ .platform_name = "msm-pcm-loopback",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,
+ },
+ /* LSM FE */
+ {
+ .name = "Listen 2 Audio Service",
+ .stream_name = "Listen 2 Audio Service",
+ .cpu_dai_name = "LSM2",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM2,
+ },
+ {
+ .name = "Listen 3 Audio Service",
+ .stream_name = "Listen 3 Audio Service",
+ .cpu_dai_name = "LSM3",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM3,
+ },
+ {
+ .name = "Listen 4 Audio Service",
+ .stream_name = "Listen 4 Audio Service",
+ .cpu_dai_name = "LSM4",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM4,
+ },
+ {
+ .name = "Listen 5 Audio Service",
+ .stream_name = "Listen 5 Audio Service",
+ .cpu_dai_name = "LSM5",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM5,
+ },
+ {
+ .name = "Listen 6 Audio Service",
+ .stream_name = "Listen 6 Audio Service",
+ .cpu_dai_name = "LSM6",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM6,
+ },
+ {
+ .name = "Listen 7 Audio Service",
+ .stream_name = "Listen 7 Audio Service",
+ .cpu_dai_name = "LSM7",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM7,
+ },
+ {
+ .name = "Listen 8 Audio Service",
+ .stream_name = "Listen 8 Audio Service",
+ .cpu_dai_name = "LSM8",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM8,
+ },
+ {
+ .name = "MSM8996 Media9",
+ .stream_name = "MultiMedia9",
+ .cpu_dai_name = "MultiMedia9",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+ },
+ {
+ .name = "VoWLAN",
+ .stream_name = "VoWLAN",
+ .cpu_dai_name = "VoWLAN",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOWLAN,
+ },
+ {
+ .name = "MSM8996 Compress4",
+ .stream_name = "Compress4",
+ .cpu_dai_name = "MultiMedia11",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11,
+ },
+ {
+ .name = "MSM8996 Compress5",
+ .stream_name = "Compress5",
+ .cpu_dai_name = "MultiMedia12",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12,
+ },
+ {
+ .name = "MSM8996 Compress6",
+ .stream_name = "Compress6",
+ .cpu_dai_name = "MultiMedia13",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13,
+ },
+ {
+ .name = "MSM8996 Compress7",
+ .stream_name = "Compress7",
+ .cpu_dai_name = "MultiMedia14",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+ },
+ {
+ .name = "MSM8996 Compress8",
+ .stream_name = "Compress8",
+ .cpu_dai_name = "MultiMedia15",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
+ },
+ {
+ .name = "MSM8996 ULL NOIRQ_2",
+ .stream_name = "MM_NOIRQ_2",
+ .cpu_dai_name = "MultiMedia16",
+ .platform_name = "msm-pcm-dsp-noirq",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16,
+ },
+ {
+ .name = "Circuit-Switch Voice",
+ .stream_name = "CS-Voice",
+ .cpu_dai_name = "CS-VOICE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_CS_VOICE,
+ },
+ {
+ .name = "Voice2",
+ .stream_name = "Voice2",
+ .cpu_dai_name = "Voice2",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICE2,
+ },
+};
+static struct snd_soc_dai_link msm8996_tdm_fe_dai_links[] = {
+ {
+ .name = "Primary TDM RX 0 Hostless",
+ .stream_name = "Primary TDM RX 0 Hostless",
+ .cpu_dai_name = "PRI_TDM_RX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM RX 1 Hostless",
+ .stream_name = "Primary TDM RX 1 Hostless",
+ .cpu_dai_name = "PRI_TDM_RX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM RX 2 Hostless",
+ .stream_name = "Primary TDM RX 2 Hostless",
+ .cpu_dai_name = "PRI_TDM_RX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM RX 3 Hostless",
+ .stream_name = "Primary TDM RX 3 Hostless",
+ .cpu_dai_name = "PRI_TDM_RX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM TX 0 Hostless",
+ .stream_name = "Primary TDM TX 0 Hostless",
+ .cpu_dai_name = "PRI_TDM_TX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM TX 1 Hostless",
+ .stream_name = "Primary TDM TX 1 Hostless",
+ .cpu_dai_name = "PRI_TDM_TX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM TX 2 Hostless",
+ .stream_name = "Primary TDM TX 2 Hostless",
+ .cpu_dai_name = "PRI_TDM_TX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Primary TDM TX 3 Hostless",
+ .stream_name = "Primary TDM TX 3 Hostless",
+ .cpu_dai_name = "PRI_TDM_TX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Secondary TDM RX 0 Hostless",
+ .stream_name = "Secondary TDM RX 0 Hostless",
+ .cpu_dai_name = "SEC_TDM_RX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Secondary TDM RX 1 Hostless",
+ .stream_name = "Secondary TDM RX 1 Hostless",
+ .cpu_dai_name = "SEC_TDM_RX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Secondary TDM RX 2 Hostless",
+ .stream_name = "Secondary TDM RX 2 Hostless",
+ .cpu_dai_name = "SEC_TDM_RX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Secondary TDM RX 3 Hostless",
+ .stream_name = "Secondary TDM RX 3 Hostless",
+ .cpu_dai_name = "SEC_TDM_RX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Secondary TDM TX 0 Hostless",
+ .stream_name = "Secondary TDM TX 0 Hostless",
+ .cpu_dai_name = "SEC_TDM_TX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Secondary TDM TX 1 Hostless",
+ .stream_name = "Secondary TDM TX 1 Hostless",
+ .cpu_dai_name = "SEC_TDM_TX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Secondary TDM TX 2 Hostless",
+ .stream_name = "Secondary TDM TX 2 Hostless",
+ .cpu_dai_name = "SEC_TDM_TX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Secondary TDM TX 3 Hostless",
+ .stream_name = "Secondary TDM TX 3 Hostless",
+ .cpu_dai_name = "SEC_TDM_TX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM RX 0 Hostless",
+ .stream_name = "Tertiary TDM RX 0 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM RX 1 Hostless",
+ .stream_name = "Tertiary TDM RX 1 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM RX 2 Hostless",
+ .stream_name = "Tertiary TDM RX 2 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM RX 3 Hostless",
+ .stream_name = "Tertiary TDM RX 3 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 0 Hostless",
+ .stream_name = "Tertiary TDM TX 0 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 1 Hostless",
+ .stream_name = "Tertiary TDM TX 1 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 2 Hostless",
+ .stream_name = "Tertiary TDM TX 2 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 3 Hostless",
+ .stream_name = "Tertiary TDM TX 3 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 0 Hostless",
+ .stream_name = "Quaternary TDM RX 0 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 1 Hostless",
+ .stream_name = "Quaternary TDM RX 1 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 2 Hostless",
+ .stream_name = "Quaternary TDM RX 2 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 3 Hostless",
+ .stream_name = "Quaternary TDM RX 3 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 0 Hostless",
+ .stream_name = "Quaternary TDM TX 0 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 1 Hostless",
+ .stream_name = "Quaternary TDM TX 1 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 2 Hostless",
+ .stream_name = "Quaternary TDM TX 2 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 3 Hostless",
+ .stream_name = "Quaternary TDM TX 3 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+
+};
+static struct snd_soc_dai_link msm8996_tasha_fe_dai_links[] = {
+ {
+ .name = LPASS_BE_SLIMBUS_4_TX,
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_vifeedback",
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ .be_hw_params_fixup = msm_slim_4_tx_be_hw_params_fixup,
+ .ops = &msm8996_be_ops,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ },
+ /* Ultrasound RX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Playback",
+ .stream_name = "SLIMBUS_2 Hostless Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16388",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm8996_slimbus_2_be_ops,
+ },
+ /* Ultrasound TX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Capture",
+ .stream_name = "SLIMBUS_2 Hostless Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16389",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm8996_slimbus_2_be_ops,
+ },
+ /* CPE LSM direct dai-link */
+ {
+ .name = "CPE Listen service",
+ .stream_name = "CPE Listen Audio Service",
+ .cpu_dai_name = "msm-dai-slim",
+ .platform_name = "msm-cpe-lsm",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "tasha_mad1",
+ .codec_name = "tasha_codec",
+ .ops = &msm8996_cpe_ops,
+ },
+ /* slimbus rx 6 hostless */
+ {
+ .name = "SLIMBUS_6 Hostless Playback",
+ .stream_name = "SLIMBUS_6 Hostless",
+ .cpu_dai_name = "SLIMBUS6_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ /* CPE LSM EC PP direct dai-link */
+ {
+ .name = "CPE Listen service ECPP",
+ .stream_name = "CPE Listen Audio Service ECPP",
+ .cpu_dai_name = "CPE_LSM_NOHOST",
+ .platform_name = "msm-cpe-lsm.3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "tasha_cpe",
+ .codec_name = "tasha_codec",
+ },
+};
+
+static struct snd_soc_dai_link msm8996_common_be_dai_links[] = {
+ /* Backend AFE DAI Links */
+ {
+ .name = LPASS_BE_AFE_PCM_RX,
+ .stream_name = "AFE Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.224",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+ .be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_AFE_PCM_TX,
+ .stream_name = "AFE Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.225",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+ .be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Primary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Uplink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_TX,
+ .stream_name = "Voice Uplink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32772",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Downlink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_RX,
+ .stream_name = "Voice Downlink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32771",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE_PLAYBACK_TX,
+ .stream_name = "Voice Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32773",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music 2 BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE2_PLAYBACK_TX,
+ .stream_name = "Voice2 Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32770",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_TX,
+ .stream_name = "Tertiary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ .be_hw_params_fixup = msm_tx_be_hw_params_fixup,
+ .ops = &msm8996_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_USB_AUDIO_RX,
+ .stream_name = "USB Audio Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.28672",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_USB_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_USB_AUDIO_TX,
+ .stream_name = "USB Audio Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.28673",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_USB_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ }
+};
+
+static struct snd_soc_dai_link msm8996_tasha_be_dai_links[] = {
+ /* Backend DAI Links */
+ {
+ .name = LPASS_BE_SLIMBUS_0_RX,
+ .stream_name = "Slimbus Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16384",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ .init = &msm_audrx_init,
+ .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm8996_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_0_TX,
+ .stream_name = "Slimbus Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16385",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ops = &msm8996_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_RX,
+ .stream_name = "Slimbus1 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16386",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+ .ops = &msm8996_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_TX,
+ .stream_name = "Slimbus1 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16387",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx3",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ .be_hw_params_fixup = msm_slim_1_tx_be_hw_params_fixup,
+ .ops = &msm8996_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_RX,
+ .stream_name = "Slimbus3 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16390",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+ .ops = &msm8996_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_TX,
+ .stream_name = "Slimbus3 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16391",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+ .ops = &msm8996_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_4_RX,
+ .stream_name = "Slimbus4 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16392",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+ .ops = &msm8996_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_5_RX,
+ .stream_name = "Slimbus5 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16394",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx3",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ .be_hw_params_fixup = msm_slim_5_rx_be_hw_params_fixup,
+ .ops = &msm8996_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ /* MAD BE */
+ {
+ .name = LPASS_BE_SLIMBUS_5_TX,
+ .stream_name = "Slimbus5 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16395",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mad1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ .be_hw_params_fixup = msm_slim_5_tx_be_hw_params_fixup,
+ .ops = &msm8996_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_6_RX,
+ .stream_name = "Slimbus6 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16396",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx4",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ .be_hw_params_fixup = msm_slim_6_rx_be_hw_params_fixup,
+ .ops = &msm8996_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ }
+};
+static struct snd_soc_dai_link msm8996_tdm_be_dai_links[] = {
+ {
+ .name = LPASS_BE_SEC_TDM_RX_0,
+ .stream_name = "Secondary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36880",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_RX_1,
+ .stream_name = "Secondary TDM1 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36882",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_RX_2,
+ .stream_name = "Secondary TDM2 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36884",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_RX_3,
+ .stream_name = "Secondary TDM3 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36886",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_0,
+ .stream_name = "Secondary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36881",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_1,
+ .stream_name = "Secondary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36883",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_2,
+ .stream_name = "Secondary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36885",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_3,
+ .stream_name = "Secondary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36887",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_0,
+ .stream_name = "Tertiary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36896",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_1,
+ .stream_name = "Tertiary TDM1 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36898",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_2,
+ .stream_name = "Tertiary TDM2 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36900",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_3,
+ .stream_name = "Tertiary TDM3 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36902",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_0,
+ .stream_name = "Tertiary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36897",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_1,
+ .stream_name = "Tertiary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36899",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_2,
+ .stream_name = "Tertiary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36901",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_3,
+ .stream_name = "Tertiary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36903",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_0,
+ .stream_name = "Quaternary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36912",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_1,
+ .stream_name = "Quaternary TDM1 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36914",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_2,
+ .stream_name = "Quaternary TDM2 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36916",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_3,
+ .stream_name = "Quaternary TDM3 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36918",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_0,
+ .stream_name = "Quaternary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36913",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_1,
+ .stream_name = "Quaternary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36915",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_2,
+ .stream_name = "Quaternary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36917",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_3,
+ .stream_name = "Quaternary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36919",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_RX_0,
+ .stream_name = "Primary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36864",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_RX_1,
+ .stream_name = "Primary TDM1 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36866",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_RX_2,
+ .stream_name = "Primary TDM2 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36868",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_RX_3,
+ .stream_name = "Primary TDM3 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36870",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_0,
+ .stream_name = "Primary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36865",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_1,
+ .stream_name = "Primary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36867",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_2,
+ .stream_name = "Primary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36869",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_3,
+ .stream_name = "Primary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36871",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8996_tdm_be_ops,
+ .ignore_suspend = 1,
+ }
+
+};
+
+static struct snd_soc_dai_link msm8996_hdmi_dai_link[] = {
+ /* HDMI BACK END DAI Link */
+ {
+ .name = LPASS_BE_HDMI,
+ .stream_name = "HDMI Playback",
+ .cpu_dai_name = "msm-dai-q6-hdmi.8",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-ext-disp-audio-codec-rx",
+ .codec_dai_name = "msm_hdmi_audio_codec_rx_dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_HDMI_RX,
+ .be_hw_params_fixup = msm8996_hdmi_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm8996_tasha_dai_links[
+ ARRAY_SIZE(msm8996_common_dai_links) +
+ ARRAY_SIZE(msm8996_tasha_fe_dai_links) +
+ ARRAY_SIZE(msm8996_tdm_fe_dai_links) +
+ ARRAY_SIZE(msm8996_common_be_dai_links) +
+ ARRAY_SIZE(msm8996_tasha_be_dai_links) +
+ ARRAY_SIZE(msm8996_tdm_be_dai_links) +
+ ARRAY_SIZE(msm8996_hdmi_dai_link)];
+
+static int msm8996_wsa881x_init(struct snd_soc_component *component)
+{
+ u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106};
+ u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107};
+ unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200};
+ unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3};
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+ struct msm8996_asoc_mach_data *pdata;
+ struct snd_soc_dapm_context *dapm;
+
+ if (!codec) {
+ pr_err("%s codec is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dapm = snd_soc_codec_get_dapm(codec);
+
+ if (!strcmp(component->name_prefix, "SpkrLeft")) {
+ dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n",
+ __func__, codec->component.name);
+ wsa881x_set_channel_map(codec, &spkleft_ports[0],
+ WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+ &ch_rate[0]);
+ if (dapm->component) {
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN");
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR");
+ }
+ } else if (!strcmp(component->name_prefix, "SpkrRight")) {
+ dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n",
+ __func__, codec->component.name);
+ wsa881x_set_channel_map(codec, &spkright_ports[0],
+ WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+ &ch_rate[0]);
+ if (dapm->component) {
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN");
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR");
+ }
+ } else {
+ dev_err(codec->dev, "%s: wrong codec name %s\n", __func__,
+ codec->component.name);
+ return -EINVAL;
+ }
+ pdata = snd_soc_card_get_drvdata(component->card);
+ if (pdata && pdata->codec_root)
+ wsa881x_codec_info_create_codec_entry(pdata->codec_root,
+ codec);
+
+ return 0;
+}
+
+struct snd_soc_card snd_soc_card_tasha_msm8996 = {
+ .name = "msm8996-tasha-snd-card",
+};
+
+static int msm8996_populate_dai_link_component_of_node(
+ struct snd_soc_card *card)
+{
+ int i, index, ret = 0;
+ struct device *cdev = card->dev;
+ struct snd_soc_dai_link *dai_link = card->dai_link;
+ struct device_node *np;
+
+ if (!cdev) {
+ pr_err("%s: Sound card device memory NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < card->num_links; i++) {
+ if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node)
+ continue;
+
+ /* populate platform_of_node for snd card dai links */
+ if (dai_link[i].platform_name &&
+ !dai_link[i].platform_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-platform-names",
+ dai_link[i].platform_name);
+ if (index < 0) {
+ pr_err("%s: No match found for platform name: %s\n",
+ __func__, dai_link[i].platform_name);
+ ret = index;
+ goto err;
+ }
+ np = of_parse_phandle(cdev->of_node, "asoc-platform",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for platform %s, index %d failed\n",
+ __func__, dai_link[i].platform_name,
+ index);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].platform_of_node = np;
+ dai_link[i].platform_name = NULL;
+ }
+
+ /* populate cpu_of_node for snd card dai links */
+ if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-cpu-names",
+ dai_link[i].cpu_dai_name);
+ if (index >= 0) {
+ np = of_parse_phandle(cdev->of_node, "asoc-cpu",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for cpu dai %s failed\n",
+ __func__,
+ dai_link[i].cpu_dai_name);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].cpu_of_node = np;
+ dai_link[i].cpu_dai_name = NULL;
+ }
+ }
+
+ /* populate codec_of_node for snd card dai links */
+ if (dai_link[i].codec_name && !dai_link[i].codec_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-codec-names",
+ dai_link[i].codec_name);
+ if (index < 0)
+ continue;
+ np = of_parse_phandle(cdev->of_node, "asoc-codec",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for codec %s failed\n",
+ __func__, dai_link[i].codec_name);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].codec_of_node = np;
+ dai_link[i].codec_name = NULL;
+ }
+ }
+
+err:
+ return ret;
+}
+
+static int msm8996_prepare_us_euro(struct snd_soc_card *card)
+{
+ struct msm8996_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+ int ret;
+ if (pdata->us_euro_gpio >= 0) {
+ dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__,
+ pdata->us_euro_gpio);
+ ret = gpio_request(pdata->us_euro_gpio, "TASHA_CODEC_US_EURO");
+ if (ret) {
+ dev_err(card->dev,
+ "%s: Failed to request codec US/EURO gpio %d error %d\n",
+ __func__, pdata->us_euro_gpio, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int msm8996_prepare_hifi(struct snd_soc_card *card)
+{
+ struct msm8996_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+ int ret;
+
+ if (gpio_is_valid(pdata->hph_en1_gpio)) {
+ dev_dbg(card->dev, "%s: hph_en1_gpio request %d\n", __func__,
+ pdata->hph_en1_gpio);
+ ret = gpio_request(pdata->hph_en1_gpio, "hph_en1_gpio");
+ if (ret) {
+ dev_err(card->dev,
+ "%s: hph_en1_gpio request failed, ret:%d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ if (gpio_is_valid(pdata->hph_en0_gpio)) {
+ dev_dbg(card->dev, "%s: hph_en0_gpio request %d\n", __func__,
+ pdata->hph_en0_gpio);
+ ret = gpio_request(pdata->hph_en0_gpio, "hph_en0_gpio");
+ if (ret) {
+ dev_err(card->dev,
+ "%s: hph_en0_gpio request failed, ret:%d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct of_device_id msm8996_asoc_machine_of_match[] = {
+ { .compatible = "qcom,msm8996-asoc-snd-tasha",
+ .data = "tasha_codec"},
+ {},
+};
+
+static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
+{
+ struct snd_soc_card *card = NULL;
+ struct snd_soc_dai_link *dailink = NULL;
+ int len_1 = 0, len_2 = 0, len_3 = 0, len_4 = 0, len_5 = 0;
+ const struct of_device_id *match;
+
+ match = of_match_node(msm8996_asoc_machine_of_match, dev->of_node);
+ if (!match) {
+ dev_err(dev, "%s: No DT match found for sound card\n",
+ __func__);
+ return NULL;
+ }
+
+ if (!strcmp(match->data, "tasha_codec")) {
+ card = &snd_soc_card_tasha_msm8996;
+ len_1 = ARRAY_SIZE(msm8996_common_dai_links);
+ len_2 = len_1 + ARRAY_SIZE(msm8996_tasha_fe_dai_links);
+ len_3 = len_2 + ARRAY_SIZE(msm8996_tdm_fe_dai_links);
+ len_4 = len_3 + ARRAY_SIZE(msm8996_common_be_dai_links);
+
+ memcpy(msm8996_tasha_dai_links,
+ msm8996_common_dai_links,
+ sizeof(msm8996_common_dai_links));
+ memcpy(msm8996_tasha_dai_links + len_1,
+ msm8996_tasha_fe_dai_links,
+ sizeof(msm8996_tasha_fe_dai_links));
+ memcpy(msm8996_tasha_dai_links + len_2,
+ msm8996_tdm_fe_dai_links,
+ sizeof(msm8996_tdm_fe_dai_links));
+ memcpy(msm8996_tasha_dai_links + len_3,
+ msm8996_common_be_dai_links,
+ sizeof(msm8996_common_be_dai_links));
+ memcpy(msm8996_tasha_dai_links + len_4,
+ msm8996_tasha_be_dai_links,
+ sizeof(msm8996_tasha_be_dai_links));
+
+ dailink = msm8996_tasha_dai_links;
+ len_5 = len_4 + ARRAY_SIZE(msm8996_tasha_be_dai_links);
+ }
+
+ if (of_property_read_bool(dev->of_node, "qcom,hdmi-audio-rx")) {
+ dev_dbg(dev, "%s(): hdmi audio support present\n",
+ __func__);
+ memcpy(dailink + len_5, msm8996_hdmi_dai_link,
+ sizeof(msm8996_hdmi_dai_link));
+ len_5 += ARRAY_SIZE(msm8996_hdmi_dai_link);
+ } else {
+ dev_dbg(dev, "%s(): No hdmi audio support\n", __func__);
+ }
+ if (of_property_read_bool(dev->of_node, "qcom,tdm-audio-intf")) {
+ dev_dbg(dev, "%s(): TDM support present\n",
+ __func__);
+ memcpy(dailink + len_5, msm8996_tdm_be_dai_links,
+ sizeof(msm8996_tdm_be_dai_links));
+ len_5 += ARRAY_SIZE(msm8996_tdm_be_dai_links);
+ } else {
+ dev_dbg(dev, "%s(): No TDM support\n", __func__);
+ }
+ if (card) {
+ card->dai_link = dailink;
+ card->num_links = len_5;
+ }
+
+ return card;
+}
+
+static int msm8996_init_wsa_dev(struct platform_device *pdev,
+ struct snd_soc_card *card)
+{
+ struct device_node *wsa_of_node;
+ u32 wsa_max_devs;
+ u32 wsa_dev_cnt;
+ char *dev_name_str = NULL;
+ struct msm8996_wsa881x_dev_info *wsa881x_dev_info;
+ const char *wsa_auxdev_name_prefix[1];
+ int found = 0;
+ int i;
+ int ret;
+
+ /* Get maximum WSA device count for this platform */
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,wsa-max-devs", &wsa_max_devs);
+ if (ret) {
+ dev_dbg(&pdev->dev,
+ "%s: wsa-max-devs property missing in DT %s, ret = %d\n",
+ __func__, pdev->dev.of_node->full_name, ret);
+ return 0;
+ }
+ if (wsa_max_devs == 0) {
+ dev_warn(&pdev->dev,
+ "%s: Max WSA devices is 0 for this target?\n",
+ __func__);
+ return 0;
+ }
+
+ /* Get count of WSA device phandles for this platform */
+ wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node,
+ "qcom,wsa-devs", NULL);
+ if (wsa_dev_cnt == -ENOENT) {
+ dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n",
+ __func__);
+ return 0;
+ } else if (wsa_dev_cnt <= 0) {
+ dev_err(&pdev->dev,
+ "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n",
+ __func__, wsa_dev_cnt);
+ return -EINVAL;
+ }
+
+ /*
+ * Expect total phandles count to be NOT less than maximum possible
+ * WSA count. However, if it is less, then assign same value to
+ * max count as well.
+ */
+ if (wsa_dev_cnt < wsa_max_devs) {
+ dev_dbg(&pdev->dev,
+ "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n",
+ __func__, wsa_max_devs, wsa_dev_cnt);
+ wsa_max_devs = wsa_dev_cnt;
+ }
+
+ /* Make sure prefix string passed for each WSA device */
+ ret = of_property_count_strings(pdev->dev.of_node,
+ "qcom,wsa-aux-dev-prefix");
+ if (ret != wsa_dev_cnt) {
+ dev_err(&pdev->dev,
+ "%s: expecting %d wsa prefix. Defined only %d in DT\n",
+ __func__, wsa_dev_cnt, ret);
+ return -EINVAL;
+ }
+
+ /*
+ * Alloc mem to store phandle and index info of WSA device, if already
+ * registered with ALSA core
+ */
+ wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs,
+ sizeof(struct msm8996_wsa881x_dev_info),
+ GFP_KERNEL);
+ if (!wsa881x_dev_info)
+ return -ENOMEM;
+
+ /*
+ * search and check whether all WSA devices are already
+ * registered with ALSA core or not. If found a node, store
+ * the node and the index in a local array of struct for later
+ * use.
+ */
+ for (i = 0; i < wsa_dev_cnt; i++) {
+ wsa_of_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,wsa-devs", i);
+ if (unlikely(!wsa_of_node)) {
+ /* we should not be here */
+ dev_err(&pdev->dev,
+ "%s: wsa dev node is not present\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (soc_find_component(wsa_of_node, NULL)) {
+ /* WSA device registered with ALSA core */
+ wsa881x_dev_info[found].of_node = wsa_of_node;
+ wsa881x_dev_info[found].index = i;
+ found++;
+ if (found == wsa_max_devs)
+ break;
+ }
+ }
+
+ if (found < wsa_max_devs) {
+ dev_dbg(&pdev->dev,
+ "%s: failed to find %d components. Found only %d\n",
+ __func__, wsa_max_devs, found);
+ return -EPROBE_DEFER;
+ }
+ dev_info(&pdev->dev,
+ "%s: found %d wsa881x devices registered with ALSA core\n",
+ __func__, found);
+
+ card->num_aux_devs = wsa_max_devs;
+ card->num_configs = wsa_max_devs;
+
+ /* Alloc array of AUX devs struct */
+ msm8996_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+ sizeof(struct snd_soc_aux_dev),
+ GFP_KERNEL);
+ if (!msm8996_aux_dev)
+ return -ENOMEM;
+
+ /* Alloc array of codec conf struct */
+ msm8996_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+ sizeof(struct snd_soc_codec_conf),
+ GFP_KERNEL);
+ if (!msm8996_codec_conf)
+ return -ENOMEM;
+
+ for (i = 0; i < card->num_aux_devs; i++) {
+ dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN,
+ GFP_KERNEL);
+ if (!dev_name_str)
+ return -ENOMEM;
+
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "qcom,wsa-aux-dev-prefix",
+ wsa881x_dev_info[i].index,
+ wsa_auxdev_name_prefix);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: failed to read wsa aux dev prefix, ret = %d\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+
+ snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i);
+ msm8996_aux_dev[i].name = dev_name_str;
+ msm8996_aux_dev[i].codec_name = NULL;
+ msm8996_aux_dev[i].codec_of_node =
+ wsa881x_dev_info[i].of_node;
+ msm8996_aux_dev[i].init = msm8996_wsa881x_init;
+ msm8996_codec_conf[i].dev_name = NULL;
+ msm8996_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0];
+ msm8996_codec_conf[i].of_node =
+ wsa881x_dev_info[i].of_node;
+ }
+ card->codec_conf = msm8996_codec_conf;
+ card->aux_dev = msm8996_aux_dev;
+
+ return 0;
+}
+
+static int msm8996_asoc_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct msm8996_asoc_mach_data *pdata;
+ const char *mbhc_audio_jack_type = NULL;
+ char *mclk_freq_prop_name;
+ const struct of_device_id *match;
+ int ret;
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "No platform supplied from device tree\n");
+ return -EINVAL;
+ }
+
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm8996_asoc_mach_data), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev, "Can't allocate msm8996_asoc_mach_data\n");
+ return -ENOMEM;
+ }
+
+ card = populate_snd_card_dailinks(&pdev->dev);
+ if (!card) {
+ dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, pdata);
+
+ ret = snd_soc_of_parse_card_name(card, "qcom,model");
+ if (ret) {
+ dev_err(&pdev->dev, "parse card name failed, err:%d\n",
+ ret);
+ goto err;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "parse audio routing failed, err:%d\n",
+ ret);
+ goto err;
+ }
+
+ match = of_match_node(msm8996_asoc_machine_of_match,
+ pdev->dev.of_node);
+ if (!match) {
+ dev_err(&pdev->dev, "%s: no matched codec is found.\n",
+ __func__);
+ goto err;
+ }
+
+ mclk_freq_prop_name = "qcom,tasha-mclk-clk-freq";
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ mclk_freq_prop_name, &pdata->mclk_freq);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Looking up %s property in node %s failed, err%d\n",
+ mclk_freq_prop_name,
+ pdev->dev.of_node->full_name, ret);
+ goto err;
+ }
+
+ if (pdata->mclk_freq != CODEC_EXT_CLK_RATE) {
+ dev_err(&pdev->dev, "unsupported mclk freq %u\n",
+ pdata->mclk_freq);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ spdev = pdev;
+
+ ret = msm8996_populate_dai_link_component_of_node(card);
+ if (ret) {
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
+
+ ret = msm8996_init_wsa_dev(pdev, card);
+ if (ret)
+ goto err;
+
+ pdata->hph_en1_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,hph-en1-gpio", 0);
+ if (pdata->hph_en1_gpio < 0) {
+ dev_dbg(&pdev->dev, "%s: %s property not found %d\n",
+ __func__, "qcom,hph-en1-gpio", pdata->hph_en1_gpio);
+ }
+
+ pdata->hph_en0_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,hph-en0-gpio", 0);
+ if (pdata->hph_en0_gpio < 0) {
+ dev_dbg(&pdev->dev, "%s: %s property not found %d\n",
+ __func__, "qcom,hph-en0-gpio", pdata->hph_en0_gpio);
+ }
+ ret = msm8996_prepare_hifi(card);
+ if (ret)
+ dev_dbg(&pdev->dev, "msm8996_prepare_hifi failed (%d)\n",
+ ret);
+
+ ret = snd_soc_register_card(card);
+ if (ret == -EPROBE_DEFER) {
+ if (codec_reg_done)
+ ret = -EINVAL;
+ goto err;
+ } else if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err;
+ }
+ dev_info(&pdev->dev, "Sound card %s registered\n", card->name);
+
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type);
+ if (ret) {
+ dev_dbg(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,mbhc-audio-jack-type",
+ pdev->dev.of_node->full_name);
+ dev_dbg(&pdev->dev, "Jack type properties set to default");
+ } else {
+ if (!strcmp(mbhc_audio_jack_type, "4-pole-jack")) {
+ dev_dbg(&pdev->dev, "This hardware has 4 pole jack");
+ } else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) {
+ dev_dbg(&pdev->dev, "This hardware has 5 pole jack");
+ } else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) {
+ dev_dbg(&pdev->dev, "This hardware has 6 pole jack");
+ } else {
+ dev_dbg(&pdev->dev, "Unknown value, set to default");
+ }
+ }
+ /*
+ * Parse US-Euro gpio info from DT. Report no error if us-euro
+ * entry is not found in DT file as some targets do not support
+ * US-Euro detection
+ */
+ pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,us-euro-gpios", 0);
+ if (pdata->us_euro_gpio < 0) {
+ dev_info(&pdev->dev, "property %s not detected in node %s",
+ "qcom,us-euro-gpios",
+ pdev->dev.of_node->full_name);
+ } else {
+ dev_dbg(&pdev->dev, "%s detected %d",
+ "qcom,us-euro-gpios", pdata->us_euro_gpio);
+ wcd_mbhc_cfg.swap_gnd_mic = msm8996_swap_gnd_mic;
+ }
+
+ ret = msm8996_prepare_us_euro(card);
+ if (ret)
+ dev_info(&pdev->dev, "msm8996_prepare_us_euro failed (%d)\n",
+ ret);
+ return 0;
+err:
+ if (pdata->us_euro_gpio > 0) {
+ dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n",
+ __func__, pdata->us_euro_gpio);
+ gpio_free(pdata->us_euro_gpio);
+ pdata->us_euro_gpio = 0;
+ }
+ if (pdata->hph_en1_gpio > 0) {
+ dev_dbg(&pdev->dev, "%s free hph_en1_gpio %d\n",
+ __func__, pdata->hph_en1_gpio);
+ gpio_free(pdata->hph_en1_gpio);
+ pdata->hph_en1_gpio = 0;
+ }
+ if (pdata->hph_en0_gpio > 0) {
+ dev_dbg(&pdev->dev, "%s free hph_en0_gpio %d\n",
+ __func__, pdata->hph_en0_gpio);
+ gpio_free(pdata->hph_en0_gpio);
+ pdata->hph_en0_gpio = 0;
+ }
+ devm_kfree(&pdev->dev, pdata);
+ return ret;
+}
+
+static int msm8996_asoc_machine_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct msm8996_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+
+ if (gpio_is_valid(ext_us_amp_gpio))
+ gpio_free(ext_us_amp_gpio);
+
+ gpio_free(pdata->us_euro_gpio);
+ gpio_free(pdata->hph_en1_gpio);
+ gpio_free(pdata->hph_en0_gpio);
+
+ if (msm8996_liquid_dock_dev != NULL) {
+ switch_dev_unregister(&msm8996_liquid_dock_dev->audio_sdev);
+
+ if (msm8996_liquid_dock_dev->dock_plug_irq)
+ free_irq(msm8996_liquid_dock_dev->dock_plug_irq,
+ msm8996_liquid_dock_dev);
+
+ if (msm8996_liquid_dock_dev->dock_plug_gpio)
+ gpio_free(msm8996_liquid_dock_dev->dock_plug_gpio);
+
+ kfree(msm8996_liquid_dock_dev);
+ msm8996_liquid_dock_dev = NULL;
+ }
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static struct platform_driver msm8996_asoc_machine_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = msm8996_asoc_machine_of_match,
+ },
+ .probe = msm8996_asoc_machine_probe,
+ .remove = msm8996_asoc_machine_remove,
+};
+module_platform_driver(msm8996_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, msm8996_asoc_machine_of_match);
diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c
new file mode 100644
index 000000000000..e48b23dffe61
--- /dev/null
+++ b/sound/soc/msm/msm8998.c
@@ -0,0 +1,9501 @@
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <linux/of_device.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6core.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <device_event.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "../codecs/wcd9335.h"
+#include "../codecs/wcd934x/wcd934x.h"
+#include "../codecs/wcd934x/wcd934x-mbhc.h"
+#include "../codecs/wsa881x.h"
+
+#define DRV_NAME "msm8998-asoc-snd"
+
+#define __CHIPSET__ "MSM8998 "
+#define MSM_DAILINK_NAME(name) (__CHIPSET__#name)
+
+#define SAMPLING_RATE_8KHZ 8000
+#define SAMPLING_RATE_11P025KHZ 11025
+#define SAMPLING_RATE_16KHZ 16000
+#define SAMPLING_RATE_22P05KHZ 22050
+#define SAMPLING_RATE_32KHZ 32000
+#define SAMPLING_RATE_44P1KHZ 44100
+#define SAMPLING_RATE_48KHZ 48000
+#define SAMPLING_RATE_88P2KHZ 88200
+#define SAMPLING_RATE_96KHZ 96000
+#define SAMPLING_RATE_176P4KHZ 176400
+#define SAMPLING_RATE_192KHZ 192000
+#define SAMPLING_RATE_352P8KHZ 352800
+#define SAMPLING_RATE_384KHZ 384000
+
+#define WCD9XXX_MBHC_DEF_BUTTONS 8
+#define WCD9XXX_MBHC_DEF_RLOADS 5
+#define CODEC_EXT_CLK_RATE 9600000
+#define ADSP_STATE_READY_TIMEOUT_MS 3000
+#define DEV_NAME_STR_LEN 32
+
+#define WSA8810_NAME_1 "wsa881x.20170211"
+#define WSA8810_NAME_2 "wsa881x.20170212"
+
+#define WCN_CDC_SLIM_RX_CH_MAX 2
+#define WCN_CDC_SLIM_TX_CH_MAX 3
+
+#define TDM_CHANNEL_MAX 16
+#define TDM_SLOT_OFFSET_MAX 32
+
+#define MSM_HIFI_ON 1
+
+enum {
+ PRIMARY_TDM_RX_0,
+ PRIMARY_TDM_RX_1,
+ PRIMARY_TDM_RX_2,
+ PRIMARY_TDM_RX_3,
+ PRIMARY_TDM_RX_4,
+ PRIMARY_TDM_RX_5,
+ PRIMARY_TDM_RX_6,
+ PRIMARY_TDM_RX_7,
+ TDM_MAX_RX,
+};
+
+enum {
+ PRIMARY_TDM_TX_0,
+ PRIMARY_TDM_TX_1,
+ PRIMARY_TDM_TX_2,
+ PRIMARY_TDM_TX_3,
+ PRIMARY_TDM_TX_4,
+ PRIMARY_TDM_TX_5,
+ PRIMARY_TDM_TX_6,
+ PRIMARY_TDM_TX_7,
+ TDM_MAX_TX,
+};
+
+enum {
+ SLIM_RX_0 = 0,
+ SLIM_RX_1,
+ SLIM_RX_2,
+ SLIM_RX_3,
+ SLIM_RX_4,
+ SLIM_RX_5,
+ SLIM_RX_6,
+ SLIM_RX_7,
+ SLIM_RX_MAX,
+};
+
+enum {
+ SLIM_TX_0 = 0,
+ SLIM_TX_1,
+ SLIM_TX_2,
+ SLIM_TX_3,
+ SLIM_TX_4,
+ SLIM_TX_5,
+ SLIM_TX_6,
+ SLIM_TX_7,
+ SLIM_TX_8,
+ SLIM_TX_MAX,
+};
+
+enum {
+ PRIM_MI2S = 0,
+ SEC_MI2S,
+ TERT_MI2S,
+ QUAT_MI2S,
+ MI2S_MAX,
+};
+
+enum {
+ PRIM_AUX_PCM = 0,
+ SEC_AUX_PCM,
+ TERT_AUX_PCM,
+ QUAT_AUX_PCM,
+ AUX_PCM_MAX,
+};
+
+enum {
+ PCM_I2S_SEL_PRIM = 0,
+ PCM_I2S_SEL_SEC,
+ PCM_I2S_SEL_TERT,
+ PCM_I2S_SEL_QUAT,
+ PCM_I2S_SEL_MAX,
+};
+
+struct mi2s_aux_pcm_common_conf {
+ struct mutex lock;
+ void *pcm_i2s_sel_vt_addr;
+};
+
+struct mi2s_conf {
+ struct mutex lock;
+ u32 ref_cnt;
+ u32 msm_is_mi2s_master;
+};
+
+struct auxpcm_conf {
+ struct mutex lock;
+ u32 ref_cnt;
+};
+
+struct dev_config {
+ u32 sample_rate;
+ u32 bit_format;
+ u32 channels;
+};
+
+enum {
+ HDMI_RX_IDX = 0,
+ DP_RX_IDX,
+ EXT_DISP_RX_IDX_MAX,
+};
+
+struct msm_wsa881x_dev_info {
+ struct device_node *of_node;
+ u32 index;
+};
+
+enum pinctrl_pin_state {
+ STATE_DISABLE = 0, /* All pins are in sleep state */
+ STATE_MI2S_ACTIVE, /* IS2 = active, TDM = sleep */
+ STATE_TDM_ACTIVE, /* IS2 = sleep, TDM = active */
+};
+
+struct msm_pinctrl_info {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *mi2s_disable;
+ struct pinctrl_state *tdm_disable;
+ struct pinctrl_state *mi2s_active;
+ struct pinctrl_state *tdm_active;
+ enum pinctrl_pin_state curr_state;
+};
+
+struct msm_asoc_mach_data {
+ u32 mclk_freq;
+ int us_euro_gpio; /* used by gpio driver API */
+ struct device_node *us_euro_gpio_p; /* used by pinctrl API */
+ struct device_node *hph_en1_gpio_p; /* used by pinctrl API */
+ struct device_node *hph_en0_gpio_p; /* used by pinctrl API */
+ struct snd_info_entry *codec_root;
+ struct msm_pinctrl_info pinctrl_info;
+};
+
+struct msm_asoc_wcd93xx_codec {
+ void* (*get_afe_config_fn)(struct snd_soc_codec *codec,
+ enum afe_config_type config_type);
+ void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec);
+};
+
+static const char *const pin_states[] = {"sleep", "i2s-active",
+ "tdm-active"};
+
+enum {
+ TDM_0 = 0,
+ TDM_1,
+ TDM_2,
+ TDM_3,
+ TDM_4,
+ TDM_5,
+ TDM_6,
+ TDM_7,
+ TDM_PORT_MAX,
+};
+
+enum {
+ TDM_PRI = 0,
+ TDM_SEC,
+ TDM_TERT,
+ TDM_QUAT,
+ TDM_INTERFACE_MAX,
+};
+
+struct tdm_port {
+ u32 mode;
+ u32 channel;
+};
+
+/* TDM default config */
+static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+ { /* PRI TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ },
+ { /* SEC TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ },
+ { /* TERT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ },
+ { /* QUAT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ }
+};
+
+/* TDM default config */
+static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+ { /* PRI TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ },
+ { /* SEC TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ },
+ { /* TERT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ },
+ { /* QUAT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ }
+};
+
+/* Default configuration of slimbus channels */
+static struct dev_config slim_rx_cfg[] = {
+ [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config slim_tx_cfg[] = {
+ [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+
+/* Default configuration of external display BE */
+static struct dev_config ext_disp_rx_cfg[] = {
+ [HDMI_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [DP_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static struct dev_config usb_rx_cfg = {
+ .sample_rate = SAMPLING_RATE_48KHZ,
+ .bit_format = SNDRV_PCM_FORMAT_S16_LE,
+ .channels = 2,
+};
+
+static struct dev_config usb_tx_cfg = {
+ .sample_rate = SAMPLING_RATE_48KHZ,
+ .bit_format = SNDRV_PCM_FORMAT_S16_LE,
+ .channels = 1,
+};
+
+static struct dev_config proxy_rx_cfg = {
+ .sample_rate = SAMPLING_RATE_48KHZ,
+ .bit_format = SNDRV_PCM_FORMAT_S16_LE,
+ .channels = 2,
+};
+
+/* Default configuration of MI2S channels */
+static struct dev_config mi2s_rx_cfg[] = {
+ [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static struct dev_config mi2s_tx_cfg[] = {
+ [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_rx_cfg[] = {
+ [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_tx_cfg[] = {
+ [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+/* TDM default slot config */
+struct tdm_slot_cfg {
+ u32 width;
+ u32 num;
+};
+
+static struct tdm_slot_cfg tdm_slot[TDM_INTERFACE_MAX] = {
+ /* PRI TDM */
+ {32, 8},
+ /* SEC TDM */
+ {32, 8},
+ /* TERT TDM */
+ {32, 8},
+ /* QUAT TDM */
+ {32, 8}
+};
+
+static unsigned int tdm_rx_slot_offset
+ [TDM_INTERFACE_MAX][TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = {
+ {/* PRI TDM */
+ {0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ },
+ {/* SEC TDM */
+ {0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ },
+ {/* TERT TDM */
+ {0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ },
+ {/* QUAT TDM */
+ {0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ }
+};
+
+static unsigned int tdm_tx_slot_offset
+ [TDM_INTERFACE_MAX][TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = {
+ {/* PRI TDM */
+ {0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ },
+ {/* SEC TDM */
+ {0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ },
+ {/* TERT TDM */
+ {0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ },
+ {/* QUAT TDM */
+ {0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF},/*MIC ARR*/
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ }
+};
+static int msm_vi_feed_tx_ch = 2;
+static const char *const slim_rx_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven",
+ "Eight"};
+static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven",
+ "Eight"};
+static const char *const vi_feed_ch_text[] = {"One", "Two"};
+static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE",
+ "S32_LE"};
+static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE"};
+static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16",
+ "KHZ_32", "KHZ_44P1", "KHZ_48",
+ "KHZ_88P2", "KHZ_96", "KHZ_176P4",
+ "KHZ_192", "KHZ_352P8", "KHZ_384"};
+static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+static char const *bt_sample_rate_rx_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+static char const *bt_sample_rate_tx_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven",
+ "Eight"};
+static char const *ch_text[] = {"Two", "Three", "Four", "Five",
+ "Six", "Seven", "Eight"};
+static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025",
+ "KHZ_16", "KHZ_22P05",
+ "KHZ_32", "KHZ_44P1", "KHZ_48",
+ "KHZ_88P2", "KHZ_96", "KHZ_176P4",
+ "KHZ_192", "KHZ_352P8", "KHZ_384"};
+static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+ "KHZ_192", "KHZ_32", "KHZ_44P1",
+ "KHZ_88P2", "KHZ_176P4"};
+static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight",
+ "Nine", "Ten", "Eleven", "Twelve",
+ "Thirteen", "Fourteen", "Fifteen",
+ "Sixteen"};
+static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"};
+static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32",
+ "KHZ_44P1", "KHZ_48", "KHZ_96",
+ "KHZ_192", "KHZ_352P8", "KHZ_384"};
+static const char *const tdm_slot_num_text[] = {"One", "Two", "Four",
+ "Eight", "Sixteen", "ThirtyTwo"};
+static const char *const tdm_slot_width_text[] = {"16", "24", "32"};
+static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"};
+static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16",
+ "KHZ_32", "KHZ_44P1", "KHZ_48",
+ "KHZ_88P2", "KHZ_96", "KHZ_176P4",
+ "KHZ_192"};
+static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven",
+ "Eight"};
+static const char *const hifi_text[] = {"Off", "On"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_rx, bt_sample_rate_rx_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_tx, bt_sample_rate_tx_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate,
+ ext_disp_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_slot_num, tdm_slot_num_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_slot_width, tdm_slot_width_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(mi2s_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text);
+
+static struct platform_device *spdev;
+static int msm_hifi_control;
+
+static bool is_initial_boot;
+static bool codec_reg_done;
+static struct snd_soc_aux_dev *msm_aux_dev;
+static struct snd_soc_codec_conf *msm_codec_conf;
+static struct msm_asoc_wcd93xx_codec msm_codec_fn;
+
+static void *def_tasha_mbhc_cal(void);
+static void *def_tavil_mbhc_cal(void);
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
+ int enable, bool dapm);
+static int msm_wsa881x_init(struct snd_soc_component *component);
+
+/*
+ * Need to report LINEIN
+ * if R/L channel impedance is larger than 5K ohm
+ */
+static struct wcd_mbhc_config wcd_mbhc_cfg = {
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .detect_extn_cable = true,
+ .mono_stero_detection = false,
+ .swap_gnd_mic = NULL,
+ .hs_ext_micbias = true,
+ .key_code[0] = KEY_MEDIA,
+ .key_code[1] = KEY_VOICECOMMAND,
+ .key_code[2] = KEY_VOLUMEUP,
+ .key_code[3] = KEY_VOLUMEDOWN,
+ .key_code[4] = 0,
+ .key_code[5] = 0,
+ .key_code[6] = 0,
+ .key_code[7] = 0,
+ .linein_th = 5000,
+ .moisture_en = true,
+ .mbhc_micbias = MIC_BIAS_2,
+ .anc_micbias = MIC_BIAS_2,
+ .enable_anc_mic_detect = false,
+};
+
+static struct snd_soc_dapm_route wcd_audio_paths_tasha[] = {
+ {"MIC BIAS1", NULL, "MCLK TX"},
+ {"MIC BIAS2", NULL, "MCLK TX"},
+ {"MIC BIAS3", NULL, "MCLK TX"},
+ {"MIC BIAS4", NULL, "MCLK TX"},
+};
+
+static struct snd_soc_dapm_route wcd_audio_paths[] = {
+ {"MIC BIAS1", NULL, "MCLK"},
+ {"MIC BIAS2", NULL, "MCLK"},
+ {"MIC BIAS3", NULL, "MCLK"},
+ {"MIC BIAS4", NULL, "MCLK"},
+};
+
+static u32 mi2s_ebit_clk[MI2S_MAX] = {
+ Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT,
+ Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT,
+ Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT,
+};
+
+static struct afe_clk_set mi2s_clk[MI2S_MAX] = {
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ }
+};
+
+static struct mi2s_aux_pcm_common_conf mi2s_auxpcm_conf[PCM_I2S_SEL_MAX];
+static struct mi2s_conf mi2s_intf_conf[MI2S_MAX];
+static struct auxpcm_conf auxpcm_intf_conf[AUX_PCM_MAX];
+
+static int slim_get_sample_rate_val(int sample_rate)
+{
+ int sample_rate_val = 0;
+
+ switch (sample_rate) {
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 0;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_88P2KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_176P4KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 8;
+ break;
+ case SAMPLING_RATE_352P8KHZ:
+ sample_rate_val = 9;
+ break;
+ case SAMPLING_RATE_384KHZ:
+ sample_rate_val = 10;
+ break;
+ default:
+ sample_rate_val = 4;
+ break;
+ }
+ return sample_rate_val;
+}
+
+static int slim_get_sample_rate(int value)
+{
+ int sample_rate = 0;
+
+ switch (value) {
+ case 0:
+ sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ sample_rate = SAMPLING_RATE_88P2KHZ;
+ break;
+ case 6:
+ sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 7:
+ sample_rate = SAMPLING_RATE_176P4KHZ;
+ break;
+ case 8:
+ sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 9:
+ sample_rate = SAMPLING_RATE_352P8KHZ;
+ break;
+ case 10:
+ sample_rate = SAMPLING_RATE_384KHZ;
+ break;
+ default:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return sample_rate;
+}
+
+static int slim_get_bit_format_val(int bit_format)
+{
+ int val = 0;
+
+ switch (bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ val = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+static int slim_get_bit_format(int val)
+{
+ int bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+
+ switch (val) {
+ case 0:
+ bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 1:
+ bit_fmt = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 2:
+ bit_fmt = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 3:
+ bit_fmt = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ default:
+ bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return bit_fmt;
+}
+
+static int slim_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+ int port_id = 0;
+
+ if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX")))
+ port_id = SLIM_RX_0;
+ else if (strnstr(kcontrol->id.name, "SLIM_2_RX", sizeof("SLIM_2_RX")))
+ port_id = SLIM_RX_2;
+ else if (strnstr(kcontrol->id.name, "SLIM_5_RX", sizeof("SLIM_5_RX")))
+ port_id = SLIM_RX_5;
+ else if (strnstr(kcontrol->id.name, "SLIM_6_RX", sizeof("SLIM_6_RX")))
+ port_id = SLIM_RX_6;
+ else if (strnstr(kcontrol->id.name, "SLIM_0_TX", sizeof("SLIM_0_TX")))
+ port_id = SLIM_TX_0;
+ else if (strnstr(kcontrol->id.name, "SLIM_1_TX", sizeof("SLIM_1_TX")))
+ port_id = SLIM_TX_1;
+ else {
+ pr_err("%s: unsupported channel: %s",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ return port_id;
+}
+
+static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ ucontrol->value.enumerated.item[0] =
+ slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate);
+
+ pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ ch_num, slim_rx_cfg[ch_num].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ slim_rx_cfg[ch_num].sample_rate =
+ slim_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ ch_num, slim_rx_cfg[ch_num].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ ucontrol->value.enumerated.item[0] =
+ slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate);
+
+ pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ ch_num, slim_tx_cfg[ch_num].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate = 0;
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]);
+ if (sample_rate == SAMPLING_RATE_44P1KHZ) {
+ pr_err("%s: Unsupported sample rate %d: for Tx path\n",
+ __func__, sample_rate);
+ return -EINVAL;
+ }
+ slim_tx_cfg[ch_num].sample_rate = sample_rate;
+
+ pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__,
+ ch_num, slim_tx_cfg[ch_num].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ ucontrol->value.enumerated.item[0] =
+ slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format);
+
+ pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+ __func__, ch_num, slim_rx_cfg[ch_num].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ slim_rx_cfg[ch_num].bit_format =
+ slim_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+ __func__, ch_num, slim_rx_cfg[ch_num].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ ucontrol->value.enumerated.item[0] =
+ slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format);
+
+ pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n",
+ __func__, ch_num, slim_tx_cfg[ch_num].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ slim_tx_cfg[ch_num].bit_format =
+ slim_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n",
+ __func__, ch_num, slim_tx_cfg[ch_num].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_slim_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__,
+ ch_num, slim_rx_cfg[ch_num].channels);
+ ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1;
+
+ return 0;
+}
+
+static int msm_slim_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1;
+ pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__,
+ ch_num, slim_rx_cfg[ch_num].channels);
+
+ return 1;
+}
+
+static int msm_slim_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__,
+ ch_num, slim_tx_cfg[ch_num].channels);
+ ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1;
+
+ return 0;
+}
+
+static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1;
+ pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__,
+ ch_num, slim_tx_cfg[ch_num].channels);
+
+ return 1;
+}
+
+static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1;
+ pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch);
+ return 1;
+}
+
+static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /*
+ * Slimbus_7_Rx/Tx sample rate values should always be in sync (same)
+ * when used for BT_SCO use case. Return either Rx or Tx sample rate
+ * value.
+ */
+ switch (slim_rx_cfg[SLIM_RX_7].sample_rate) {
+ case SAMPLING_RATE_48KHZ:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: sample rate = %d", __func__,
+ slim_rx_cfg[SLIM_RX_7].sample_rate);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ;
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ;
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 0:
+ default:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ;
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n",
+ __func__,
+ slim_rx_cfg[SLIM_RX_7].sample_rate,
+ slim_tx_cfg[SLIM_TX_7].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (slim_rx_cfg[SLIM_RX_7].sample_rate) {
+ case SAMPLING_RATE_48KHZ:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: sample rate = %d", __func__,
+ slim_rx_cfg[SLIM_RX_7].sample_rate);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 0:
+ default:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: sample rates: slim7_rx = %d, value = %d\n",
+ __func__,
+ slim_rx_cfg[SLIM_RX_7].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_tx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (slim_tx_cfg[SLIM_TX_7].sample_rate) {
+ case SAMPLING_RATE_48KHZ:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: sample rate = %d", __func__,
+ slim_tx_cfg[SLIM_TX_7].sample_rate);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_tx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 0:
+ default:
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: sample rates: slim7_tx = %d, value = %d\n",
+ __func__,
+ slim_tx_cfg[SLIM_TX_7].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: usb_audio_rx_ch = %d\n", __func__,
+ usb_rx_cfg.channels);
+ ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1;
+ return 0;
+}
+
+static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels);
+ return 1;
+}
+
+static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val;
+
+ switch (usb_rx_cfg.sample_rate) {
+ case SAMPLING_RATE_384KHZ:
+ sample_rate_val = 12;
+ break;
+ case SAMPLING_RATE_352P8KHZ:
+ sample_rate_val = 11;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 10;
+ break;
+ case SAMPLING_RATE_176P4KHZ:
+ sample_rate_val = 9;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 8;
+ break;
+ case SAMPLING_RATE_88P2KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_22P05KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_11P025KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__,
+ usb_rx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 12:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+ break;
+ case 11:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ;
+ break;
+ case 10:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 9:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ;
+ break;
+ case 8:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 7:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ;
+ break;
+ case 6:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+ break;
+ case 2:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 1:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+ break;
+ case 0:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ default:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+
+ pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n",
+ __func__, ucontrol->value.integer.value[0],
+ usb_rx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (usb_rx_cfg.bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_rx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 3:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ case 2:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 1:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_rx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return rc;
+}
+
+static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: usb_audio_tx_ch = %d\n", __func__,
+ usb_tx_cfg.channels);
+ ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1;
+ return 0;
+}
+
+static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels);
+ return 1;
+}
+
+static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val;
+
+ switch (usb_tx_cfg.sample_rate) {
+ case SAMPLING_RATE_384KHZ:
+ sample_rate_val = 12;
+ break;
+ case SAMPLING_RATE_352P8KHZ:
+ sample_rate_val = 11;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 10;
+ break;
+ case SAMPLING_RATE_176P4KHZ:
+ sample_rate_val = 9;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 8;
+ break;
+ case SAMPLING_RATE_88P2KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_22P05KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_11P025KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 0;
+ break;
+ default:
+ sample_rate_val = 6;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__,
+ usb_tx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 12:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+ break;
+ case 11:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ;
+ break;
+ case 10:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 9:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ;
+ break;
+ case 8:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 7:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ;
+ break;
+ case 6:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+ break;
+ case 2:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 1:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+ break;
+ case 0:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ default:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+
+ pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n",
+ __func__, ucontrol->value.integer.value[0],
+ usb_tx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (usb_tx_cfg.bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_tx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 3:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ case 2:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 1:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_tx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return rc;
+}
+
+static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+ int idx;
+
+ if (strnstr(kcontrol->id.name, "HDMI_RX", sizeof("HDMI_RX")))
+ idx = HDMI_RX_IDX;
+ else if (strnstr(kcontrol->id.name, "Display Port RX",
+ sizeof("Display Port RX")))
+ idx = DP_RX_IDX;
+ else {
+ pr_err("%s: unsupported BE: %s",
+ __func__, kcontrol->id.name);
+ idx = -EINVAL;
+ }
+
+ return idx;
+}
+
+static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ switch (ext_disp_rx_cfg[idx].bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n",
+ __func__, idx, ext_disp_rx_cfg[idx].bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n",
+ __func__, idx, ext_disp_rx_cfg[idx].bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.integer.value[0] =
+ ext_disp_rx_cfg[idx].channels - 2;
+
+ pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__,
+ idx, ext_disp_rx_cfg[idx].channels);
+
+ return 0;
+}
+
+static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ext_disp_rx_cfg[idx].channels =
+ ucontrol->value.integer.value[0] + 2;
+
+ pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__,
+ idx, ext_disp_rx_cfg[idx].channels);
+ return 1;
+}
+
+static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val;
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ switch (ext_disp_rx_cfg[idx].sample_rate) {
+ case SAMPLING_RATE_176P4KHZ:
+ sample_rate_val = 6;
+ break;
+
+ case SAMPLING_RATE_88P2KHZ:
+ sample_rate_val = 5;
+ break;
+
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 4;
+ break;
+
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 3;
+ break;
+
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 2;
+ break;
+
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 1;
+ break;
+
+ case SAMPLING_RATE_48KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__,
+ idx, ext_disp_rx_cfg[idx].sample_rate);
+
+ return 0;
+}
+
+static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 6:
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_176P4KHZ;
+ break;
+ case 5:
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_88P2KHZ;
+ break;
+ case 4:
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 3:
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 2:
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 1:
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 0:
+ default:
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+
+ pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n",
+ __func__, ucontrol->value.integer.value[0], idx,
+ ext_disp_rx_cfg[idx].sample_rate);
+ return 0;
+}
+
+static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: proxy_rx channels = %d\n",
+ __func__, proxy_rx_cfg.channels);
+ ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2;
+
+ return 0;
+}
+
+static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2;
+ pr_debug("%s: proxy_rx channels = %d\n",
+ __func__, proxy_rx_cfg.channels);
+
+ return 1;
+}
+
+static int tdm_get_sample_rate(int value)
+{
+ int sample_rate = 0;
+
+ switch (value) {
+ case 0:
+ sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 6:
+ sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 7:
+ sample_rate = SAMPLING_RATE_352P8KHZ;
+ break;
+ case 8:
+ sample_rate = SAMPLING_RATE_384KHZ;
+ break;
+ default:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return sample_rate;
+}
+
+static int aux_pcm_get_sample_rate(int value)
+{
+ int sample_rate;
+
+ switch (value) {
+ case 1:
+ sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 0:
+ default:
+ sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ return sample_rate;
+}
+
+static int tdm_get_sample_rate_val(int sample_rate)
+{
+ int sample_rate_val = 0;
+
+ switch (sample_rate) {
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 0;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_352P8KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_384KHZ:
+ sample_rate_val = 8;
+ break;
+ default:
+ sample_rate_val = 4;
+ break;
+ }
+ return sample_rate_val;
+}
+
+static int aux_pcm_get_sample_rate_val(int sample_rate)
+{
+ int sample_rate_val;
+
+ switch (sample_rate) {
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+ return sample_rate_val;
+}
+
+static int tdm_get_mode(struct snd_kcontrol *kcontrol)
+{
+ int mode;
+
+ if (strnstr(kcontrol->id.name, "PRI",
+ sizeof(kcontrol->id.name))) {
+ mode = TDM_PRI;
+ } else if (strnstr(kcontrol->id.name, "SEC",
+ sizeof(kcontrol->id.name))) {
+ mode = TDM_SEC;
+ } else if (strnstr(kcontrol->id.name, "TERT",
+ sizeof(kcontrol->id.name))) {
+ mode = TDM_TERT;
+ } else if (strnstr(kcontrol->id.name, "QUAT",
+ sizeof(kcontrol->id.name))) {
+ mode = TDM_QUAT;
+ } else {
+ pr_err("%s: unsupported mode in: %s",
+ __func__, kcontrol->id.name);
+ mode = -EINVAL;
+ }
+
+ return mode;
+}
+
+static int tdm_get_channel(struct snd_kcontrol *kcontrol)
+{
+ int channel;
+
+ if (strnstr(kcontrol->id.name, "RX_0",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_0",
+ sizeof(kcontrol->id.name))) {
+ channel = TDM_0;
+ } else if (strnstr(kcontrol->id.name, "RX_1",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_1",
+ sizeof(kcontrol->id.name))) {
+ channel = TDM_1;
+ } else if (strnstr(kcontrol->id.name, "RX_2",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_2",
+ sizeof(kcontrol->id.name))) {
+ channel = TDM_2;
+ } else if (strnstr(kcontrol->id.name, "RX_3",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_3",
+ sizeof(kcontrol->id.name))) {
+ channel = TDM_3;
+ } else if (strnstr(kcontrol->id.name, "RX_4",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_4",
+ sizeof(kcontrol->id.name))) {
+ channel = TDM_4;
+ } else if (strnstr(kcontrol->id.name, "RX_5",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_5",
+ sizeof(kcontrol->id.name))) {
+ channel = TDM_5;
+ } else if (strnstr(kcontrol->id.name, "RX_6",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_6",
+ sizeof(kcontrol->id.name))) {
+ channel = TDM_6;
+ } else if (strnstr(kcontrol->id.name, "RX_7",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_7",
+ sizeof(kcontrol->id.name))) {
+ channel = TDM_7;
+ } else {
+ pr_err("%s: unsupported channel in: %s",
+ __func__, kcontrol->id.name);
+ channel = -EINVAL;
+ }
+
+ return channel;
+}
+
+static int tdm_get_port_idx(struct snd_kcontrol *kcontrol,
+ struct tdm_port *port)
+{
+ if (port) {
+ port->mode = tdm_get_mode(kcontrol);
+ if (port->mode < 0)
+ return port->mode;
+
+ port->channel = tdm_get_channel(kcontrol);
+ if (port->channel < 0)
+ return port->channel;
+ } else
+ return -EINVAL;
+ return 0;
+}
+
+static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+ tdm_rx_cfg[port.mode][port.channel].sample_rate);
+
+ pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_rx_cfg[port.mode][port.channel].sample_rate =
+ tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+ tdm_tx_cfg[port.mode][port.channel].sample_rate);
+
+ pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_tx_cfg[port.mode][port.channel].sample_rate =
+ tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_get_format(int value)
+{
+ int format = 0;
+
+ switch (value) {
+ case 0:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 1:
+ format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 2:
+ format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ default:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return format;
+}
+
+static int tdm_get_format_val(int format)
+{
+ int value = 0;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ value = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ value = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ value = 2;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ return value;
+}
+
+static int tdm_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+ tdm_rx_cfg[port.mode][port.channel].bit_format);
+
+ pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_rx_cfg[port.mode][port.channel].bit_format =
+ tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+ tdm_tx_cfg[port.mode][port.channel].bit_format);
+
+ pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_tx_cfg[port.mode][port.channel].bit_format =
+ tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+
+ ucontrol->value.enumerated.item[0] =
+ tdm_rx_cfg[port.mode][port.channel].channels - 1;
+
+ pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].channels - 1,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_rx_cfg[port.mode][port.channel].channels =
+ ucontrol->value.enumerated.item[0] + 1;
+
+ pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].channels,
+ ucontrol->value.enumerated.item[0] + 1);
+ }
+ return ret;
+}
+
+static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] =
+ tdm_tx_cfg[port.mode][port.channel].channels - 1;
+
+ pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].channels - 1,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_tx_cfg[port.mode][port.channel].channels =
+ ucontrol->value.enumerated.item[0] + 1;
+
+ pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].channels,
+ ucontrol->value.enumerated.item[0] + 1);
+ }
+ return ret;
+}
+
+static int tdm_get_slot_num_val(int slot_num)
+{
+ int slot_num_val;
+
+ switch (slot_num) {
+ case 1:
+ slot_num_val = 0;
+ break;
+ case 2:
+ slot_num_val = 1;
+ break;
+ case 4:
+ slot_num_val = 2;
+ break;
+ case 8:
+ slot_num_val = 3;
+ break;
+ case 16:
+ slot_num_val = 4;
+ break;
+ case 32:
+ slot_num_val = 5;
+ break;
+ default:
+ slot_num_val = 5;
+ break;
+ }
+ return slot_num_val;
+}
+
+static int tdm_slot_num_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mode = tdm_get_mode(kcontrol);
+
+ if (mode < 0) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ return mode;
+ }
+
+ ucontrol->value.enumerated.item[0] =
+ tdm_get_slot_num_val(tdm_slot[mode].num);
+
+ pr_debug("%s: mode = %d, tdm_slot_num = %d, item = %d\n", __func__,
+ mode, tdm_slot[mode].num,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int tdm_get_slot_num(int value)
+{
+ int slot_num;
+
+ switch (value) {
+ case 0:
+ slot_num = 1;
+ break;
+ case 1:
+ slot_num = 2;
+ break;
+ case 2:
+ slot_num = 4;
+ break;
+ case 3:
+ slot_num = 8;
+ break;
+ case 4:
+ slot_num = 16;
+ break;
+ case 5:
+ slot_num = 32;
+ break;
+ default:
+ slot_num = 8;
+ break;
+ }
+ return slot_num;
+}
+
+static int tdm_slot_num_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mode = tdm_get_mode(kcontrol);
+
+ if (mode < 0) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ return mode;
+ }
+
+ tdm_slot[mode].num =
+ tdm_get_slot_num(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: mode = %d, tdm_slot_num = %d, item = %d\n", __func__,
+ mode, tdm_slot[mode].num,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int tdm_get_slot_width_val(int slot_width)
+{
+ int slot_width_val;
+
+ switch (slot_width) {
+ case 16:
+ slot_width_val = 0;
+ break;
+ case 24:
+ slot_width_val = 1;
+ break;
+ case 32:
+ slot_width_val = 2;
+ break;
+ default:
+ slot_width_val = 2;
+ break;
+ }
+ return slot_width_val;
+}
+
+static int tdm_slot_width_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mode = tdm_get_mode(kcontrol);
+
+ if (mode < 0) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ return mode;
+ }
+
+ ucontrol->value.enumerated.item[0] =
+ tdm_get_slot_width_val(tdm_slot[mode].width);
+
+ pr_debug("%s: mode = %d, tdm_slot_width = %d, item = %d\n", __func__,
+ mode, tdm_slot[mode].width,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int tdm_get_slot_width(int value)
+{
+ int slot_width;
+
+ switch (value) {
+ case 0:
+ slot_width = 16;
+ break;
+ case 1:
+ slot_width = 24;
+ break;
+ case 2:
+ slot_width = 32;
+ break;
+ default:
+ slot_width = 32;
+ break;
+ }
+ return slot_width;
+}
+
+static int tdm_slot_width_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mode = tdm_get_mode(kcontrol);
+
+ if (mode < 0) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ return mode;
+ }
+
+ tdm_slot[mode].width =
+ tdm_get_slot_width(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: mode = %d, tdm_slot_width = %d, item = %d\n", __func__,
+ mode, tdm_slot[mode].width,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int tdm_rx_slot_mapping_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int *slot_offset;
+ int i;
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ if (port.mode < TDM_INTERFACE_MAX &&
+ port.channel < TDM_PORT_MAX) {
+ slot_offset =
+ tdm_rx_slot_offset[port.mode][port.channel];
+ pr_debug("%s: mode = %d, channel = %d\n",
+ __func__, port.mode, port.channel);
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ ucontrol->value.integer.value[i] =
+ slot_offset[i];
+ pr_debug("%s: offset %d, value %d\n",
+ __func__, i, slot_offset[i]);
+ }
+ } else {
+ pr_err("%s: unsupported mode/channel", __func__);
+ }
+ }
+ return ret;
+}
+
+static int tdm_rx_slot_mapping_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int *slot_offset;
+ int i;
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ if (port.mode < TDM_INTERFACE_MAX &&
+ port.channel < TDM_PORT_MAX) {
+ slot_offset =
+ tdm_rx_slot_offset[port.mode][port.channel];
+ pr_debug("%s: mode = %d, channel = %d\n",
+ __func__, port.mode, port.channel);
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ slot_offset[i] =
+ ucontrol->value.integer.value[i];
+ pr_debug("%s: offset %d, value %d\n",
+ __func__, i, slot_offset[i]);
+ }
+ } else {
+ pr_err("%s: unsupported mode/channel", __func__);
+ }
+ }
+ return ret;
+}
+
+static int tdm_tx_slot_mapping_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int *slot_offset;
+ int i;
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ if (port.mode < TDM_INTERFACE_MAX &&
+ port.channel < TDM_PORT_MAX) {
+ slot_offset =
+ tdm_tx_slot_offset[port.mode][port.channel];
+ pr_debug("%s: mode = %d, channel = %d\n",
+ __func__, port.mode, port.channel);
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ ucontrol->value.integer.value[i] =
+ slot_offset[i];
+ pr_debug("%s: offset %d, value %d\n",
+ __func__, i, slot_offset[i]);
+ }
+ } else {
+ pr_err("%s: unsupported mode/channel", __func__);
+ }
+ }
+ return ret;
+}
+
+static int tdm_tx_slot_mapping_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int *slot_offset;
+ int i;
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ if (port.mode < TDM_INTERFACE_MAX &&
+ port.channel < TDM_PORT_MAX) {
+ slot_offset =
+ tdm_tx_slot_offset[port.mode][port.channel];
+ pr_debug("%s: mode = %d, channel = %d\n",
+ __func__, port.mode, port.channel);
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ slot_offset[i] =
+ ucontrol->value.integer.value[i];
+ pr_debug("%s: offset %d, value %d\n",
+ __func__, i, slot_offset[i]);
+ }
+ } else {
+ pr_err("%s: unsupported mode/channel", __func__);
+ }
+ }
+ return ret;
+}
+
+static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+ int idx;
+
+ if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM",
+ sizeof("PRIM_AUX_PCM")))
+ idx = PRIM_AUX_PCM;
+ else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM",
+ sizeof("SEC_AUX_PCM")))
+ idx = SEC_AUX_PCM;
+ else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM",
+ sizeof("TERT_AUX_PCM")))
+ idx = TERT_AUX_PCM;
+ else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM",
+ sizeof("QUAT_AUX_PCM")))
+ idx = QUAT_AUX_PCM;
+ else {
+ pr_err("%s: unsupported port: %s",
+ __func__, kcontrol->id.name);
+ idx = -EINVAL;
+ }
+
+ return idx;
+}
+
+static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ aux_pcm_rx_cfg[idx].sample_rate =
+ aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ aux_pcm_tx_cfg[idx].sample_rate =
+ aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+ int idx;
+
+ if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX",
+ sizeof("PRIM_MI2S_RX")))
+ idx = PRIM_MI2S;
+ else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX",
+ sizeof("SEC_MI2S_RX")))
+ idx = SEC_MI2S;
+ else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX",
+ sizeof("TERT_MI2S_RX")))
+ idx = TERT_MI2S;
+ else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX",
+ sizeof("QUAT_MI2S_RX")))
+ idx = QUAT_MI2S;
+ else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX",
+ sizeof("PRIM_MI2S_TX")))
+ idx = PRIM_MI2S;
+ else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX",
+ sizeof("SEC_MI2S_TX")))
+ idx = SEC_MI2S;
+ else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX",
+ sizeof("TERT_MI2S_TX")))
+ idx = TERT_MI2S;
+ else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX",
+ sizeof("QUAT_MI2S_TX")))
+ idx = QUAT_MI2S;
+ else {
+ pr_err("%s: unsupported channel: %s",
+ __func__, kcontrol->id.name);
+ idx = -EINVAL;
+ }
+
+ return idx;
+}
+
+static int mi2s_get_sample_rate_val(int sample_rate)
+{
+ int sample_rate_val;
+
+ switch (sample_rate) {
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 0;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_88P2KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_176P4KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 8;
+ break;
+ default:
+ sample_rate_val = 4;
+ break;
+ }
+ return sample_rate_val;
+}
+
+static int mi2s_get_sample_rate(int value)
+{
+ int sample_rate;
+
+ switch (value) {
+ case 0:
+ sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ sample_rate = SAMPLING_RATE_88P2KHZ;
+ break;
+ case 6:
+ sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 7:
+ sample_rate = SAMPLING_RATE_176P4KHZ;
+ break;
+ case 8:
+ sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ default:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return sample_rate;
+}
+
+static int mi2s_get_format(int value)
+{
+ int format;
+
+ switch (value) {
+ case 0:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 1:
+ format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 2:
+ format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 3:
+ format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ default:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return format;
+}
+
+static int mi2s_get_format_value(int format)
+{
+ int value;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ value = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ value = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ value = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ value = 3;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ return value;
+}
+
+static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_rx_cfg[idx].sample_rate =
+ mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_tx_cfg[idx].sample_rate =
+ mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].channels);
+ ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1;
+
+ return 0;
+}
+
+static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+ pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].channels);
+
+ return 1;
+}
+
+static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].channels);
+ ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1;
+
+ return 0;
+}
+
+static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+ pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].channels);
+
+ return 1;
+}
+
+static int msm_mi2s_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_format_value(mi2s_rx_cfg[idx].bit_format);
+
+ pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_mi2s_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_rx_cfg[idx].bit_format =
+ mi2s_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_mi2s_tx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_format_value(mi2s_tx_cfg[idx].bit_format);
+
+ pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_tx_cfg[idx].bit_format =
+ mi2s_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_hifi_ctrl(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_card *card = codec->component.card;
+ struct msm_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+
+ pr_debug("%s: msm_hifi_control = %d", __func__,
+ msm_hifi_control);
+
+ if (!pdata || !pdata->hph_en1_gpio_p) {
+ pr_err("%s: hph_en1_gpio is invalid\n", __func__);
+ return -EINVAL;
+ }
+ if (msm_hifi_control == MSM_HIFI_ON) {
+ msm_cdc_pinctrl_select_active_state(pdata->hph_en1_gpio_p);
+ /* 5msec delay needed as per HW requirement */
+ usleep_range(5000, 5010);
+ } else {
+ msm_cdc_pinctrl_select_sleep_state(pdata->hph_en1_gpio_p);
+ }
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static int msm_hifi_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_hifi_control = %d\n",
+ __func__, msm_hifi_control);
+ ucontrol->value.integer.value[0] = msm_hifi_control;
+
+ return 0;
+}
+
+static int msm_hifi_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ msm_hifi_control = ucontrol->value.integer.value[0];
+ msm_hifi_ctrl(codec);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+ SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs,
+ msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs,
+ msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs,
+ msm_slim_tx_ch_get, msm_slim_tx_ch_put),
+ SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs,
+ msm_slim_tx_ch_get, msm_slim_tx_ch_put),
+ SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs,
+ msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs,
+ msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+ SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs,
+ msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put),
+ SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs,
+ usb_audio_rx_ch_get, usb_audio_rx_ch_put),
+ SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs,
+ usb_audio_tx_ch_get, usb_audio_tx_ch_put),
+ SOC_ENUM_EXT("HDMI_RX Channels", ext_disp_rx_chs,
+ ext_disp_rx_ch_get, ext_disp_rx_ch_put),
+ SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs,
+ ext_disp_rx_ch_get, ext_disp_rx_ch_put),
+ SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs,
+ proxy_rx_ch_get, proxy_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format,
+ slim_rx_bit_format_get, slim_rx_bit_format_put),
+ SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format,
+ slim_rx_bit_format_get, slim_rx_bit_format_put),
+ SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format,
+ slim_rx_bit_format_get, slim_rx_bit_format_put),
+ SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format,
+ slim_tx_bit_format_get, slim_tx_bit_format_put),
+ SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format,
+ usb_audio_rx_format_get, usb_audio_rx_format_put),
+ SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format,
+ usb_audio_tx_format_get, usb_audio_tx_format_put),
+ SOC_ENUM_EXT("HDMI_RX Bit Format", ext_disp_rx_format,
+ ext_disp_rx_format_get, ext_disp_rx_format_put),
+ SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format,
+ ext_disp_rx_format_get, ext_disp_rx_format_put),
+ SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate,
+ slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate,
+ slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate,
+ slim_tx_sample_rate_get, slim_tx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate,
+ slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate,
+ slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+ SOC_ENUM_EXT("BT SampleRate", bt_sample_rate,
+ msm_bt_sample_rate_get,
+ msm_bt_sample_rate_put),
+ SOC_ENUM_EXT("BT SampleRate RX", bt_sample_rate_rx,
+ msm_bt_sample_rate_rx_get,
+ msm_bt_sample_rate_rx_put),
+ SOC_ENUM_EXT("BT SampleRate TX", bt_sample_rate_tx,
+ msm_bt_sample_rate_tx_get,
+ msm_bt_sample_rate_tx_put),
+ SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate,
+ usb_audio_rx_sample_rate_get,
+ usb_audio_rx_sample_rate_put),
+ SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate,
+ usb_audio_tx_sample_rate_get,
+ usb_audio_tx_sample_rate_put),
+ SOC_ENUM_EXT("HDMI_RX SampleRate", ext_disp_rx_sample_rate,
+ ext_disp_rx_sample_rate_get,
+ ext_disp_rx_sample_rate_put),
+ SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate,
+ ext_disp_rx_sample_rate_get,
+ ext_disp_rx_sample_rate_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+ tdm_rx_sample_rate_get,
+ tdm_rx_sample_rate_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_1 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_2 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_3 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format,
+ tdm_rx_format_get,
+ tdm_rx_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_1 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_2 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_3 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs,
+ tdm_rx_ch_get,
+ tdm_rx_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_1 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_2 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_3 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+ tdm_rx_sample_rate_get,
+ tdm_rx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format,
+ tdm_rx_format_get,
+ tdm_rx_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs,
+ tdm_rx_ch_get,
+ tdm_rx_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+ tdm_rx_sample_rate_get,
+ tdm_rx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format,
+ tdm_rx_format_get,
+ tdm_rx_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs,
+ tdm_rx_ch_get,
+ tdm_rx_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+ tdm_rx_sample_rate_get,
+ tdm_rx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_1 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_2 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_3 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format,
+ tdm_rx_format_get,
+ tdm_rx_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_1 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_2 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_3 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs,
+ tdm_rx_ch_get,
+ tdm_rx_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_1 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_2 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_3 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("PRI_TDM SlotNumber", tdm_slot_num,
+ tdm_slot_num_get, tdm_slot_num_put),
+ SOC_ENUM_EXT("PRI_TDM SlotWidth", tdm_slot_width,
+ tdm_slot_width_get, tdm_slot_width_put),
+ SOC_ENUM_EXT("SEC_TDM SlotNumber", tdm_slot_num,
+ tdm_slot_num_get, tdm_slot_num_put),
+ SOC_ENUM_EXT("SEC_TDM SlotWidth", tdm_slot_width,
+ tdm_slot_width_get, tdm_slot_width_put),
+ SOC_ENUM_EXT("TERT_TDM SlotNumber", tdm_slot_num,
+ tdm_slot_num_get, tdm_slot_num_put),
+ SOC_ENUM_EXT("TERT_TDM SlotWidth", tdm_slot_width,
+ tdm_slot_width_get, tdm_slot_width_put),
+ SOC_ENUM_EXT("QUAT_TDM SlotNumber", tdm_slot_num,
+ tdm_slot_num_get, tdm_slot_num_put),
+ SOC_ENUM_EXT("QUAT_TDM SlotWidth", tdm_slot_width,
+ tdm_slot_width_get, tdm_slot_width_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_0 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_1 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_2 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_3 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_4 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_5 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_6 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_7 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_0 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_1 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_2 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_3 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_4 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_5 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_6 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_7 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_0 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_1 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_2 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_3 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_4 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_5 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_6 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_7 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_0 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_1 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_2 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_3 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_4 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_5 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_6 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_7 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_0 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_1 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_2 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_3 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_4 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_5 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_6 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_7 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_0 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_1 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_2 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_3 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_4 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_5 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_6 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_7 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_0 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_1 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_2 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_3 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_4 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_5 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_6 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_7 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_0 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_1 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_2 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_3 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_4 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_5 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_6 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_7 SlotMapping",
+ SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX,
+ tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put),
+ SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate,
+ aux_pcm_rx_sample_rate_get,
+ aux_pcm_rx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate,
+ aux_pcm_rx_sample_rate_get,
+ aux_pcm_rx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate,
+ aux_pcm_rx_sample_rate_get,
+ aux_pcm_rx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate,
+ aux_pcm_rx_sample_rate_get,
+ aux_pcm_rx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("PRIM_MI2S_RX Format", mi2s_rx_format,
+ msm_mi2s_rx_format_get, msm_mi2s_rx_format_put),
+ SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format,
+ msm_mi2s_tx_format_get, msm_mi2s_tx_format_put),
+ SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format,
+ msm_mi2s_rx_format_get, msm_mi2s_rx_format_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format,
+ msm_mi2s_tx_format_get, msm_mi2s_tx_format_put),
+ SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format,
+ msm_mi2s_rx_format_get, msm_mi2s_rx_format_put),
+ SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format,
+ msm_mi2s_tx_format_get, msm_mi2s_tx_format_put),
+ SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format,
+ msm_mi2s_rx_format_get, msm_mi2s_rx_format_put),
+ SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format,
+ msm_mi2s_tx_format_get, msm_mi2s_tx_format_put),
+ SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get,
+ msm_hifi_put),
+};
+
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
+ int enable, bool dapm)
+{
+ int ret = 0;
+
+ if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+ ret = tasha_cdc_mclk_enable(codec, enable, dapm);
+ else if (!strcmp(dev_name(codec->dev), "tavil_codec"))
+ ret = tavil_cdc_mclk_enable(codec, enable);
+ else {
+ dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+ __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec,
+ int enable, bool dapm)
+{
+ int ret = 0;
+
+ if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+ ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm);
+ else {
+ dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+ __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int msm_mclk_tx_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return msm_snd_enable_codec_ext_tx_clk(codec, 1, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return msm_snd_enable_codec_ext_tx_clk(codec, 0, true);
+ }
+ return 0;
+}
+
+static int msm_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return msm_snd_enable_codec_ext_clk(codec, 1, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return msm_snd_enable_codec_ext_clk(codec, 0, true);
+ }
+ return 0;
+}
+
+static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_card *card = codec->component.card;
+ struct msm_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+
+ pr_debug("%s: msm_hifi_control = %d", __func__, msm_hifi_control);
+
+ if (!pdata || !pdata->hph_en0_gpio_p) {
+ pr_err("%s: hph_en0_gpio is invalid\n", __func__);
+ return -EINVAL;
+ }
+
+ if (msm_hifi_control != MSM_HIFI_ON) {
+ pr_debug("%s: HiFi mixer control is not set\n",
+ __func__);
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msm_cdc_pinctrl_select_active_state(pdata->hph_en0_gpio_p);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ msm_cdc_pinctrl_select_sleep_state(pdata->hph_en0_gpio_p);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget msm_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
+ msm_mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0,
+ msm_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SPK("Lineout_1 amp", NULL),
+ SND_SOC_DAPM_SPK("Lineout_3 amp", NULL),
+ SND_SOC_DAPM_SPK("Lineout_2 amp", NULL),
+ SND_SOC_DAPM_SPK("Lineout_4 amp", NULL),
+ SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event),
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic5", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic6", NULL),
+
+ SND_SOC_DAPM_MIC("Digital Mic0", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+};
+
+static inline int param_is_mask(int p)
+{
+ return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p,
+ int n)
+{
+ return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
+{
+ if (bit >= SNDRV_MASK_MAX)
+ return;
+ if (param_is_mask(n)) {
+ struct snd_mask *m = param_to_mask(p, n);
+
+ m->bits[0] = 0;
+ m->bits[1] = 0;
+ m->bits[bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
+static int msm_slim_get_ch_from_beid(int32_t be_id)
+{
+ int ch_id = 0;
+
+ switch (be_id) {
+ case MSM_BACKEND_DAI_SLIMBUS_0_RX:
+ ch_id = SLIM_RX_0;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_1_RX:
+ ch_id = SLIM_RX_1;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_2_RX:
+ ch_id = SLIM_RX_2;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_3_RX:
+ ch_id = SLIM_RX_3;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_4_RX:
+ ch_id = SLIM_RX_4;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+ ch_id = SLIM_RX_6;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_0_TX:
+ ch_id = SLIM_TX_0;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+ ch_id = SLIM_TX_3;
+ break;
+ default:
+ ch_id = SLIM_RX_0;
+ break;
+ }
+
+ return ch_id;
+}
+
+static int msm_ext_disp_get_idx_from_beid(int32_t be_id)
+{
+ int idx;
+
+ switch (be_id) {
+ case MSM_BACKEND_DAI_HDMI_RX:
+ idx = HDMI_RX_IDX;
+ break;
+ case MSM_BACKEND_DAI_DISPLAY_PORT_RX:
+ idx = DP_RX_IDX;
+ break;
+ default:
+ pr_err("%s: Incorrect ext_disp be_id %d\n", __func__, be_id);
+ idx = -EINVAL;
+ break;
+ }
+
+ return idx;
+}
+
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ int rc = 0;
+ int idx;
+ void *config = NULL;
+ struct snd_soc_codec *codec = NULL;
+
+ pr_debug("%s: format = %d, rate = %d\n",
+ __func__, params_format(params), params_rate(params));
+
+ switch (dai_link->be_id) {
+ case MSM_BACKEND_DAI_SLIMBUS_0_RX:
+ case MSM_BACKEND_DAI_SLIMBUS_1_RX:
+ case MSM_BACKEND_DAI_SLIMBUS_2_RX:
+ case MSM_BACKEND_DAI_SLIMBUS_3_RX:
+ case MSM_BACKEND_DAI_SLIMBUS_4_RX:
+ case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+ idx = msm_slim_get_ch_from_beid(dai_link->be_id);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim_rx_cfg[idx].bit_format);
+ rate->min = rate->max = slim_rx_cfg[idx].sample_rate;
+ channels->min = channels->max = slim_rx_cfg[idx].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_0_TX:
+ case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+ idx = msm_slim_get_ch_from_beid(dai_link->be_id);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim_tx_cfg[idx].bit_format);
+ rate->min = rate->max = slim_tx_cfg[idx].sample_rate;
+ channels->min = channels->max = slim_tx_cfg[idx].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_1_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim_tx_cfg[1].bit_format);
+ rate->min = rate->max = slim_tx_cfg[1].sample_rate;
+ channels->min = channels->max = slim_tx_cfg[1].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_4_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_FORMAT_S32_LE);
+ rate->min = rate->max = SAMPLING_RATE_8KHZ;
+ channels->min = channels->max = msm_vi_feed_tx_ch;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_5_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim_rx_cfg[5].bit_format);
+ rate->min = rate->max = slim_rx_cfg[5].sample_rate;
+ channels->min = channels->max = slim_rx_cfg[5].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_5_TX:
+ codec = rtd->codec;
+ rate->min = rate->max = SAMPLING_RATE_16KHZ;
+ channels->min = channels->max = 1;
+
+ config = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_SLIMBUS_SLAVE_PORT_CONFIG);
+ if (config) {
+ rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG,
+ config, SLIMBUS_5_TX);
+ if (rc)
+ pr_err("%s: Failed to set slimbus slave port config %d\n",
+ __func__, rc);
+ }
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_7_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim_rx_cfg[SLIM_RX_7].bit_format);
+ rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate;
+ channels->min = channels->max =
+ slim_rx_cfg[SLIM_RX_7].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_7_TX:
+ rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate;
+ channels->min = channels->max =
+ slim_tx_cfg[SLIM_TX_7].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_8_TX:
+ rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate;
+ channels->min = channels->max =
+ slim_tx_cfg[SLIM_TX_8].channels;
+ break;
+
+ case MSM_BACKEND_DAI_USB_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ usb_rx_cfg.bit_format);
+ rate->min = rate->max = usb_rx_cfg.sample_rate;
+ channels->min = channels->max = usb_rx_cfg.channels;
+ break;
+
+ case MSM_BACKEND_DAI_USB_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ usb_tx_cfg.bit_format);
+ rate->min = rate->max = usb_tx_cfg.sample_rate;
+ channels->min = channels->max = usb_tx_cfg.channels;
+ break;
+
+ case MSM_BACKEND_DAI_HDMI_RX:
+ case MSM_BACKEND_DAI_DISPLAY_PORT_RX:
+ idx = msm_ext_disp_get_idx_from_beid(dai_link->be_id);
+ if (IS_ERR_VALUE(idx)) {
+ pr_err("%s: Incorrect ext disp idx %d\n",
+ __func__, idx);
+ rc = idx;
+ goto done;
+ }
+
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ ext_disp_rx_cfg[idx].bit_format);
+ rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate;
+ channels->min = channels->max = ext_disp_rx_cfg[idx].channels;
+ break;
+
+ case MSM_BACKEND_DAI_AFE_PCM_RX:
+ channels->min = channels->max = proxy_rx_cfg.channels;
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ break;
+
+ case MSM_BACKEND_DAI_PRI_TDM_RX_0:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_PRI][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_PRI][TDM_0].bit_format);
+ rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_PRI_TDM_TX_0:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_PRI][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_PRI][TDM_0].bit_format);
+ rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_SEC_TDM_RX_0:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_SEC][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_SEC][TDM_0].bit_format);
+ rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_SEC_TDM_TX_0:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_SEC][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_SEC][TDM_0].bit_format);
+ rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_TERT_TDM_RX_0:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_TERT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_TERT][TDM_0].bit_format);
+ rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_TERT_TDM_TX_0:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_TERT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_TERT][TDM_0].bit_format);
+ rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_QUAT_TDM_RX_0:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format);
+ rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_QUAT_TDM_TX_0:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format);
+ rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_AUXPCM_RX:
+ rate->min = rate->max =
+ aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[PRIM_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[PRIM_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SEC_AUXPCM_RX:
+ rate->min = rate->max =
+ aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[SEC_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SEC_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[SEC_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_TERT_AUXPCM_RX:
+ rate->min = rate->max =
+ aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[TERT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_TERT_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[TERT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_QUAT_AUXPCM_RX:
+ rate->min = rate->max =
+ aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[QUAT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_QUAT_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[QUAT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_PRI_MI2S_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[PRIM_MI2S].bit_format);
+ rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[PRIM_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_PRI_MI2S_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[PRIM_MI2S].bit_format);
+ rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[PRIM_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[SEC_MI2S].bit_format);
+ rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[SEC_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[SEC_MI2S].bit_format);
+ rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[SEC_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[TERT_MI2S].bit_format);
+ rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[TERT_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[TERT_MI2S].bit_format);
+ rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[TERT_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[QUAT_MI2S].bit_format);
+ rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[QUAT_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[QUAT_MI2S].bit_format);
+ rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[QUAT_MI2S].channels;
+ break;
+
+ default:
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ break;
+ }
+
+done:
+ return rc;
+}
+
+static bool msm_swap_gnd_mic(struct snd_soc_codec *codec)
+{
+ struct snd_soc_card *card = codec->component.card;
+ struct msm_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+ int value = 0;
+
+ if (pdata->us_euro_gpio_p) {
+ value = msm_cdc_pinctrl_get_state(pdata->us_euro_gpio_p);
+ if (value)
+ msm_cdc_pinctrl_select_sleep_state(
+ pdata->us_euro_gpio_p);
+ else
+ msm_cdc_pinctrl_select_active_state(
+ pdata->us_euro_gpio_p);
+ } else if (pdata->us_euro_gpio >= 0) {
+ value = gpio_get_value_cansleep(pdata->us_euro_gpio);
+ gpio_set_value_cansleep(pdata->us_euro_gpio, !value);
+ }
+ pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value);
+ return true;
+}
+
+static int msm_afe_set_config(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ void *config_data = NULL;
+
+ if (!msm_codec_fn.get_afe_config_fn) {
+ dev_err(codec->dev, "%s: codec get afe config not init'ed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_CDC_REGISTERS_CONFIG);
+ if (config_data) {
+ ret = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0);
+ if (ret) {
+ dev_err(codec->dev,
+ "%s: Failed to set codec registers config %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_CDC_REGISTER_PAGE_CONFIG);
+ if (config_data) {
+ ret = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data,
+ 0);
+ if (ret)
+ dev_err(codec->dev,
+ "%s: Failed to set cdc register page config\n",
+ __func__);
+ }
+
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_SLIMBUS_SLAVE_CONFIG);
+ if (config_data) {
+ ret = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0);
+ if (ret) {
+ dev_err(codec->dev,
+ "%s: Failed to set slimbus slave config %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void msm_afe_clear_config(void)
+{
+ afe_clear_config(AFE_CDC_REGISTERS_CONFIG);
+ afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG);
+}
+
+static int msm_adsp_power_up_config(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ unsigned long timeout;
+ int adsp_ready = 0;
+
+ timeout = jiffies +
+ msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+
+ do {
+ if (q6core_is_adsp_ready()) {
+ pr_debug("%s: ADSP Audio is ready\n", __func__);
+ adsp_ready = 1;
+ break;
+ } else {
+ /*
+ * ADSP will be coming up after subsystem restart and
+ * it might not be fully up when the control reaches
+ * here. So, wait for 50msec before checking ADSP state
+ */
+ msleep(50);
+ }
+ } while (time_after(timeout, jiffies));
+
+ if (!adsp_ready) {
+ pr_err("%s: timed out waiting for ADSP Audio\n", __func__);
+ ret = -ETIMEDOUT;
+ goto err_fail;
+ }
+
+ ret = msm_afe_set_config(codec);
+ if (ret)
+ pr_err("%s: Failed to set AFE config. err %d\n",
+ __func__, ret);
+
+ return 0;
+
+err_fail:
+ return ret;
+}
+
+static int msm8998_notifier_service_cb(struct notifier_block *this,
+ unsigned long opcode, void *ptr)
+{
+ int ret;
+ struct snd_soc_card *card = NULL;
+ const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_codec *codec;
+
+ pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode);
+
+ switch (opcode) {
+ case AUDIO_NOTIFIER_SERVICE_DOWN:
+ /*
+ * Use flag to ignore initial boot notifications
+ * On initial boot msm_adsp_power_up_config is
+ * called on init. There is no need to clear
+ * and set the config again on initial boot.
+ */
+ if (is_initial_boot)
+ break;
+ msm_afe_clear_config();
+ break;
+ case AUDIO_NOTIFIER_SERVICE_UP:
+ if (is_initial_boot) {
+ is_initial_boot = false;
+ break;
+ }
+ if (!spdev)
+ return -EINVAL;
+
+ card = platform_get_drvdata(spdev);
+ rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+ if (!rtd) {
+ dev_err(card->dev,
+ "%s: snd_soc_get_pcm_runtime for %s failed!\n",
+ __func__, be_dl_name);
+ ret = -EINVAL;
+ goto done;
+ }
+ codec = rtd->codec;
+
+ ret = msm_adsp_power_up_config(codec);
+ if (ret < 0) {
+ dev_err(card->dev,
+ "%s: msm_adsp_power_up_config failed ret = %d!\n",
+ __func__, ret);
+ goto done;
+ }
+ break;
+ default:
+ break;
+ }
+done:
+ return NOTIFY_OK;
+}
+
+static struct notifier_block service_nb = {
+ .notifier_call = msm8998_notifier_service_cb,
+ .priority = -INT_MAX,
+};
+
+static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+ void *config_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux;
+ struct snd_card *card;
+ struct snd_info_entry *entry;
+ struct msm_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(rtd->card);
+
+ /* Codec SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156};
+ unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143};
+
+ /* Tavil Codec SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch_tavil[WCD934X_RX_MAX] = {144, 145, 146, 147, 148,
+ 149, 150, 151};
+ unsigned int tx_ch_tavil[WCD934X_TX_MAX] = {128, 129, 130, 131, 132,
+ 133, 134, 135, 136, 137,
+ 138, 139, 140, 141, 142,
+ 143};
+
+ pr_info("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+ rtd->pmdown_time = 0;
+
+ ret = snd_soc_add_codec_controls(codec, msm_snd_controls,
+ ARRAY_SIZE(msm_snd_controls));
+ if (ret < 0) {
+ pr_err("%s: add_codec_controls failed, err %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ snd_soc_dapm_new_controls(dapm, msm_dapm_widgets,
+ ARRAY_SIZE(msm_dapm_widgets));
+
+ if (!strcmp(dev_name(codec_dai->dev), "tasha_codec"))
+ snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tasha,
+ ARRAY_SIZE(wcd_audio_paths_tasha));
+ else
+ snd_soc_dapm_add_routes(dapm, wcd_audio_paths,
+ ARRAY_SIZE(wcd_audio_paths));
+
+ snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic5");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6");
+ snd_soc_dapm_ignore_suspend(dapm, "MADINPUT");
+ snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT");
+ snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2");
+ snd_soc_dapm_ignore_suspend(dapm, "EAR");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC EAR");
+ snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "HPHL");
+ snd_soc_dapm_ignore_suspend(dapm, "HPHR");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI");
+ snd_soc_dapm_ignore_suspend(dapm, "VIINPUT");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR");
+
+ if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) {
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2");
+ }
+
+ snd_soc_dapm_sync(dapm);
+
+ if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch_tavil),
+ tx_ch_tavil, ARRAY_SIZE(rx_ch_tavil),
+ rx_ch_tavil);
+ } else {
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch),
+ rx_ch);
+ }
+
+ if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+ msm_codec_fn.get_afe_config_fn = tavil_get_afe_config;
+ } else {
+ msm_codec_fn.get_afe_config_fn = tasha_get_afe_config;
+ msm_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit;
+ }
+
+ ret = msm_adsp_power_up_config(codec);
+ if (ret) {
+ pr_err("%s: Failed to set AFE config %d\n", __func__, ret);
+ goto err_afe_cfg;
+ }
+
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_AANC_VERSION);
+ if (config_data) {
+ ret = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+ if (ret) {
+ pr_err("%s: Failed to set aanc version %d\n",
+ __func__, ret);
+ goto err_afe_cfg;
+ }
+ }
+
+ if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) {
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_CDC_CLIP_REGISTERS_CONFIG);
+ if (config_data) {
+ ret = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG,
+ config_data, 0);
+ if (ret) {
+ pr_err("%s: Failed to set clip registers %d\n",
+ __func__, ret);
+ goto err_afe_cfg;
+ }
+ }
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_CLIP_BANK_SEL);
+ if (config_data) {
+ ret = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0);
+ if (ret) {
+ pr_err("%s: Failed to set AFE bank selection %d\n",
+ __func__, ret);
+ goto err_afe_cfg;
+ }
+ }
+ }
+
+ /*
+ * Send speaker configuration only for WSA8810.
+ * Defalut configuration is for WSA8815.
+ */
+ pr_debug("%s: Number of aux devices: %d\n",
+ __func__, rtd->card->num_aux_devs);
+ if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+ if (rtd->card->num_aux_devs && rtd_aux && rtd_aux->component)
+ if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
+ !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
+ tavil_set_spkr_mode(rtd->codec, SPKR_MODE_1);
+ tavil_set_spkr_gain_offset(rtd->codec,
+ RX_GAIN_OFFSET_M1P5_DB);
+ }
+ card = rtd->card->snd_card;
+ entry = snd_register_module_info(card->module, "codecs",
+ card->proc_root);
+ if (!entry) {
+ pr_debug("%s: Cannot create codecs module entry\n",
+ __func__);
+ pdata->codec_root = NULL;
+ goto done;
+ }
+ pdata->codec_root = entry;
+ tavil_codec_info_create_codec_entry(pdata->codec_root, codec);
+ } else {
+ if (rtd->card->num_aux_devs && rtd_aux && rtd_aux->component)
+ if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
+ !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
+ tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1);
+ tasha_set_spkr_gain_offset(rtd->codec,
+ RX_GAIN_OFFSET_M1P5_DB);
+ }
+ card = rtd->card->snd_card;
+ entry = snd_register_module_info(card->module, "codecs",
+ card->proc_root);
+ if (!entry) {
+ pr_debug("%s: Cannot create codecs module entry\n",
+ __func__);
+ ret = 0;
+ goto err_snd_module;
+ }
+ pdata->codec_root = entry;
+ tasha_codec_info_create_codec_entry(pdata->codec_root, codec);
+ }
+done:
+ codec_reg_done = true;
+ return 0;
+
+err_snd_module:
+err_afe_cfg:
+ return ret;
+}
+
+static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd)
+{
+ unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158};
+ unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161};
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+}
+
+static void *def_tasha_mbhc_cal(void)
+{
+ void *tasha_wcd_cal;
+ struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+ u16 *btn_high;
+
+ tasha_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS,
+ WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL);
+ if (!tasha_wcd_cal)
+ return NULL;
+
+#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tasha_wcd_cal)->X) = (Y))
+ S(v_hs_max, 1600);
+#undef S
+#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal)->X) = (Y))
+ S(num_btn, WCD_MBHC_DEF_BUTTONS);
+#undef S
+
+ btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal);
+ btn_high = ((void *)&btn_cfg->_v_btn_low) +
+ (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn);
+
+ btn_high[0] = 75;
+ btn_high[1] = 150;
+ btn_high[2] = 237;
+ btn_high[3] = 500;
+ btn_high[4] = 500;
+ btn_high[5] = 500;
+ btn_high[6] = 500;
+ btn_high[7] = 500;
+
+ return tasha_wcd_cal;
+}
+
+static void *def_tavil_mbhc_cal(void)
+{
+ void *tavil_wcd_cal;
+ struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+ u16 *btn_high;
+
+ tavil_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS,
+ WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL);
+ if (!tavil_wcd_cal)
+ return NULL;
+
+#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tavil_wcd_cal)->X) = (Y))
+ S(v_hs_max, 1600);
+#undef S
+#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal)->X) = (Y))
+ S(num_btn, WCD_MBHC_DEF_BUTTONS);
+#undef S
+
+ btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal);
+ btn_high = ((void *)&btn_cfg->_v_btn_low) +
+ (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn);
+
+ btn_high[0] = 75;
+ btn_high[1] = 150;
+ btn_high[2] = 237;
+ btn_high[3] = 500;
+ btn_high[4] = 500;
+ btn_high[5] = 500;
+ btn_high[6] = 500;
+ btn_high[7] = 500;
+
+ return tavil_wcd_cal;
+}
+
+static int msm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ int ret = 0;
+ u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+ u32 user_set_tx_ch = 0;
+ u32 rx_ch_count;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map, err:%d\n",
+ __func__, ret);
+ goto err_ch_map;
+ }
+ if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_5_RX) {
+ pr_debug("%s: rx_5_ch=%d\n", __func__,
+ slim_rx_cfg[5].channels);
+ rx_ch_count = slim_rx_cfg[5].channels;
+ } else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_2_RX) {
+ pr_debug("%s: rx_2_ch=%d\n", __func__,
+ slim_rx_cfg[2].channels);
+ rx_ch_count = slim_rx_cfg[2].channels;
+ } else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_6_RX) {
+ pr_debug("%s: rx_6_ch=%d\n", __func__,
+ slim_rx_cfg[6].channels);
+ rx_ch_count = slim_rx_cfg[6].channels;
+ } else {
+ pr_debug("%s: rx_0_ch=%d\n", __func__,
+ slim_rx_cfg[0].channels);
+ rx_ch_count = slim_rx_cfg[0].channels;
+ }
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+ rx_ch_count, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto err_ch_map;
+ }
+ } else {
+
+ pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
+ codec_dai->name, codec_dai->id, user_set_tx_ch);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map\n, err:%d\n",
+ __func__, ret);
+ goto err_ch_map;
+ }
+ /* For <codec>_tx1 case */
+ if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_0_TX)
+ user_set_tx_ch = slim_tx_cfg[0].channels;
+ /* For <codec>_tx3 case */
+ else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_1_TX)
+ user_set_tx_ch = slim_tx_cfg[1].channels;
+ else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_4_TX)
+ user_set_tx_ch = msm_vi_feed_tx_ch;
+ else
+ user_set_tx_ch = tx_ch_cnt;
+
+ pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), be_id (%d)\n",
+ __func__, slim_tx_cfg[0].channels, user_set_tx_ch,
+ tx_ch_cnt, dai_link->be_id);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ user_set_tx_ch, tx_ch, 0, 0);
+ if (ret < 0)
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ }
+
+err_ch_map:
+ return ret;
+}
+
+static int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ int ret = 0;
+ u32 tx_ch[SLIM_MAX_TX_PORTS];
+ u32 tx_ch_cnt = 0;
+ u32 user_set_tx_ch = 0;
+
+ if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) {
+ pr_err("%s: Invalid stream type %d\n",
+ __func__, substream->stream);
+ ret = -EINVAL;
+ goto err_stream_type;
+ }
+
+ pr_debug("%s: %s_tx_dai_id_%d\n", __func__,
+ codec_dai->name, codec_dai->id);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, NULL, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map\n, err:%d\n",
+ __func__, ret);
+ goto err_ch_map;
+ }
+
+ user_set_tx_ch = tx_ch_cnt;
+
+ pr_debug("%s: tx_ch_cnt(%d) be_id %d\n",
+ __func__, tx_ch_cnt, dai_link->be_id);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ user_set_tx_ch, tx_ch, 0, 0);
+ if (ret < 0)
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+err_ch_map:
+err_stream_type:
+ return ret;
+}
+
+static int msm_slimbus_2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
+ unsigned int num_tx_ch = 0;
+ unsigned int num_rx_ch = 0;
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ num_rx_ch = params_channels(params);
+ pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__,
+ codec_dai->name, codec_dai->id, num_rx_ch);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map, err:%d\n",
+ __func__, ret);
+ goto err_ch_map;
+ }
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+ num_rx_ch, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto err_ch_map;
+ }
+ } else {
+ num_tx_ch = params_channels(params);
+ pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__,
+ codec_dai->name, codec_dai->id, num_tx_ch);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map, err:%d\n",
+ __func__, ret);
+ goto err_ch_map;
+ }
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ num_tx_ch, tx_ch, 0, 0);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto err_ch_map;
+ }
+ }
+
+err_ch_map:
+ return ret;
+}
+
+static int msm_wcn_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX];
+ u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+ int ret;
+
+ dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__,
+ codec_dai->name, codec_dai->id);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret) {
+ dev_err(rtd->dev,
+ "%s: failed to get BTFM codec chan map\n, err:%d\n",
+ __func__, ret);
+ goto exit;
+ }
+
+ dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) be_id %d\n",
+ __func__, tx_ch_cnt, dai_link->be_id);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch);
+ if (ret)
+ dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+
+exit:
+ return ret;
+}
+
+static int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int index = cpu_dai->id - 1;
+
+ dev_dbg(rtd->card->dev,
+ "%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
+ __func__, substream->name, substream->stream,
+ cpu_dai->name, cpu_dai->id);
+
+ if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
+ ret = -EINVAL;
+ dev_err(rtd->card->dev,
+ "%s: CPU DAI id (%d) out of range\n",
+ __func__, cpu_dai->id);
+ goto done;
+ }
+
+ mutex_lock(&auxpcm_intf_conf[index].lock);
+ if (++auxpcm_intf_conf[index].ref_cnt == 1) {
+ if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+ mutex_lock(&mi2s_auxpcm_conf[index].lock);
+ iowrite32(1,
+ mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+ mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+ } else {
+ dev_err(rtd->card->dev,
+ "%s lpaif_tert_muxsel_virt_addr is NULL\n",
+ __func__);
+ ret = -EINVAL;
+ }
+ }
+ if (IS_ERR_VALUE(ret))
+ auxpcm_intf_conf[index].ref_cnt--;
+
+ mutex_unlock(&auxpcm_intf_conf[index].lock);
+
+done:
+ return ret;
+}
+
+static void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int index = rtd->cpu_dai->id - 1;
+
+ dev_dbg(rtd->card->dev,
+ "%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
+ __func__,
+ substream->name, substream->stream,
+ rtd->cpu_dai->name, rtd->cpu_dai->id);
+
+ if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
+ dev_err(rtd->card->dev,
+ "%s: CPU DAI id (%d) out of range\n",
+ __func__, rtd->cpu_dai->id);
+ return;
+ }
+
+ mutex_lock(&auxpcm_intf_conf[index].lock);
+ if (--auxpcm_intf_conf[index].ref_cnt == 0) {
+ if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+ mutex_lock(&mi2s_auxpcm_conf[index].lock);
+ iowrite32(0,
+ mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+ mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+ } else {
+ dev_err(rtd->card->dev,
+ "%s lpaif_tert_muxsel_virt_addr is NULL\n",
+ __func__);
+ }
+ }
+ mutex_unlock(&auxpcm_intf_conf[index].lock);
+}
+
+static int msm_get_port_id(int be_id)
+{
+ int afe_port_id;
+
+ switch (be_id) {
+ case MSM_BACKEND_DAI_PRI_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_PRI_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+ break;
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+ break;
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+ break;
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+ break;
+ default:
+ pr_err("%s: Invalid be_id: %d\n", __func__, be_id);
+ afe_port_id = -EINVAL;
+ }
+
+ return afe_port_id;
+}
+
+static u32 get_mi2s_bits_per_sample(u32 bit_format)
+{
+ u32 bit_per_sample;
+
+ switch (bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bit_per_sample = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bit_per_sample = 16;
+ break;
+ }
+
+ return bit_per_sample;
+}
+
+static void update_mi2s_clk_val(int dai_id, int stream)
+{
+ u32 bit_per_sample;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ bit_per_sample =
+ get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format);
+ mi2s_clk[dai_id].clk_freq_in_hz =
+ mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+ } else {
+ bit_per_sample =
+ get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format);
+ mi2s_clk[dai_id].clk_freq_in_hz =
+ mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+ }
+}
+
+static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int port_id = 0;
+ int index = cpu_dai->id;
+
+ port_id = msm_get_port_id(rtd->dai_link->be_id);
+ if (IS_ERR_VALUE(port_id)) {
+ dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__);
+ ret = port_id;
+ goto done;
+ }
+
+ if (enable) {
+ update_mi2s_clk_val(index, substream->stream);
+ dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__,
+ mi2s_clk[index].clk_freq_in_hz);
+ }
+
+ mi2s_clk[index].enable = enable;
+ ret = afe_set_lpass_clock_v2(port_id,
+ &mi2s_clk[index]);
+ if (ret < 0) {
+ dev_err(rtd->card->dev,
+ "%s: afe lpass clock failed for port 0x%x , err:%d\n",
+ __func__, port_id, ret);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info,
+ enum pinctrl_pin_state new_state)
+{
+ int ret = 0;
+ int curr_state = 0;
+
+ if (pinctrl_info == NULL) {
+ pr_err("%s: pinctrl_info is NULL\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (pinctrl_info->pinctrl == NULL) {
+ pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ curr_state = pinctrl_info->curr_state;
+ pinctrl_info->curr_state = new_state;
+ pr_debug("%s: curr_state = %s new_state = %s\n", __func__,
+ pin_states[curr_state], pin_states[pinctrl_info->curr_state]);
+
+ if (curr_state == pinctrl_info->curr_state) {
+ pr_debug("%s: Already in same state\n", __func__);
+ goto err;
+ }
+
+ if (curr_state != STATE_DISABLE &&
+ pinctrl_info->curr_state != STATE_DISABLE) {
+ pr_debug("%s: state already active cannot switch\n", __func__);
+ ret = -EIO;
+ goto err;
+ }
+
+ switch (pinctrl_info->curr_state) {
+ case STATE_MI2S_ACTIVE:
+ ret = pinctrl_select_state(pinctrl_info->pinctrl,
+ pinctrl_info->mi2s_active);
+ if (ret) {
+ pr_err("%s: MI2S state select failed with %d\n",
+ __func__, ret);
+ ret = -EIO;
+ goto err;
+ }
+ break;
+ case STATE_TDM_ACTIVE:
+ ret = pinctrl_select_state(pinctrl_info->pinctrl,
+ pinctrl_info->tdm_active);
+ if (ret) {
+ pr_err("%s: TDM state select failed with %d\n",
+ __func__, ret);
+ ret = -EIO;
+ goto err;
+ }
+ break;
+ case STATE_DISABLE:
+ if (curr_state == STATE_MI2S_ACTIVE) {
+ ret = pinctrl_select_state(pinctrl_info->pinctrl,
+ pinctrl_info->mi2s_disable);
+ } else {
+ ret = pinctrl_select_state(pinctrl_info->pinctrl,
+ pinctrl_info->tdm_disable);
+ }
+ if (ret) {
+ pr_err("%s: state disable failed with %d\n",
+ __func__, ret);
+ ret = -EIO;
+ goto err;
+ }
+ break;
+ default:
+ pr_err("%s: TLMM pin state is invalid\n", __func__);
+ return -EINVAL;
+ }
+
+err:
+ return ret;
+}
+
+static void msm_release_pinctrl(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+
+ if (pinctrl_info->pinctrl) {
+ devm_pinctrl_put(pinctrl_info->pinctrl);
+ pinctrl_info->pinctrl = NULL;
+ }
+}
+
+static int msm_get_pinctrl(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_pinctrl_info *pinctrl_info = NULL;
+ struct pinctrl *pinctrl;
+ int ret;
+
+ pinctrl_info = &pdata->pinctrl_info;
+
+ if (pinctrl_info == NULL) {
+ pr_err("%s: pinctrl_info is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR_OR_NULL(pinctrl)) {
+ pr_err("%s: Unable to get pinctrl handle\n", __func__);
+ return -EINVAL;
+ }
+ pinctrl_info->pinctrl = pinctrl;
+
+ /* get all the states handles from Device Tree */
+ pinctrl_info->mi2s_disable = pinctrl_lookup_state(pinctrl,
+ "quat-mi2s-sleep");
+ if (IS_ERR(pinctrl_info->mi2s_disable)) {
+ pr_err("%s: could not get mi2s_disable pinstate\n", __func__);
+ goto err;
+ }
+ pinctrl_info->mi2s_active = pinctrl_lookup_state(pinctrl,
+ "quat-mi2s-active");
+ if (IS_ERR(pinctrl_info->mi2s_active)) {
+ pr_err("%s: could not get mi2s_active pinstate\n", __func__);
+ goto err;
+ }
+ pinctrl_info->tdm_disable = pinctrl_lookup_state(pinctrl,
+ "quat-tdm-sleep");
+ if (IS_ERR(pinctrl_info->tdm_disable)) {
+ pr_err("%s: could not get tdm_disable pinstate\n", __func__);
+ goto err;
+ }
+ pinctrl_info->tdm_active = pinctrl_lookup_state(pinctrl,
+ "quat-tdm-active");
+ if (IS_ERR(pinctrl_info->tdm_active)) {
+ pr_err("%s: could not get tdm_active pinstate\n",
+ __func__);
+ goto err;
+ }
+ /* Reset the TLMM pins to a default state */
+ ret = pinctrl_select_state(pinctrl_info->pinctrl,
+ pinctrl_info->mi2s_disable);
+ if (ret != 0) {
+ pr_err("%s: Disable TLMM pins failed with %d\n",
+ __func__, ret);
+ ret = -EIO;
+ goto err;
+ }
+ pinctrl_info->curr_state = STATE_DISABLE;
+
+ return 0;
+
+err:
+ devm_pinctrl_put(pinctrl);
+ pinctrl_info->pinctrl = NULL;
+ return -EINVAL;
+}
+
+static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_PRI][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_PRI][TDM_0].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_PRI][TDM_1].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_PRI][TDM_1].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_PRI][TDM_1].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_PRI][TDM_2].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_PRI][TDM_2].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_PRI][TDM_2].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_PRI][TDM_3].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_PRI][TDM_3].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_PRI][TDM_3].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_PRI][TDM_4].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_PRI][TDM_4].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_PRI][TDM_4].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_PRI][TDM_5].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_PRI][TDM_5].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_PRI][TDM_5].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_PRI][TDM_6].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_PRI][TDM_6].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_PRI][TDM_6].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_PRI][TDM_7].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_PRI][TDM_7].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_PRI][TDM_7].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_PRI][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_PRI][TDM_0].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_PRI][TDM_1].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_PRI][TDM_1].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_PRI][TDM_1].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_PRI][TDM_2].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_PRI][TDM_2].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_PRI][TDM_2].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_PRI][TDM_3].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_PRI][TDM_3].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_PRI][TDM_3].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_PRI][TDM_4].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_PRI][TDM_4].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_PRI][TDM_4].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_PRI][TDM_5].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_PRI][TDM_5].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_PRI][TDM_5].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_PRI][TDM_6].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_PRI][TDM_6].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_PRI][TDM_6].sample_rate;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_PRI][TDM_7].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_PRI][TDM_7].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_PRI][TDM_7].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_SEC][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_SEC][TDM_0].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_SEC][TDM_1].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_SEC][TDM_1].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_SEC][TDM_1].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_SEC][TDM_2].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_SEC][TDM_2].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_SEC][TDM_2].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_SEC][TDM_3].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_SEC][TDM_3].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_SEC][TDM_3].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_SEC][TDM_4].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_SEC][TDM_4].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_SEC][TDM_4].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_SEC][TDM_5].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_SEC][TDM_5].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_SEC][TDM_5].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_SEC][TDM_6].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_SEC][TDM_6].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_SEC][TDM_6].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_SEC][TDM_7].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_SEC][TDM_7].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_SEC][TDM_7].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_SEC][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_SEC][TDM_0].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_SEC][TDM_1].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_SEC][TDM_1].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_SEC][TDM_1].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_SEC][TDM_2].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_SEC][TDM_2].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_SEC][TDM_2].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_SEC][TDM_3].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_SEC][TDM_3].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_SEC][TDM_3].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_SEC][TDM_4].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_SEC][TDM_4].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_SEC][TDM_4].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_SEC][TDM_5].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_SEC][TDM_5].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_SEC][TDM_5].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_SEC][TDM_6].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_SEC][TDM_6].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_SEC][TDM_6].sample_rate;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_SEC][TDM_7].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_SEC][TDM_7].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_SEC][TDM_7].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_TERT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_TERT][TDM_0].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_TERT][TDM_1].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_TERT][TDM_1].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_TERT][TDM_1].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_TERT][TDM_2].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_TERT][TDM_2].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_TERT][TDM_2].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_TERT][TDM_3].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_TERT][TDM_3].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_TERT][TDM_3].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_TERT][TDM_4].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_TERT][TDM_4].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_TERT][TDM_4].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_TERT][TDM_5].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_TERT][TDM_5].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_TERT][TDM_5].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_TERT][TDM_6].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_TERT][TDM_6].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_TERT][TDM_6].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_TERT][TDM_7].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_TERT][TDM_7].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_TERT][TDM_7].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_TERT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_TERT][TDM_0].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_TERT][TDM_1].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_TERT][TDM_1].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_TERT][TDM_1].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_TERT][TDM_2].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_TERT][TDM_2].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_TERT][TDM_2].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_TERT][TDM_3].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_TERT][TDM_3].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_TERT][TDM_3].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_TERT][TDM_4].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_TERT][TDM_4].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_TERT][TDM_4].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_TERT][TDM_5].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_TERT][TDM_5].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_TERT][TDM_5].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_TERT][TDM_6].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_TERT][TDM_6].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_TERT][TDM_6].sample_rate;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_TERT][TDM_7].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_TERT][TDM_7].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_TERT][TDM_7].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_1].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_QUAT][TDM_1].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_1].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_2].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_QUAT][TDM_2].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_2].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_3].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_QUAT][TDM_3].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_3].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_4].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_QUAT][TDM_4].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_4].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_5].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_QUAT][TDM_5].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_5].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_6].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_QUAT][TDM_6].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_6].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_7].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_QUAT][TDM_7].bit_format);
+ rate->min = rate->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_7].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_1].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_QUAT][TDM_1].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_1].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_2].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_QUAT][TDM_2].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_2].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_3].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_QUAT][TDM_3].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_3].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_4].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_QUAT][TDM_4].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_4].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_5].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_QUAT][TDM_5].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_5].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_6].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_QUAT][TDM_6].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_6].sample_rate;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_7].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_QUAT][TDM_7].bit_format);
+ rate->min = rate->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_7].sample_rate;
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n",
+ __func__, cpu_dai->id, channels->max, rate->max,
+ params_format(params));
+
+ return 0;
+}
+
+static unsigned int msm8998_tdm_param_set_slot_mask(u16 port_id, int slot_width,
+ int slots, int tdm_interface)
+{
+ unsigned int slot_mask = 0;
+ int upper, lower, i, j, rx_path = 0;
+ unsigned int *slot_offset;
+
+ switch (port_id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ lower = PRIMARY_TDM_RX_0;
+ upper = PRIMARY_TDM_RX_7;
+ rx_path = 1;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ lower = PRIMARY_TDM_RX_0;
+ upper = PRIMARY_TDM_RX_7;
+ rx_path = 1;
+ break;
+
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ lower = PRIMARY_TDM_TX_0;
+ upper = PRIMARY_TDM_TX_7;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ lower = PRIMARY_TDM_TX_0;
+ upper = PRIMARY_TDM_TX_7;
+ break;
+ default:
+ return slot_mask;
+ }
+
+ for (i = lower; i <= upper; i++) {
+ if (rx_path)
+ slot_offset = tdm_rx_slot_offset[tdm_interface][i];
+ else
+ slot_offset = tdm_tx_slot_offset[tdm_interface][i];
+
+ for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) {
+ if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) {
+ /*
+ * set the mask of active slot according to
+ * the offset table for the group of devices
+ */
+ slot_mask |=
+ (1 << ((slot_offset[j] * 8) / slot_width));
+ } else {
+ break;
+ }
+ }
+ }
+ return slot_mask;
+}
+
+static int msm8998_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ int channels, slot_width, slots, rate, format, tdm_interface;
+ unsigned int slot_mask;
+ unsigned int *slot_offset;
+ int offset_channels = 0;
+ int i;
+ int clk_freq;
+
+ pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+ channels = params_channels(params);
+ if (channels < 1 || channels > 32) {
+ pr_err("%s: invalid param channels %d\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+
+ format = params_format(params);
+ if (format != SNDRV_PCM_FORMAT_S32_LE &&
+ format != SNDRV_PCM_FORMAT_S24_LE &&
+ format != SNDRV_PCM_FORMAT_S16_LE) {
+ /*
+ * Up to 8 channel HW configuration should
+ * use 32 bit slot width for max support of
+ * stream bit width. (slot_width > bit_width)
+ */
+ pr_err("%s: invalid param format 0x%x\n",
+ __func__, format);
+ return -EINVAL;
+ }
+
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_0];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_1];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_2];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_3];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_4];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_5];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_6];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_7];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_0];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_1];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_2];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_3];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_4];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_5];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_6];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ slots = tdm_slot[TDM_PRI].num;
+ slot_width = tdm_slot[TDM_PRI].width;
+ slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_7];
+ tdm_interface = TDM_PRI;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_0];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_1];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_2];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_3];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_4];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_5];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_6];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_7];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_0];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_1];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_2];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_3];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_4];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_5];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_6];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ slots = tdm_slot[TDM_SEC].num;
+ slot_width = tdm_slot[TDM_SEC].width;
+ slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_7];
+ tdm_interface = TDM_SEC;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_0];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_1];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_2];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_3];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_4];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_5];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_6];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_7];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_0];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_1];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_2];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_3];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_4];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_5];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_6];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ slots = tdm_slot[TDM_TERT].num;
+ slot_width = tdm_slot[TDM_TERT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_7];
+ tdm_interface = TDM_TERT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_0];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_1];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_2];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_3];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_4];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_5];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_6];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_7];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_0];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_1];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_2];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_3];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_4];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_5];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_6];
+ tdm_interface = TDM_QUAT;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ slots = tdm_slot[TDM_QUAT].num;
+ slot_width = tdm_slot[TDM_QUAT].width;
+ slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_7];
+ tdm_interface = TDM_QUAT;
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ offset_channels++;
+ else
+ break;
+ }
+
+ if (offset_channels == 0) {
+ pr_err("%s: invalid offset_channels %d\n",
+ __func__, offset_channels);
+ return -EINVAL;
+ }
+
+ if (channels > offset_channels) {
+ pr_err("%s: channels %d exceed offset_channels %d\n",
+ __func__, channels, offset_channels);
+ return -EINVAL;
+ }
+
+ slot_mask = msm8998_tdm_param_set_slot_mask(cpu_dai->id,
+ slot_width, slots, tdm_interface);
+ pr_debug("%s: slot_mask :%x\n", __func__, slot_mask);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ /* over write slot-mask if configured for full slots */
+ if ((slots == 32) && (slot_width == 16))
+ slot_mask = 0xffffffff;
+ else if ((slots == 16) && (slot_width == 32))
+ slot_mask = 0xffff;
+ pr_debug("%s:slot_mask :%x slots %d slot_width %d\n", __func__,
+ slot_mask, slots, slot_width);
+ }
+ if (!slot_mask) {
+ pr_err("%s: invalid slot_mask 0x%x\n",
+ __func__, slot_mask);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ pr_debug("%s: slot_width %d\n", __func__, slot_width);
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ 0, NULL, channels, slot_offset);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ channels, slot_offset, 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+ ret = -EINVAL;
+ pr_err("%s: invalid use case, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ rate = params_rate(params);
+ clk_freq = rate * slot_width * slots;
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm clk, err:%d\n",
+ __func__, ret);
+ }
+
+end:
+ return ret;
+}
+
+static int msm8998_tdm_snd_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+
+ ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE);
+ if (ret)
+ pr_err("%s: TDM TLMM pinctrl set failed with %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static void msm8998_tdm_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+
+ ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE);
+ if (ret)
+ pr_err("%s: TDM TLMM pinctrl set failed with %d\n",
+ __func__, ret);
+
+}
+
+static struct snd_soc_ops msm8998_tdm_be_ops = {
+ .hw_params = msm8998_tdm_snd_hw_params,
+ .startup = msm8998_tdm_snd_startup,
+ .shutdown = msm8998_tdm_snd_shutdown
+};
+
+static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int index = cpu_dai->id;
+ unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS;
+ struct snd_soc_card *card = rtd->card;
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+ int ret_pinctrl = 0;
+
+ dev_dbg(rtd->card->dev,
+ "%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
+ __func__, substream->name, substream->stream,
+ cpu_dai->name, cpu_dai->id);
+
+ if (index < PRIM_MI2S || index > QUAT_MI2S) {
+ ret = -EINVAL;
+ dev_err(rtd->card->dev,
+ "%s: CPU DAI id (%d) out of range\n",
+ __func__, cpu_dai->id);
+ goto done;
+ }
+ if (index == QUAT_MI2S) {
+ ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE);
+ if (ret_pinctrl) {
+ pr_err("%s: MI2S TLMM pinctrl set failed with %d\n",
+ __func__, ret_pinctrl);
+ }
+ }
+
+ /*
+ * Muxtex protection in case the same MI2S
+ * interface using for both TX and RX so
+ * that the same clock won't be enable twice.
+ */
+ mutex_lock(&mi2s_intf_conf[index].lock);
+ if (++mi2s_intf_conf[index].ref_cnt == 1) {
+ /* Check if msm needs to provide the clock to the interface */
+ if (!mi2s_intf_conf[index].msm_is_mi2s_master) {
+ fmt = SND_SOC_DAIFMT_CBM_CFM;
+ mi2s_clk[index].clk_id = mi2s_ebit_clk[index];
+ }
+ ret = msm_mi2s_set_sclk(substream, true);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(rtd->card->dev,
+ "%s: afe lpass clock failed to enable MI2S clock, err:%d\n",
+ __func__, ret);
+ goto clean_up;
+ }
+ if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+ mutex_lock(&mi2s_auxpcm_conf[index].lock);
+ iowrite32(0,
+ mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+ mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+ } else {
+ dev_err(rtd->card->dev,
+ "%s lpaif_muxsel_virt_addr is NULL for dai %d\n",
+ __func__, index);
+ ret = -EINVAL;
+ goto clk_off;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n",
+ __func__, index, ret);
+ goto clk_off;
+ }
+ }
+clk_off:
+ if (IS_ERR_VALUE(ret))
+ msm_mi2s_set_sclk(substream, false);
+clean_up:
+ if (IS_ERR_VALUE(ret))
+ mi2s_intf_conf[index].ref_cnt--;
+ mutex_unlock(&mi2s_intf_conf[index].lock);
+done:
+ return ret;
+}
+
+static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ int ret;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int index = rtd->cpu_dai->id;
+ struct snd_soc_card *card = rtd->card;
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+ int ret_pinctrl = 0;
+
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+ if (index < PRIM_MI2S || index > QUAT_MI2S) {
+ pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index);
+ return;
+ }
+
+ mutex_lock(&mi2s_intf_conf[index].lock);
+ if (--mi2s_intf_conf[index].ref_cnt == 0) {
+ ret = msm_mi2s_set_sclk(substream, false);
+ if (ret < 0)
+ pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n",
+ __func__, index, ret);
+ }
+ mutex_unlock(&mi2s_intf_conf[index].lock);
+
+ if (index == QUAT_MI2S) {
+ ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_DISABLE);
+ if (ret_pinctrl)
+ pr_err("%s: MI2S TLMM pinctrl set failed with %d\n",
+ __func__, ret_pinctrl);
+ }
+}
+
+static struct snd_soc_ops msm_mi2s_be_ops = {
+ .startup = msm_mi2s_snd_startup,
+ .shutdown = msm_mi2s_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_aux_pcm_be_ops = {
+ .startup = msm_aux_pcm_snd_startup,
+ .shutdown = msm_aux_pcm_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_be_ops = {
+ .hw_params = msm_snd_hw_params,
+};
+
+static struct snd_soc_ops msm_cpe_ops = {
+ .hw_params = msm_snd_cpe_hw_params,
+};
+
+static struct snd_soc_ops msm_slimbus_2_be_ops = {
+ .hw_params = msm_slimbus_2_hw_params,
+};
+
+static struct snd_soc_ops msm_wcn_ops = {
+ .hw_params = msm_wcn_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm_common_dai_links[] = {
+ /* FrontEnd DAI Links */
+ {
+ .name = MSM_DAILINK_NAME(Media1),
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+ {
+ .name = MSM_DAILINK_NAME(Media2),
+ .stream_name = "MultiMedia2",
+ .cpu_dai_name = "MultiMedia2",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+ },
+ {
+ .name = "VoiceMMode1",
+ .stream_name = "VoiceMMode1",
+ .cpu_dai_name = "VoiceMMode1",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE1,
+ },
+ {
+ .name = "MSM VoIP",
+ .stream_name = "VoIP",
+ .cpu_dai_name = "VoIP",
+ .platform_name = "msm-voip-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_VOIP,
+ },
+ {
+ .name = MSM_DAILINK_NAME(ULL),
+ .stream_name = "MultiMedia3",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-dsp.2",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+ /* Hostless PCM purpose */
+ {
+ .name = "SLIMBUS_0 Hostless",
+ .stream_name = "SLIMBUS_0 Hostless",
+ .cpu_dai_name = "SLIMBUS0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "MSM AFE-PCM RX",
+ .stream_name = "AFE-PROXY RX",
+ .cpu_dai_name = "msm-dai-q6-dev.241",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .platform_name = "msm-pcm-afe",
+ .dpcm_playback = 1,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = "MSM AFE-PCM TX",
+ .stream_name = "AFE-PROXY TX",
+ .cpu_dai_name = "msm-dai-q6-dev.240",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .platform_name = "msm-pcm-afe",
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = MSM_DAILINK_NAME(Compress1),
+ .stream_name = "Compress1",
+ .cpu_dai_name = "MultiMedia4",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+ },
+ {
+ .name = "AUXPCM Hostless",
+ .stream_name = "AUXPCM Hostless",
+ .cpu_dai_name = "AUXPCM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_1 Hostless",
+ .stream_name = "SLIMBUS_1 Hostless",
+ .cpu_dai_name = "SLIMBUS1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_3 Hostless",
+ .stream_name = "SLIMBUS_3 Hostless",
+ .cpu_dai_name = "SLIMBUS3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_4 Hostless",
+ .stream_name = "SLIMBUS_4 Hostless",
+ .cpu_dai_name = "SLIMBUS4_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = MSM_DAILINK_NAME(LowLatency),
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+ },
+ {
+ .name = "Listen 1 Audio Service",
+ .stream_name = "Listen 1 Audio Service",
+ .cpu_dai_name = "LSM1",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM1,
+ },
+ /* Multiple Tunnel instances */
+ {
+ .name = MSM_DAILINK_NAME(Compress2),
+ .stream_name = "Compress2",
+ .cpu_dai_name = "MultiMedia7",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+ },
+ {
+ .name = MSM_DAILINK_NAME(Compress3),
+ .stream_name = "Compress3",
+ .cpu_dai_name = "MultiMedia10",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10,
+ },
+ {
+ .name = MSM_DAILINK_NAME(ULL_NOIRQ),
+ .stream_name = "MM_NOIRQ",
+ .cpu_dai_name = "MultiMedia8",
+ .platform_name = "msm-pcm-dsp-noirq",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+ },
+ /* HDMI Hostless */
+ {
+ .name = "HDMI_RX_HOSTLESS",
+ .stream_name = "HDMI_RX_HOSTLESS",
+ .cpu_dai_name = "HDMI_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "VoiceMMode2",
+ .stream_name = "VoiceMMode2",
+ .cpu_dai_name = "VoiceMMode2",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE2,
+ },
+ /* LSM FE */
+ {
+ .name = "Listen 2 Audio Service",
+ .stream_name = "Listen 2 Audio Service",
+ .cpu_dai_name = "LSM2",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM2,
+ },
+ {
+ .name = "Listen 3 Audio Service",
+ .stream_name = "Listen 3 Audio Service",
+ .cpu_dai_name = "LSM3",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM3,
+ },
+ {
+ .name = "Listen 4 Audio Service",
+ .stream_name = "Listen 4 Audio Service",
+ .cpu_dai_name = "LSM4",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM4,
+ },
+ {
+ .name = "Listen 5 Audio Service",
+ .stream_name = "Listen 5 Audio Service",
+ .cpu_dai_name = "LSM5",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM5,
+ },
+ {
+ .name = "Listen 6 Audio Service",
+ .stream_name = "Listen 6 Audio Service",
+ .cpu_dai_name = "LSM6",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM6,
+ },
+ {
+ .name = "Listen 7 Audio Service",
+ .stream_name = "Listen 7 Audio Service",
+ .cpu_dai_name = "LSM7",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM7,
+ },
+ {
+ .name = "Listen 8 Audio Service",
+ .stream_name = "Listen 8 Audio Service",
+ .cpu_dai_name = "LSM8",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM8,
+ },
+ {
+ .name = MSM_DAILINK_NAME(Media9),
+ .stream_name = "MultiMedia9",
+ .cpu_dai_name = "MultiMedia9",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+ },
+ {
+ .name = MSM_DAILINK_NAME(Compress4),
+ .stream_name = "Compress4",
+ .cpu_dai_name = "MultiMedia11",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11,
+ },
+ {
+ .name = MSM_DAILINK_NAME(Compress5),
+ .stream_name = "Compress5",
+ .cpu_dai_name = "MultiMedia12",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12,
+ },
+ {
+ .name = MSM_DAILINK_NAME(Compress6),
+ .stream_name = "Compress6",
+ .cpu_dai_name = "MultiMedia13",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13,
+ },
+ {
+ .name = MSM_DAILINK_NAME(Compress7),
+ .stream_name = "Compress7",
+ .cpu_dai_name = "MultiMedia14",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+ },
+ {
+ .name = MSM_DAILINK_NAME(Compress8),
+ .stream_name = "Compress8",
+ .cpu_dai_name = "MultiMedia15",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
+ },
+ {
+ .name = MSM_DAILINK_NAME(ULL_NOIRQ_2),
+ .stream_name = "MM_NOIRQ_2",
+ .cpu_dai_name = "MultiMedia16",
+ .platform_name = "msm-pcm-dsp-noirq",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16,
+ },
+ {
+ .name = "SLIMBUS_8 Hostless",
+ .stream_name = "SLIMBUS8_HOSTLESS Capture",
+ .cpu_dai_name = "SLIMBUS8_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+};
+
+static struct snd_soc_dai_link msm_tasha_fe_dai_links[] = {
+ {
+ .name = LPASS_BE_SLIMBUS_4_TX,
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_vifeedback",
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ },
+ /* Ultrasound RX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Playback",
+ .stream_name = "SLIMBUS_2 Hostless Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16388",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm_slimbus_2_be_ops,
+ },
+ /* Ultrasound TX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Capture",
+ .stream_name = "SLIMBUS_2 Hostless Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16389",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm_slimbus_2_be_ops,
+ },
+ /* CPE LSM direct dai-link */
+ {
+ .name = "CPE Listen service",
+ .stream_name = "CPE Listen Audio Service",
+ .cpu_dai_name = "msm-dai-slim",
+ .platform_name = "msm-cpe-lsm",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "tasha_mad1",
+ .codec_name = "tasha_codec",
+ .ops = &msm_cpe_ops,
+ },
+ {
+ .name = "SLIMBUS_6 Hostless Playback",
+ .stream_name = "SLIMBUS_6 Hostless",
+ .cpu_dai_name = "SLIMBUS6_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ /* CPE LSM EC PP direct dai-link */
+ {
+ .name = "CPE Listen service ECPP",
+ .stream_name = "CPE Listen Audio Service ECPP",
+ .cpu_dai_name = "CPE_LSM_NOHOST",
+ .platform_name = "msm-cpe-lsm.3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "tasha_cpe",
+ .codec_name = "tasha_codec",
+ },
+};
+
+static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = {
+ {
+ .name = LPASS_BE_SLIMBUS_4_TX,
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_vifeedback",
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ },
+ /* Ultrasound RX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Playback",
+ .stream_name = "SLIMBUS_2 Hostless Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16388",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm_slimbus_2_be_ops,
+ },
+ /* Ultrasound TX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Capture",
+ .stream_name = "SLIMBUS_2 Hostless Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16389",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_tx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm_slimbus_2_be_ops,
+ },
+};
+
+static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = {
+ {
+ .name = MSM_DAILINK_NAME(ASM Loopback),
+ .stream_name = "MultiMedia6",
+ .cpu_dai_name = "MultiMedia6",
+ .platform_name = "msm-pcm-loopback",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,
+ },
+ {
+ .name = "USB Audio Hostless",
+ .stream_name = "USB Audio Hostless",
+ .cpu_dai_name = "USBAUDIO_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = MSM_DAILINK_NAME(Transcode Loopback Playback),
+ .stream_name = "Transcode Loopback Playback",
+ .cpu_dai_name = "MultiMedia26",
+ .platform_name = "msm-transcode-loopback",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA26,
+ },
+ {
+ .name = MSM_DAILINK_NAME(Transcode Loopback Capture),
+ .stream_name = "Transcode Loopback Capture",
+ .cpu_dai_name = "MultiMedia27",
+ .platform_name = "msm-transcode-loopback",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA27,
+ },
+ {
+ .name = "MultiMedia21",
+ .stream_name = "MultiMedia21",
+ .cpu_dai_name = "MultiMedia21",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA21,
+ },
+ {
+ .name = "MultiMedia22",
+ .stream_name = "MultiMedia22",
+ .cpu_dai_name = "MultiMedia22",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA22,
+ },
+ {
+ .name = "MultiMedia23",
+ .stream_name = "MultiMedia23",
+ .cpu_dai_name = "MultiMedia23",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA23,
+ },
+ {
+ .name = "MultiMedia24",
+ .stream_name = "MultiMedia24",
+ .cpu_dai_name = "MultiMedia24",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA24,
+ },
+ {
+ .name = "MultiMedia25",
+ .stream_name = "MultiMedia25",
+ .cpu_dai_name = "MultiMedia25",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA25,
+ },
+};
+
+static struct snd_soc_dai_link msm_ext_common_fe_dai[] = {
+ /* Extra FrontEnd DAI Links after compress fe*/
+ {/* hw:x,66 */
+ .name = MSM_DAILINK_NAME(Media30),
+ .stream_name = "MultiMedia30",
+ .cpu_dai_name = "MultiMedia30",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA30,
+ },
+ {/* hw:x,67 */
+ .name = MSM_DAILINK_NAME(Media31),
+ .stream_name = "MultiMedia31",
+ .cpu_dai_name = "MultiMedia31",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA31,
+ },
+ {/* hw:x,68 */
+ .name = MSM_DAILINK_NAME(Media32),
+ .stream_name = "MultiMedia32",
+ .cpu_dai_name = "MultiMedia32",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA32,
+ },
+ {/* hw:x,69 */
+ .name = MSM_DAILINK_NAME(Media33),
+ .stream_name = "MultiMedia33",
+ .cpu_dai_name = "MultiMedia33",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA33,
+ },
+};
+
+static struct snd_soc_dai_link msm_common_be_dai_links[] = {
+ /* Backend AFE DAI Links */
+ {
+ .name = LPASS_BE_AFE_PCM_RX,
+ .stream_name = "AFE Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.224",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_AFE_PCM_TX,
+ .stream_name = "AFE Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.225",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Uplink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_TX,
+ .stream_name = "Voice Uplink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32772",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Downlink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_RX,
+ .stream_name = "Voice Downlink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32771",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE_PLAYBACK_TX,
+ .stream_name = "Voice Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32773",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music 2 BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE2_PLAYBACK_TX,
+ .stream_name = "Voice2 Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32770",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Proxy Tx BACK END DAI Link */
+ {
+ .name = LPASS_BE_PROXY_TX,
+ .stream_name = "Proxy Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.8195",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PROXY_TX,
+ .ignore_suspend = 1,
+ },
+ /* Proxy Rx BACK END DAI Link */
+ {
+ .name = LPASS_BE_PROXY_RX,
+ .stream_name = "Proxy Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.8194",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PROXY_RX,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_USB_AUDIO_RX,
+ .stream_name = "USB Audio Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.28672",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_USB_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_USB_AUDIO_TX,
+ .stream_name = "USB Audio Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.28673",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_USB_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_RX_0,
+ .stream_name = "Primary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36864",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_0,
+ .stream_name = "Primary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36865",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_RX_0,
+ .stream_name = "Secondary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36880",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_0,
+ .stream_name = "Secondary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36881",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_0,
+ .stream_name = "Tertiary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36896",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_0,
+ .stream_name = "Tertiary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36897",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_0,
+ .stream_name = "Quaternary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36912",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_0,
+ .stream_name = "Quaternary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36913",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_tasha_be_dai_links[] = {
+ {
+ .name = LPASS_BE_SLIMBUS_0_RX,
+ .stream_name = "Slimbus Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16384",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ .init = &msm_audrx_init,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_0_TX,
+ .stream_name = "Slimbus Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16385",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ops = &msm_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_RX,
+ .stream_name = "Slimbus1 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16386",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_TX,
+ .stream_name = "Slimbus1 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16387",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx3",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_RX,
+ .stream_name = "Slimbus3 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16390",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_TX,
+ .stream_name = "Slimbus3 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16391",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_4_RX,
+ .stream_name = "Slimbus4 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16392",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_5_RX,
+ .stream_name = "Slimbus5 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16394",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx3",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ /* MAD BE */
+ {
+ .name = LPASS_BE_SLIMBUS_5_TX,
+ .stream_name = "Slimbus5 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16395",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mad1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_6_RX,
+ .stream_name = "Slimbus6 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16396",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx4",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ /* Slimbus VI Recording */
+ {
+ .name = LPASS_BE_SLIMBUS_TX_VI,
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_vifeedback",
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .ignore_pmdown_time = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_tavil_be_dai_links[] = {
+ {
+ .name = LPASS_BE_SLIMBUS_0_RX,
+ .stream_name = "Slimbus Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16384",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ .init = &msm_audrx_init,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_0_TX,
+ .stream_name = "Slimbus Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16385",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ops = &msm_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_RX,
+ .stream_name = "Slimbus1 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16386",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_TX,
+ .stream_name = "Slimbus1 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16387",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_tx3",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_2_RX,
+ .stream_name = "Slimbus2 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16388",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx2",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_RX,
+ .stream_name = "Slimbus3 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16390",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_TX,
+ .stream_name = "Slimbus3 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16391",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_4_RX,
+ .stream_name = "Slimbus4 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16392",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_5_RX,
+ .stream_name = "Slimbus5 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16394",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx3",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ /* MAD BE */
+ {
+ .name = LPASS_BE_SLIMBUS_5_TX,
+ .stream_name = "Slimbus5 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16395",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_mad1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_6_RX,
+ .stream_name = "Slimbus6 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16396",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx4",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ /* Slimbus VI Recording */
+ {
+ .name = LPASS_BE_SLIMBUS_TX_VI,
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_vifeedback",
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .ignore_pmdown_time = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_wcn_be_dai_links[] = {
+ {
+ .name = LPASS_BE_SLIMBUS_7_RX,
+ .stream_name = "Slimbus7 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16398",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "btfmslim_slave",
+ /* BT codec driver determines capabilities based on
+ * dai name, bt codecdai name should always contains
+ * supported usecase information
+ */
+ .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_wcn_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_7_TX,
+ .stream_name = "Slimbus7 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16399",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "btfmslim_slave",
+ .codec_dai_name = "btfm_bt_sco_slim_tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_wcn_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_8_TX,
+ .stream_name = "Slimbus8 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16401",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "btfmslim_slave",
+ .codec_dai_name = "btfm_fm_slim_tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .init = &msm_wcn_init,
+ .ops = &msm_wcn_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link ext_disp_be_dai_link[] = {
+ /* HDMI BACK END DAI Link */
+ {
+ .name = LPASS_BE_HDMI,
+ .stream_name = "HDMI Playback",
+ .cpu_dai_name = "msm-dai-q6-hdmi.8",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-ext-disp-audio-codec-rx",
+ .codec_dai_name = "msm_hdmi_audio_codec_rx_dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_HDMI_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ /* DISP PORT BACK END DAI Link */
+ {
+ .name = LPASS_BE_DISPLAY_PORT,
+ .stream_name = "Display Port Playback",
+ .cpu_dai_name = "msm-dai-q6-dp.24608",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-ext-disp-audio-codec-rx",
+ .codec_dai_name = "msm_dp_audio_codec_rx_dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = {
+ {
+ .name = LPASS_BE_PRI_MI2S_RX,
+ .stream_name = "Primary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_MI2S_TX,
+ .stream_name = "Primary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_RX,
+ .stream_name = "Secondary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_TX,
+ .stream_name = "Secondary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_RX,
+ .stream_name = "Tertiary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_TX,
+ .stream_name = "Tertiary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_RX,
+ .stream_name = "Quaternary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_TX,
+ .stream_name = "Quaternary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = {
+ /* Primary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Secondary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_SEC_AUXPCM_RX,
+ .stream_name = "Sec AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_SEC_AUXPCM_TX,
+ .stream_name = "Sec AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Tertiary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_TERT_AUXPCM_RX,
+ .stream_name = "Tert AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_TERT_AUXPCM_TX,
+ .stream_name = "Tert AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Quaternary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_QUAT_AUXPCM_RX,
+ .stream_name = "Quat AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.4",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_QUAT_AUXPCM_TX,
+ .stream_name = "Quat AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.4",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+};
+
+static struct snd_soc_dai_link msm_ext_tdm_tx_group_be_dai[] = {
+ {
+ .name = LPASS_BE_PRI_TDM_TX_1,
+ .stream_name = "Primary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36867",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_2,
+ .stream_name = "Primary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36869",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_3,
+ .stream_name = "Primary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36871",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_1,
+ .stream_name = "Quaternary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36915",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_2,
+ .stream_name = "Quaternary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36917",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_3,
+ .stream_name = "Quaternary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36919",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &msm8998_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_tasha_dai_links[
+ ARRAY_SIZE(msm_common_dai_links) +
+ ARRAY_SIZE(msm_tasha_fe_dai_links) +
+ ARRAY_SIZE(msm_common_misc_fe_dai_links) +
+ ARRAY_SIZE(msm_common_be_dai_links) +
+ ARRAY_SIZE(msm_tasha_be_dai_links) +
+ ARRAY_SIZE(msm_wcn_be_dai_links) +
+ ARRAY_SIZE(ext_disp_be_dai_link) +
+ ARRAY_SIZE(msm_mi2s_be_dai_links) +
+ ARRAY_SIZE(msm_auxpcm_be_dai_links) +
+ ARRAY_SIZE(msm_ext_tdm_tx_group_be_dai) +
+ ARRAY_SIZE(msm_ext_common_fe_dai)];
+
+static struct snd_soc_dai_link msm_tavil_dai_links[
+ ARRAY_SIZE(msm_common_dai_links) +
+ ARRAY_SIZE(msm_tavil_fe_dai_links) +
+ ARRAY_SIZE(msm_common_misc_fe_dai_links) +
+ ARRAY_SIZE(msm_common_be_dai_links) +
+ ARRAY_SIZE(msm_tavil_be_dai_links) +
+ ARRAY_SIZE(msm_wcn_be_dai_links) +
+ ARRAY_SIZE(ext_disp_be_dai_link) +
+ ARRAY_SIZE(msm_mi2s_be_dai_links) +
+ ARRAY_SIZE(msm_auxpcm_be_dai_links)];
+
+static int msm_snd_card_late_probe(struct snd_soc_card *card)
+{
+ const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret = 0;
+ void *mbhc_calibration;
+
+ rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+ if (!rtd) {
+ dev_err(card->dev,
+ "%s: snd_soc_get_pcm_runtime for %s failed!\n",
+ __func__, be_dl_name);
+ ret = -EINVAL;
+ goto err_pcm_runtime;
+ }
+
+ mbhc_calibration = def_tasha_mbhc_cal();
+ if (!mbhc_calibration) {
+ ret = -ENOMEM;
+ goto err_mbhc_cal;
+ }
+ wcd_mbhc_cfg.calibration = mbhc_calibration;
+ ret = tasha_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg);
+ if (ret) {
+ dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n",
+ __func__, ret);
+ goto err_hs_detect;
+ }
+ return 0;
+
+err_hs_detect:
+ kfree(mbhc_calibration);
+err_mbhc_cal:
+err_pcm_runtime:
+ return ret;
+}
+
+static int msm_snd_card_tavil_late_probe(struct snd_soc_card *card)
+{
+ const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret = 0;
+ void *mbhc_calibration;
+
+ rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+ if (!rtd) {
+ dev_err(card->dev,
+ "%s: snd_soc_get_pcm_runtime for %s failed!\n",
+ __func__, be_dl_name);
+ ret = -EINVAL;
+ goto err_pcm_runtime;
+ }
+
+ mbhc_calibration = def_tavil_mbhc_cal();
+ if (!mbhc_calibration) {
+ ret = -ENOMEM;
+ goto err_mbhc_cal;
+ }
+ wcd_mbhc_cfg.calibration = mbhc_calibration;
+ ret = tavil_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg);
+ if (ret) {
+ dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n",
+ __func__, ret);
+ goto err_hs_detect;
+ }
+ return 0;
+
+err_hs_detect:
+ kfree(mbhc_calibration);
+err_mbhc_cal:
+err_pcm_runtime:
+ return ret;
+}
+
+struct snd_soc_card snd_soc_card_tasha_msm = {
+ .name = "msm8998-tasha-snd-card",
+ .late_probe = msm_snd_card_late_probe,
+};
+
+struct snd_soc_card snd_soc_card_tavil_msm = {
+ .name = "msm8998-tavil-snd-card",
+ .late_probe = msm_snd_card_tavil_late_probe,
+};
+
+static int msm_populate_dai_link_component_of_node(
+ struct snd_soc_card *card)
+{
+ int i, index, ret = 0;
+ struct device *cdev = card->dev;
+ struct snd_soc_dai_link *dai_link = card->dai_link;
+ struct device_node *np;
+
+ if (!cdev) {
+ pr_err("%s: Sound card device memory NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < card->num_links; i++) {
+ if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node)
+ continue;
+
+ /* populate platform_of_node for snd card dai links */
+ if (dai_link[i].platform_name &&
+ !dai_link[i].platform_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-platform-names",
+ dai_link[i].platform_name);
+ if (index < 0) {
+ pr_err("%s: No match found for platform name: %s\n",
+ __func__, dai_link[i].platform_name);
+ ret = index;
+ goto err;
+ }
+ np = of_parse_phandle(cdev->of_node, "asoc-platform",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for platform %s, index %d failed\n",
+ __func__, dai_link[i].platform_name,
+ index);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].platform_of_node = np;
+ dai_link[i].platform_name = NULL;
+ }
+
+ /* populate cpu_of_node for snd card dai links */
+ if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-cpu-names",
+ dai_link[i].cpu_dai_name);
+ if (index >= 0) {
+ np = of_parse_phandle(cdev->of_node, "asoc-cpu",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for cpu dai %s failed\n",
+ __func__,
+ dai_link[i].cpu_dai_name);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].cpu_of_node = np;
+ dai_link[i].cpu_dai_name = NULL;
+ }
+ }
+
+ /* populate codec_of_node for snd card dai links */
+ if (dai_link[i].codec_name && !dai_link[i].codec_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-codec-names",
+ dai_link[i].codec_name);
+ if (index < 0)
+ continue;
+ np = of_parse_phandle(cdev->of_node, "asoc-codec",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for codec %s failed\n",
+ __func__, dai_link[i].codec_name);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].codec_of_node = np;
+ dai_link[i].codec_name = NULL;
+ }
+ }
+
+err:
+ return ret;
+}
+
+static int msm_prepare_us_euro(struct snd_soc_card *card)
+{
+ struct msm_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ if (pdata->us_euro_gpio >= 0) {
+ dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__,
+ pdata->us_euro_gpio);
+ ret = gpio_request(pdata->us_euro_gpio, "TASHA_CODEC_US_EURO");
+ if (ret) {
+ dev_err(card->dev,
+ "%s: Failed to request codec US/EURO gpio %d error %d\n",
+ __func__, pdata->us_euro_gpio, ret);
+ }
+ }
+
+ return ret;
+}
+
+static int msm_audrx_stub_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ ret = snd_soc_add_codec_controls(codec, msm_snd_controls,
+ ARRAY_SIZE(msm_snd_controls));
+ if (ret < 0) {
+ dev_err(codec->dev, "%s: add_codec_controls failed, err%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ snd_soc_dapm_new_controls(dapm, msm_dapm_widgets,
+ ARRAY_SIZE(msm_dapm_widgets));
+
+ return 0;
+}
+
+static int msm_snd_stub_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ int ret = 0;
+ unsigned int rx_ch[] = {144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156};
+ unsigned int tx_ch[] = {128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143};
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+ slim_rx_cfg[0].channels,
+ rx_ch);
+ if (ret < 0)
+ pr_err("%s: RX failed to set cpu chan map error %d\n",
+ __func__, ret);
+ } else {
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ slim_tx_cfg[0].channels,
+ tx_ch, 0, 0);
+ if (ret < 0)
+ pr_err("%s: TX failed to set cpu chan map error %d\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static struct snd_soc_ops msm_stub_be_ops = {
+ .hw_params = msm_snd_stub_hw_params,
+};
+
+static struct snd_soc_dai_link msm_stub_fe_dai_links[] = {
+
+ /* FrontEnd DAI Links */
+ {
+ .name = "MSMSTUB Media1",
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+};
+
+static struct snd_soc_dai_link msm_stub_be_dai_links[] = {
+
+ /* Backend DAI Links */
+ {
+ .name = LPASS_BE_SLIMBUS_0_RX,
+ .stream_name = "Slimbus Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16384",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ .init = &msm_audrx_stub_init,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1, /* dai link has playback support */
+ .ignore_suspend = 1,
+ .ops = &msm_stub_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_0_TX,
+ .stream_name = "Slimbus Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16385",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ops = &msm_stub_be_ops,
+ },
+};
+
+static struct snd_soc_dai_link msm_stub_dai_links[
+ ARRAY_SIZE(msm_stub_fe_dai_links) +
+ ARRAY_SIZE(msm_stub_be_dai_links)];
+
+struct snd_soc_card snd_soc_card_stub_msm = {
+ .name = "msm8998-stub-snd-card",
+};
+
+static const struct of_device_id msm8998_asoc_machine_of_match[] = {
+ { .compatible = "qcom,msm8998-asoc-snd-tasha",
+ .data = "tasha_codec"},
+ { .compatible = "qcom,msm8998-asoc-snd-tavil",
+ .data = "tavil_codec"},
+ { .compatible = "qcom,msm8998-asoc-snd-stub",
+ .data = "stub_codec"},
+ {},
+};
+
+static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
+{
+ struct snd_soc_card *card = NULL;
+ struct snd_soc_dai_link *dailink;
+ int len_1, len_2, len_3, len_4, len3_t;
+ int total_links;
+ const struct of_device_id *match;
+
+ match = of_match_node(msm8998_asoc_machine_of_match, dev->of_node);
+ if (!match) {
+ dev_err(dev, "%s: No DT match found for sound card\n",
+ __func__);
+ return NULL;
+ }
+
+ if (!strcmp(match->data, "tasha_codec")) {
+ card = &snd_soc_card_tasha_msm;
+ len_1 = ARRAY_SIZE(msm_common_dai_links);
+ len_2 = len_1 + ARRAY_SIZE(msm_tasha_fe_dai_links);
+ len_3 = len_2 + ARRAY_SIZE(msm_common_misc_fe_dai_links);
+ len3_t = len_3 + ARRAY_SIZE(msm_ext_common_fe_dai);
+ len_4 = len3_t + ARRAY_SIZE(msm_common_be_dai_links);
+ total_links = len_4 + ARRAY_SIZE(msm_tasha_be_dai_links);
+ memcpy(msm_tasha_dai_links,
+ msm_common_dai_links,
+ sizeof(msm_common_dai_links));
+ memcpy(msm_tasha_dai_links + len_1,
+ msm_tasha_fe_dai_links,
+ sizeof(msm_tasha_fe_dai_links));
+ memcpy(msm_tasha_dai_links + len_2,
+ msm_common_misc_fe_dai_links,
+ sizeof(msm_common_misc_fe_dai_links));
+ memcpy(msm_tasha_dai_links + len_3,
+ msm_ext_common_fe_dai,
+ sizeof(msm_ext_common_fe_dai));
+ memcpy(msm_tasha_dai_links + len3_t,
+ msm_common_be_dai_links,
+ sizeof(msm_common_be_dai_links));
+ memcpy(msm_tasha_dai_links + len_4,
+ msm_tasha_be_dai_links,
+ sizeof(msm_tasha_be_dai_links));
+
+ if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) {
+ dev_dbg(dev, "%s(): WCN BTFM support present\n",
+ __func__);
+ memcpy(msm_tasha_dai_links + total_links,
+ msm_wcn_be_dai_links,
+ sizeof(msm_wcn_be_dai_links));
+ total_links += ARRAY_SIZE(msm_wcn_be_dai_links);
+ }
+
+ if (of_property_read_bool(dev->of_node,
+ "qcom,ext-disp-audio-rx")) {
+ dev_dbg(dev, "%s(): External display audio support present\n",
+ __func__);
+ memcpy(msm_tasha_dai_links + total_links,
+ ext_disp_be_dai_link,
+ sizeof(ext_disp_be_dai_link));
+ total_links += ARRAY_SIZE(ext_disp_be_dai_link);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,mi2s-audio-intf")) {
+ memcpy(msm_tasha_dai_links + total_links,
+ msm_mi2s_be_dai_links,
+ sizeof(msm_mi2s_be_dai_links));
+ total_links += ARRAY_SIZE(msm_mi2s_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,auxpcm-audio-intf")) {
+ memcpy(msm_tasha_dai_links + total_links,
+ msm_auxpcm_be_dai_links,
+ sizeof(msm_auxpcm_be_dai_links));
+ total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,tdm-tx-group-enable")) {
+ dev_dbg(dev, "%s(): TDM Tx group enable\n", __func__);
+ memcpy(msm_tasha_dai_links + total_links,
+ msm_ext_tdm_tx_group_be_dai,
+ sizeof(msm_ext_tdm_tx_group_be_dai));
+ total_links += ARRAY_SIZE(msm_ext_tdm_tx_group_be_dai);
+ }
+ dailink = msm_tasha_dai_links;
+ } else if (!strcmp(match->data, "tavil_codec")) {
+ card = &snd_soc_card_tavil_msm;
+ len_1 = ARRAY_SIZE(msm_common_dai_links);
+ len_2 = len_1 + ARRAY_SIZE(msm_tavil_fe_dai_links);
+ len_3 = len_2 + ARRAY_SIZE(msm_common_misc_fe_dai_links);
+ len_4 = len_3 + ARRAY_SIZE(msm_common_be_dai_links);
+ total_links = len_4 + ARRAY_SIZE(msm_tavil_be_dai_links);
+ memcpy(msm_tavil_dai_links,
+ msm_common_dai_links,
+ sizeof(msm_common_dai_links));
+ memcpy(msm_tavil_dai_links + len_1,
+ msm_tavil_fe_dai_links,
+ sizeof(msm_tavil_fe_dai_links));
+ memcpy(msm_tavil_dai_links + len_2,
+ msm_common_misc_fe_dai_links,
+ sizeof(msm_common_misc_fe_dai_links));
+ memcpy(msm_tavil_dai_links + len_3,
+ msm_common_be_dai_links,
+ sizeof(msm_common_be_dai_links));
+ memcpy(msm_tavil_dai_links + len_4,
+ msm_tavil_be_dai_links,
+ sizeof(msm_tavil_be_dai_links));
+
+ if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) {
+ dev_dbg(dev, "%s(): WCN BTFM support present\n",
+ __func__);
+ memcpy(msm_tavil_dai_links + total_links,
+ msm_wcn_be_dai_links,
+ sizeof(msm_wcn_be_dai_links));
+ total_links += ARRAY_SIZE(msm_wcn_be_dai_links);
+ }
+
+ if (of_property_read_bool(dev->of_node,
+ "qcom,ext-disp-audio-rx")) {
+ dev_dbg(dev, "%s(): ext disp audio support present\n",
+ __func__);
+ memcpy(msm_tavil_dai_links + total_links,
+ ext_disp_be_dai_link,
+ sizeof(ext_disp_be_dai_link));
+ total_links += ARRAY_SIZE(ext_disp_be_dai_link);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,mi2s-audio-intf")) {
+ memcpy(msm_tavil_dai_links + total_links,
+ msm_mi2s_be_dai_links,
+ sizeof(msm_mi2s_be_dai_links));
+ total_links += ARRAY_SIZE(msm_mi2s_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,auxpcm-audio-intf")) {
+ memcpy(msm_tavil_dai_links + total_links,
+ msm_auxpcm_be_dai_links,
+ sizeof(msm_auxpcm_be_dai_links));
+ total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+ }
+ dailink = msm_tavil_dai_links;
+ } else if (!strcmp(match->data, "stub_codec")) {
+ card = &snd_soc_card_stub_msm;
+ len_1 = ARRAY_SIZE(msm_stub_fe_dai_links);
+ len_2 = len_1 + ARRAY_SIZE(msm_stub_be_dai_links);
+
+ memcpy(msm_stub_dai_links,
+ msm_stub_fe_dai_links,
+ sizeof(msm_stub_fe_dai_links));
+ memcpy(msm_stub_dai_links + len_1,
+ msm_stub_be_dai_links,
+ sizeof(msm_stub_be_dai_links));
+
+ dailink = msm_stub_dai_links;
+ total_links = len_2;
+ }
+
+ if (card) {
+ card->dai_link = dailink;
+ card->num_links = total_links;
+ }
+
+ return card;
+}
+
+static int msm_wsa881x_init(struct snd_soc_component *component)
+{
+ u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106};
+ u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107};
+ unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200};
+ unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3};
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+ struct msm_asoc_mach_data *pdata;
+ struct snd_soc_dapm_context *dapm;
+ int ret = 0;
+
+ if (!codec) {
+ pr_err("%s codec is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dapm = snd_soc_codec_get_dapm(codec);
+
+ if (!strcmp(component->name_prefix, "SpkrLeft")) {
+ dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n",
+ __func__, codec->component.name);
+ wsa881x_set_channel_map(codec, &spkleft_ports[0],
+ WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+ &ch_rate[0]);
+ if (dapm->component) {
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN");
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR");
+ }
+ } else if (!strcmp(component->name_prefix, "SpkrRight")) {
+ dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n",
+ __func__, codec->component.name);
+ wsa881x_set_channel_map(codec, &spkright_ports[0],
+ WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+ &ch_rate[0]);
+ if (dapm->component) {
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN");
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR");
+ }
+ } else {
+ dev_err(codec->dev, "%s: wrong codec name %s\n", __func__,
+ codec->component.name);
+ ret = -EINVAL;
+ goto err_codec;
+ }
+ pdata = snd_soc_card_get_drvdata(component->card);
+ if (pdata && pdata->codec_root)
+ wsa881x_codec_info_create_codec_entry(pdata->codec_root,
+ codec);
+
+err_codec:
+ return ret;
+}
+
+static int msm_init_wsa_dev(struct platform_device *pdev,
+ struct snd_soc_card *card)
+{
+ struct device_node *wsa_of_node;
+ u32 wsa_max_devs;
+ u32 wsa_dev_cnt;
+ int i;
+ struct msm_wsa881x_dev_info *wsa881x_dev_info;
+ const char *wsa_auxdev_name_prefix[1];
+ char *dev_name_str = NULL;
+ int found = 0;
+ int ret = 0;
+
+ /* Get maximum WSA device count for this platform */
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,wsa-max-devs", &wsa_max_devs);
+ if (ret) {
+ dev_dbg(&pdev->dev,
+ "%s: wsa-max-devs property missing in DT %s, ret = %d\n",
+ __func__, pdev->dev.of_node->full_name, ret);
+ goto err_dt;
+ }
+ if (wsa_max_devs == 0) {
+ dev_warn(&pdev->dev,
+ "%s: Max WSA devices is 0 for this target?\n",
+ __func__);
+ goto err_dt;
+ }
+
+ /* Get count of WSA device phandles for this platform */
+ wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node,
+ "qcom,wsa-devs", NULL);
+ if (wsa_dev_cnt == -ENOENT) {
+ dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n",
+ __func__);
+ goto err_dt;
+ } else if (wsa_dev_cnt <= 0) {
+ dev_err(&pdev->dev,
+ "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n",
+ __func__, wsa_dev_cnt);
+ ret = -EINVAL;
+ goto err_dt;
+ }
+
+ /*
+ * Expect total phandles count to be NOT less than maximum possible
+ * WSA count. However, if it is less, then assign same value to
+ * max count as well.
+ */
+ if (wsa_dev_cnt < wsa_max_devs) {
+ dev_dbg(&pdev->dev,
+ "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n",
+ __func__, wsa_max_devs, wsa_dev_cnt);
+ wsa_max_devs = wsa_dev_cnt;
+ }
+
+ /* Make sure prefix string passed for each WSA device */
+ ret = of_property_count_strings(pdev->dev.of_node,
+ "qcom,wsa-aux-dev-prefix");
+ if (ret != wsa_dev_cnt) {
+ dev_err(&pdev->dev,
+ "%s: expecting %d wsa prefix. Defined only %d in DT\n",
+ __func__, wsa_dev_cnt, ret);
+ ret = -EINVAL;
+ goto err_dt;
+ }
+
+ /*
+ * Alloc mem to store phandle and index info of WSA device, if already
+ * registered with ALSA core
+ */
+ wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs,
+ sizeof(struct msm_wsa881x_dev_info),
+ GFP_KERNEL);
+ if (!wsa881x_dev_info) {
+ ret = -ENOMEM;
+ goto err_mem;
+ }
+
+ /*
+ * search and check whether all WSA devices are already
+ * registered with ALSA core or not. If found a node, store
+ * the node and the index in a local array of struct for later
+ * use.
+ */
+ for (i = 0; i < wsa_dev_cnt; i++) {
+ wsa_of_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,wsa-devs", i);
+ if (unlikely(!wsa_of_node)) {
+ /* we should not be here */
+ dev_err(&pdev->dev,
+ "%s: wsa dev node is not present\n",
+ __func__);
+ ret = -EINVAL;
+ goto err_dev_node;
+ }
+ if (soc_find_component(wsa_of_node, NULL)) {
+ /* WSA device registered with ALSA core */
+ wsa881x_dev_info[found].of_node = wsa_of_node;
+ wsa881x_dev_info[found].index = i;
+ found++;
+ if (found == wsa_max_devs)
+ break;
+ }
+ }
+
+ if (found < wsa_max_devs) {
+ dev_dbg(&pdev->dev,
+ "%s: failed to find %d components. Found only %d\n",
+ __func__, wsa_max_devs, found);
+ return -EPROBE_DEFER;
+ }
+ dev_info(&pdev->dev,
+ "%s: found %d wsa881x devices registered with ALSA core\n",
+ __func__, found);
+
+ card->num_aux_devs = wsa_max_devs;
+ card->num_configs = wsa_max_devs;
+
+ /* Alloc array of AUX devs struct */
+ msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+ sizeof(struct snd_soc_aux_dev),
+ GFP_KERNEL);
+ if (!msm_aux_dev) {
+ ret = -ENOMEM;
+ goto err_auxdev_mem;
+ }
+
+ /* Alloc array of codec conf struct */
+ msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+ sizeof(struct snd_soc_codec_conf),
+ GFP_KERNEL);
+ if (!msm_codec_conf) {
+ ret = -ENOMEM;
+ goto err_codec_conf;
+ }
+
+ for (i = 0; i < card->num_aux_devs; i++) {
+ dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN,
+ GFP_KERNEL);
+ if (!dev_name_str) {
+ ret = -ENOMEM;
+ goto err_dev_str;
+ }
+
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "qcom,wsa-aux-dev-prefix",
+ wsa881x_dev_info[i].index,
+ wsa_auxdev_name_prefix);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: failed to read wsa aux dev prefix, ret = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto err_dt_prop;
+ }
+
+ snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i);
+ msm_aux_dev[i].name = dev_name_str;
+ msm_aux_dev[i].codec_name = NULL;
+ msm_aux_dev[i].codec_of_node =
+ wsa881x_dev_info[i].of_node;
+ msm_aux_dev[i].init = msm_wsa881x_init;
+ msm_codec_conf[i].dev_name = NULL;
+ msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0];
+ msm_codec_conf[i].of_node =
+ wsa881x_dev_info[i].of_node;
+ }
+ card->codec_conf = msm_codec_conf;
+ card->aux_dev = msm_aux_dev;
+
+ return 0;
+
+err_dt_prop:
+ devm_kfree(&pdev->dev, dev_name_str);
+err_dev_str:
+ devm_kfree(&pdev->dev, msm_codec_conf);
+err_codec_conf:
+ devm_kfree(&pdev->dev, msm_aux_dev);
+err_auxdev_mem:
+err_dev_node:
+ devm_kfree(&pdev->dev, wsa881x_dev_info);
+err_mem:
+err_dt:
+ return ret;
+}
+
+static void i2s_auxpcm_init(struct platform_device *pdev)
+{
+ struct resource *muxsel;
+ int count;
+ u32 mi2s_master_slave[MI2S_MAX];
+ int ret;
+ char *str[PCM_I2S_SEL_MAX] = {
+ "lpaif_pri_mode_muxsel",
+ "lpaif_sec_mode_muxsel",
+ "lpaif_tert_mode_muxsel",
+ "lpaif_quat_mode_muxsel"
+ };
+
+ for (count = 0; count < MI2S_MAX; count++) {
+ mutex_init(&mi2s_intf_conf[count].lock);
+ mi2s_intf_conf[count].ref_cnt = 0;
+ }
+
+ for (count = 0; count < AUX_PCM_MAX; count++) {
+ mutex_init(&auxpcm_intf_conf[count].lock);
+ auxpcm_intf_conf[count].ref_cnt = 0;
+ }
+
+ for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
+ mutex_init(&mi2s_auxpcm_conf[count].lock);
+ mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL;
+ }
+
+ for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
+ muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ str[count]);
+ if (muxsel) {
+ mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr
+ = ioremap(muxsel->start, resource_size(muxsel));
+ }
+ }
+
+ ret = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-mi2s-master",
+ mi2s_master_slave, MI2S_MAX);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n",
+ __func__);
+ } else {
+ for (count = 0; count < MI2S_MAX; count++) {
+ mi2s_intf_conf[count].msm_is_mi2s_master =
+ mi2s_master_slave[count];
+ }
+ }
+}
+
+static void i2s_auxpcm_deinit(void)
+{
+ int count;
+
+ for (count = 0; count < PCM_I2S_SEL_MAX; count++)
+ if (mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr !=
+ NULL)
+ iounmap(
+ mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr);
+}
+
+static int msm_asoc_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct msm_asoc_mach_data *pdata;
+ const char *mbhc_audio_jack_type = NULL;
+ char *mclk_freq_prop_name;
+ const struct of_device_id *match;
+ int ret;
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "No platform supplied from device tree\n");
+ return -EINVAL;
+ }
+
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_asoc_mach_data), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ card = populate_snd_card_dailinks(&pdev->dev);
+ if (!card) {
+ dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, pdata);
+
+ ret = snd_soc_of_parse_card_name(card, "qcom,model");
+ if (ret) {
+ dev_err(&pdev->dev, "parse card name failed, err:%d\n",
+ ret);
+ goto err;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "parse audio routing failed, err:%d\n",
+ ret);
+ goto err;
+ }
+
+ match = of_match_node(msm8998_asoc_machine_of_match,
+ pdev->dev.of_node);
+ if (!match) {
+ dev_err(&pdev->dev, "%s: no matched codec is found.\n",
+ __func__);
+ goto err;
+ }
+
+ if (!strcmp(match->data, "tasha_codec"))
+ mclk_freq_prop_name = "qcom,tasha-mclk-clk-freq";
+ else
+ mclk_freq_prop_name = "qcom,tavil-mclk-clk-freq";
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ mclk_freq_prop_name, &pdata->mclk_freq);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Looking up %s property in node %s failed, err%d\n",
+ mclk_freq_prop_name,
+ pdev->dev.of_node->full_name, ret);
+ goto err;
+ }
+
+ if (pdata->mclk_freq != CODEC_EXT_CLK_RATE) {
+ dev_err(&pdev->dev, "unsupported mclk freq %u\n",
+ pdata->mclk_freq);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = msm_populate_dai_link_component_of_node(card);
+ if (ret) {
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
+ ret = msm_init_wsa_dev(pdev, card);
+ if (ret)
+ goto err;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret == -EPROBE_DEFER) {
+ if (codec_reg_done)
+ ret = -EINVAL;
+ goto err;
+ } else if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err;
+ }
+ dev_info(&pdev->dev, "Sound card %s registered\n", card->name);
+ spdev = pdev;
+
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: failed to add child nodes, ret=%d\n",
+ __func__, ret);
+ } else {
+ pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,hph-en1-gpio", 0);
+ if (!pdata->hph_en1_gpio_p) {
+ dev_dbg(&pdev->dev, "property %s not detected in node %s",
+ "qcom,hph-en1-gpio",
+ pdev->dev.of_node->full_name);
+ }
+
+ pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,hph-en0-gpio", 0);
+ if (!pdata->hph_en0_gpio_p) {
+ dev_dbg(&pdev->dev, "property %s not detected in node %s",
+ "qcom,hph-en0-gpio",
+ pdev->dev.of_node->full_name);
+ }
+ }
+
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type);
+ if (ret) {
+ dev_dbg(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,mbhc-audio-jack-type",
+ pdev->dev.of_node->full_name);
+ dev_dbg(&pdev->dev, "Jack type properties set to default");
+ } else {
+ if (!strcmp(mbhc_audio_jack_type, "4-pole-jack")) {
+ wcd_mbhc_cfg.enable_anc_mic_detect = false;
+ dev_dbg(&pdev->dev, "This hardware has 4 pole jack");
+ } else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) {
+ wcd_mbhc_cfg.enable_anc_mic_detect = true;
+ dev_dbg(&pdev->dev, "This hardware has 5 pole jack");
+ } else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) {
+ wcd_mbhc_cfg.enable_anc_mic_detect = true;
+ dev_dbg(&pdev->dev, "This hardware has 6 pole jack");
+ } else {
+ wcd_mbhc_cfg.enable_anc_mic_detect = false;
+ dev_dbg(&pdev->dev, "Unknown value, set to default");
+ }
+ }
+ /*
+ * Parse US-Euro gpio info from DT. Report no error if us-euro
+ * entry is not found in DT file as some targets do not support
+ * US-Euro detection
+ */
+ pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,us-euro-gpios", 0);
+ if (!gpio_is_valid(pdata->us_euro_gpio))
+ pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,us-euro-gpios", 0);
+ if (!gpio_is_valid(pdata->us_euro_gpio) && (!pdata->us_euro_gpio_p)) {
+ dev_dbg(&pdev->dev, "property %s not detected in node %s",
+ "qcom,us-euro-gpios", pdev->dev.of_node->full_name);
+ } else {
+ dev_dbg(&pdev->dev, "%s detected",
+ "qcom,us-euro-gpios");
+ wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic;
+ }
+
+ ret = msm_prepare_us_euro(card);
+ if (ret)
+ dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n",
+ ret);
+
+ /* Parse pinctrl info from devicetree */
+ ret = msm_get_pinctrl(pdev);
+ if (!ret) {
+ pr_debug("%s: pinctrl parsing successful\n", __func__);
+ } else {
+ dev_dbg(&pdev->dev,
+ "%s: Parsing pinctrl failed with %d. Cannot use Ports\n",
+ __func__, ret);
+ ret = 0;
+ }
+
+ i2s_auxpcm_init(pdev);
+
+ is_initial_boot = true;
+ ret = audio_notifier_register("msm8998", AUDIO_NOTIFIER_ADSP_DOMAIN,
+ &service_nb);
+ if (ret < 0)
+ pr_err("%s: Audio notifier register failed ret = %d\n",
+ __func__, ret);
+
+ return 0;
+err:
+ if (pdata->us_euro_gpio > 0) {
+ dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n",
+ __func__, pdata->us_euro_gpio);
+ gpio_free(pdata->us_euro_gpio);
+ pdata->us_euro_gpio = 0;
+ }
+ msm_release_pinctrl(pdev);
+ devm_kfree(&pdev->dev, pdata);
+ return ret;
+}
+
+static int msm_asoc_machine_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct msm_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+
+ gpio_free(pdata->us_euro_gpio);
+ i2s_auxpcm_deinit();
+
+ snd_soc_unregister_card(card);
+ return 0;
+}
+
+static struct platform_driver msm8998_asoc_machine_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = msm8998_asoc_machine_of_match,
+ },
+ .probe = msm_asoc_machine_probe,
+ .remove = msm_asoc_machine_remove,
+};
+module_platform_driver(msm8998_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, msm8998_asoc_machine_of_match);
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
new file mode 100644
index 000000000000..67d3d277404d
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -0,0 +1,24 @@
+snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o \
+ msm-compress-q6-v2.o \
+ msm-pcm-afe-v2.o msm-pcm-voip-v2.o \
+ msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o \
+ msm-lsm-client.o msm-pcm-host-voice-v2.o \
+ msm-audio-effects-q6-v2.o msm-pcm-loopback-v2.o \
+ msm-transcode-loopback-q6-v2.o \
+ adsp_err.o
+obj-$(CONFIG_SLIMBUS) += msm-dai-slim.o audio_slimslave.o
+obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o \
+ msm-dai-stub-v2.o
+obj-$(CONFIG_SND_SOC_QDSP6V2_VM) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o \
+ msm-dai-stub-v2.o
+obj-$(CONFIG_SND_HWDEP) += msm-pcm-routing-devdep.o
+obj-$(CONFIG_DOLBY_DAP) += msm-dolby-dap-config.o
+obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o
+obj-$(CONFIG_DOLBY_LICENSE) += msm-ds2-dap-config.o
+obj-$(CONFIG_DTS_SRS_TM) += msm-dts-srs-tm-config.o
+obj-$(CONFIG_QTI_PP) += msm-qti-pp-config.o
+obj-y += audio_calibration.o audio_cal_utils.o q6adm.o q6afe.o q6asm.o \
+ q6audio-v2.o q6voice.o q6core.o rtac.o q6lsm.o \
+ msm-pcm-q6-noirq.o q6common.o
+ocmem-audio-objs += audio_ocmem.o
+obj-$(CONFIG_AUDIO_OCMEM) += ocmem-audio.o
diff --git a/sound/soc/msm/qdsp6v2/adsp_err.c b/sound/soc/msm/qdsp6v2/adsp_err.c
new file mode 100644
index 000000000000..978102253817
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/adsp_err.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2016, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/errno.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <sound/apr_audio-v2.h>
+
+
+/* ERROR STRING */
+/* Success. The operation completed with no errors. */
+#define ADSP_EOK_STR "ADSP_EOK"
+/* General failure. */
+#define ADSP_EFAILED_STR "ADSP_EFAILED"
+/* Bad operation parameter. */
+#define ADSP_EBADPARAM_STR "ADSP_EBADPARAM"
+/* Unsupported routine or operation. */
+#define ADSP_EUNSUPPORTED_STR "ADSP_EUNSUPPORTED"
+/* Unsupported version. */
+#define ADSP_EVERSION_STR "ADSP_EVERSION"
+/* Unexpected problem encountered. */
+#define ADSP_EUNEXPECTED_STR "ADSP_EUNEXPECTED"
+/* Unhandled problem occurred. */
+#define ADSP_EPANIC_STR "ADSP_EPANIC"
+/* Unable to allocate resource. */
+#define ADSP_ENORESOURCE_STR "ADSP_ENORESOURCE"
+/* Invalid handle. */
+#define ADSP_EHANDLE_STR "ADSP_EHANDLE"
+/* Operation is already processed. */
+#define ADSP_EALREADY_STR "ADSP_EALREADY"
+/* Operation is not ready to be processed. */
+#define ADSP_ENOTREADY_STR "ADSP_ENOTREADY"
+/* Operation is pending completion. */
+#define ADSP_EPENDING_STR "ADSP_EPENDING"
+/* Operation could not be accepted or processed. */
+#define ADSP_EBUSY_STR "ADSP_EBUSY"
+/* Operation aborted due to an error. */
+#define ADSP_EABORTED_STR "ADSP_EABORTED"
+/* Operation preempted by a higher priority. */
+#define ADSP_EPREEMPTED_STR "ADSP_EPREEMPTED"
+/* Operation requests intervention to complete. */
+#define ADSP_ECONTINUE_STR "ADSP_ECONTINUE"
+/* Operation requests immediate intervention to complete. */
+#define ADSP_EIMMEDIATE_STR "ADSP_EIMMEDIATE"
+/* Operation is not implemented. */
+#define ADSP_ENOTIMPL_STR "ADSP_ENOTIMPL"
+/* Operation needs more data or resources. */
+#define ADSP_ENEEDMORE_STR "ADSP_ENEEDMORE"
+/* Operation does not have memory. */
+#define ADSP_ENOMEMORY_STR "ADSP_ENOMEMORY"
+/* Item does not exist. */
+#define ADSP_ENOTEXIST_STR "ADSP_ENOTEXIST"
+/* Unexpected error code. */
+#define ADSP_ERR_MAX_STR "ADSP_ERR_MAX"
+
+#ifdef CONFIG_SND_SOC_QDSP_DEBUG
+static bool adsp_err_panic;
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_adsp_err;
+
+static ssize_t adsp_err_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char cmd;
+
+ if (copy_from_user(&cmd, ubuf, 1))
+ return -EFAULT;
+
+ if (cmd == '0')
+ adsp_err_panic = false;
+ else
+ adsp_err_panic = true;
+
+ return cnt;
+}
+
+static const struct file_operations adsp_err_debug_ops = {
+ .write = adsp_err_debug_write,
+};
+#endif
+#endif
+
+struct adsp_err_code {
+ int lnx_err_code;
+ char *adsp_err_str;
+};
+
+
+static struct adsp_err_code adsp_err_code_info[ADSP_ERR_MAX+1] = {
+ { 0, ADSP_EOK_STR},
+ { -ENOTRECOVERABLE, ADSP_EFAILED_STR},
+ { -EINVAL, ADSP_EBADPARAM_STR},
+ { -ENOSYS, ADSP_EUNSUPPORTED_STR},
+ { -ENOPROTOOPT, ADSP_EVERSION_STR},
+ { -ENOTRECOVERABLE, ADSP_EUNEXPECTED_STR},
+ { -ENOTRECOVERABLE, ADSP_EPANIC_STR},
+ { -ENOSPC, ADSP_ENORESOURCE_STR},
+ { -EBADR, ADSP_EHANDLE_STR},
+ { -EALREADY, ADSP_EALREADY_STR},
+ { -EPERM, ADSP_ENOTREADY_STR},
+ { -EINPROGRESS, ADSP_EPENDING_STR},
+ { -EBUSY, ADSP_EBUSY_STR},
+ { -ECANCELED, ADSP_EABORTED_STR},
+ { -EAGAIN, ADSP_EPREEMPTED_STR},
+ { -EAGAIN, ADSP_ECONTINUE_STR},
+ { -EAGAIN, ADSP_EIMMEDIATE_STR},
+ { -EAGAIN, ADSP_ENOTIMPL_STR},
+ { -ENODATA, ADSP_ENEEDMORE_STR},
+ { -EADV, ADSP_ERR_MAX_STR},
+ { -ENOMEM, ADSP_ENOMEMORY_STR},
+ { -ENODEV, ADSP_ENOTEXIST_STR},
+ { -EADV, ADSP_ERR_MAX_STR},
+};
+
+#ifdef CONFIG_SND_SOC_QDSP_DEBUG
+static inline void adsp_err_check_panic(u32 adsp_error)
+{
+ if (adsp_err_panic && adsp_error != ADSP_EALREADY)
+ panic("%s: encounter adsp_err=0x%x\n", __func__, adsp_error);
+}
+#else
+static inline void adsp_err_check_panic(u32 adsp_error) {}
+#endif
+
+int adsp_err_get_lnx_err_code(u32 adsp_error)
+{
+ adsp_err_check_panic(adsp_error);
+
+ if (adsp_error > ADSP_ERR_MAX)
+ return adsp_err_code_info[ADSP_ERR_MAX].lnx_err_code;
+ else
+ return adsp_err_code_info[adsp_error].lnx_err_code;
+}
+
+char *adsp_err_get_err_str(u32 adsp_error)
+{
+ if (adsp_error > ADSP_ERR_MAX)
+ return adsp_err_code_info[ADSP_ERR_MAX].adsp_err_str;
+ else
+ return adsp_err_code_info[adsp_error].adsp_err_str;
+}
+
+#if defined(CONFIG_SND_SOC_QDSP_DEBUG) && defined(CONFIG_DEBUG_FS)
+static int __init adsp_err_init(void)
+{
+
+
+ debugfs_adsp_err = debugfs_create_file("msm_adsp_audio_debug",
+ S_IFREG | S_IRUGO, NULL, NULL,
+ &adsp_err_debug_ops);
+
+ return 0;
+}
+
+device_initcall(adsp_err_init);
+#endif
diff --git a/sound/soc/msm/qdsp6v2/audio_cal_utils.c b/sound/soc/msm/qdsp6v2/audio_cal_utils.c
new file mode 100644
index 000000000000..4f2531a3da63
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/audio_cal_utils.c
@@ -0,0 +1,968 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <sound/audio_cal_utils.h>
+
+static int unmap_memory(struct cal_type_data *cal_type,
+ struct cal_block_data *cal_block);
+
+size_t get_cal_info_size(int32_t cal_type)
+{
+ size_t size = 0;
+
+ switch (cal_type) {
+ case CVP_VOC_RX_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_voc_top);
+ break;
+ case CVP_VOC_TX_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_voc_top);
+ break;
+ case CVP_VOCPROC_STATIC_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_vocproc);
+ break;
+ case CVP_VOCPROC_DYNAMIC_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_vocvol);
+ break;
+ case CVS_VOCSTRM_STATIC_CAL_TYPE:
+ size = 0;
+ break;
+ case CVP_VOCDEV_CFG_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_vocdev_cfg);
+ break;
+ case CVP_VOCPROC_STATIC_COL_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_voc_col);
+ break;
+ case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_voc_col);
+ break;
+ case CVS_VOCSTRM_STATIC_COL_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_voc_col);
+ break;
+ case ADM_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_adm_top);
+ break;
+ case ADM_CUST_TOPOLOGY_CAL_TYPE:
+ case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE:
+ size = 0;
+ break;
+ case ADM_AUDPROC_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_audproc);
+ break;
+ case ADM_AUDVOL_CAL_TYPE:
+ case ADM_RTAC_AUDVOL_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_audvol);
+ break;
+ case ASM_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_asm_top);
+ break;
+ case ASM_CUST_TOPOLOGY_CAL_TYPE:
+ size = 0;
+ break;
+ case ASM_AUDSTRM_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_audstrm);
+ break;
+ case AFE_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_afe_top);
+ break;
+ case AFE_CUST_TOPOLOGY_CAL_TYPE:
+ size = 0;
+ break;
+ case AFE_COMMON_RX_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_afe);
+ break;
+ case AFE_COMMON_TX_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_afe);
+ break;
+ case AFE_FB_SPKR_PROT_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_spk_prot_cfg);
+ break;
+ case AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE:
+ /*
+ * Since get and set parameter structures are different in size
+ * use the maximum size of get and set parameter structure
+ */
+ size = max(sizeof(struct audio_cal_info_sp_th_vi_ftm_cfg),
+ sizeof(struct audio_cal_info_sp_th_vi_param));
+ break;
+ case AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE:
+ /*
+ * Since get and set parameter structures are different in size
+ * use the maximum size of get and set parameter structure
+ */
+ size = max(sizeof(struct audio_cal_info_sp_ex_vi_ftm_cfg),
+ sizeof(struct audio_cal_info_sp_ex_vi_param));
+ break;
+ case AFE_ANC_CAL_TYPE:
+ size = 0;
+ break;
+ case AFE_AANC_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_aanc);
+ break;
+ case AFE_HW_DELAY_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_hw_delay);
+ break;
+ case AFE_SIDETONE_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_sidetone);
+ break;
+ case AFE_SIDETONE_IIR_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_sidetone_iir);
+ break;
+ case LSM_CUST_TOPOLOGY_CAL_TYPE:
+ size = 0;
+ break;
+ case LSM_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_lsm_top);
+ break;
+ case ULP_LSM_TOPOLOGY_ID_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_lsm_top);
+ break;
+ case LSM_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_lsm);
+ break;
+ case ADM_RTAC_INFO_CAL_TYPE:
+ size = 0;
+ break;
+ case VOICE_RTAC_INFO_CAL_TYPE:
+ size = 0;
+ break;
+ case ADM_RTAC_APR_CAL_TYPE:
+ size = 0;
+ break;
+ case ASM_RTAC_APR_CAL_TYPE:
+ size = 0;
+ break;
+ case VOICE_RTAC_APR_CAL_TYPE:
+ size = 0;
+ break;
+ case MAD_CAL_TYPE:
+ size = 0;
+ break;
+ case ULP_AFE_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_afe);
+ break;
+ case ULP_LSM_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_lsm);
+ break;
+ case AUDIO_CORE_METAINFO_CAL_TYPE:
+ size = sizeof(struct audio_cal_info_metainfo);
+ break;
+ case SRS_TRUMEDIA_CAL_TYPE:
+ size = 0;
+ break;
+ default:
+ pr_err("%s:Invalid cal type %d!",
+ __func__, cal_type);
+ }
+ return size;
+}
+
+size_t get_user_cal_type_size(int32_t cal_type)
+{
+ size_t size = 0;
+
+ switch (cal_type) {
+ case CVP_VOC_RX_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_voc_top);
+ break;
+ case CVP_VOC_TX_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_voc_top);
+ break;
+ case CVP_VOCPROC_STATIC_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_vocproc);
+ break;
+ case CVP_VOCPROC_DYNAMIC_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_vocvol);
+ break;
+ case CVS_VOCSTRM_STATIC_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_basic);
+ break;
+ case CVP_VOCDEV_CFG_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_vocdev_cfg);
+ break;
+ case CVP_VOCPROC_STATIC_COL_CAL_TYPE:
+ case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE:
+ case CVS_VOCSTRM_STATIC_COL_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_voc_col);
+ break;
+ case ADM_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_adm_top);
+ break;
+ case ADM_CUST_TOPOLOGY_CAL_TYPE:
+ case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_basic);
+ break;
+ case ADM_AUDPROC_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_audproc);
+ break;
+ case ADM_AUDVOL_CAL_TYPE:
+ case ADM_RTAC_AUDVOL_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_audvol);
+ break;
+ case ASM_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_asm_top);
+ break;
+ case ASM_CUST_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_basic);
+ break;
+ case ASM_AUDSTRM_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_audstrm);
+ break;
+ case AFE_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_afe_top);
+ break;
+ case AFE_CUST_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_basic);
+ break;
+ case AFE_COMMON_RX_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_afe);
+ break;
+ case AFE_COMMON_TX_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_afe);
+ break;
+ case AFE_FB_SPKR_PROT_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_fb_spk_prot_cfg);
+ break;
+ case AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE:
+ /*
+ * Since get and set parameter structures are different in size
+ * use the maximum size of get and set parameter structure
+ */
+ size = max(sizeof(struct audio_cal_type_sp_th_vi_ftm_cfg),
+ sizeof(struct audio_cal_type_sp_th_vi_param));
+ break;
+ case AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE:
+ /*
+ * Since get and set parameter structures are different in size
+ * use the maximum size of get and set parameter structure
+ */
+ size = max(sizeof(struct audio_cal_type_sp_ex_vi_ftm_cfg),
+ sizeof(struct audio_cal_type_sp_ex_vi_param));
+ break;
+ case AFE_ANC_CAL_TYPE:
+ size = 0;
+ break;
+ case AFE_AANC_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_aanc);
+ break;
+ case AFE_HW_DELAY_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_hw_delay);
+ break;
+ case AFE_SIDETONE_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_sidetone);
+ break;
+ case AFE_SIDETONE_IIR_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_sidetone_iir);
+ break;
+ case LSM_CUST_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_basic);
+ break;
+ case LSM_TOPOLOGY_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_lsm_top);
+ break;
+ case ULP_LSM_TOPOLOGY_ID_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_lsm_top);
+ break;
+ case LSM_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_lsm);
+ break;
+ case ADM_RTAC_INFO_CAL_TYPE:
+ size = 0;
+ break;
+ case VOICE_RTAC_INFO_CAL_TYPE:
+ size = 0;
+ break;
+ case ADM_RTAC_APR_CAL_TYPE:
+ size = 0;
+ break;
+ case ASM_RTAC_APR_CAL_TYPE:
+ size = 0;
+ break;
+ case VOICE_RTAC_APR_CAL_TYPE:
+ size = 0;
+ break;
+ case MAD_CAL_TYPE:
+ size = 0;
+ break;
+ case ULP_AFE_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_afe);
+ break;
+ case ULP_LSM_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_lsm);
+ break;
+ case AUDIO_CORE_METAINFO_CAL_TYPE:
+ size = sizeof(struct audio_cal_type_metainfo);
+ break;
+ case SRS_TRUMEDIA_CAL_TYPE:
+ size = 0;
+ break;
+ default:
+ pr_err("%s:Invalid cal type %d!",
+ __func__, cal_type);
+ }
+ return size;
+}
+
+int32_t cal_utils_get_cal_type_version(void *cal_type_data)
+{
+ struct audio_cal_type_basic *data = NULL;
+
+ data = (struct audio_cal_type_basic *)cal_type_data;
+
+ return data->cal_hdr.version;
+}
+
+static struct cal_type_data *create_cal_type_data(
+ struct cal_type_info *info)
+{
+ struct cal_type_data *cal_type = NULL;
+
+ if ((info->reg.cal_type < 0) ||
+ (info->reg.cal_type >= MAX_CAL_TYPES)) {
+ pr_err("%s: cal type %d is Invalid!\n",
+ __func__, info->reg.cal_type);
+ goto done;
+ }
+
+ if (info->cal_util_callbacks.match_block == NULL) {
+ pr_err("%s: cal type %d no method to match blocks!\n",
+ __func__, info->reg.cal_type);
+ goto done;
+ }
+
+ cal_type = kmalloc(sizeof(*cal_type), GFP_KERNEL);
+ if (cal_type == NULL) {
+ pr_err("%s: could not allocate cal_type!\n", __func__);
+ goto done;
+ }
+ INIT_LIST_HEAD(&cal_type->cal_blocks);
+ mutex_init(&cal_type->lock);
+ memcpy(&cal_type->info, info,
+ sizeof(cal_type->info));
+done:
+ return cal_type;
+}
+
+int cal_utils_create_cal_types(int num_cal_types,
+ struct cal_type_data **cal_type,
+ struct cal_type_info *info)
+{
+ int ret = 0;
+ int i;
+ pr_debug("%s\n", __func__);
+
+ if (cal_type == NULL) {
+ pr_err("%s: cal_type is NULL!\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ } else if (info == NULL) {
+ pr_err("%s: info is NULL!\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ } else if ((num_cal_types <= 0) ||
+ (num_cal_types > MAX_CAL_TYPES)) {
+ pr_err("%s: num_cal_types of %d is Invalid!\n",
+ __func__, num_cal_types);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ for (i = 0; i < num_cal_types; i++) {
+ if ((info[i].reg.cal_type < 0) ||
+ (info[i].reg.cal_type >= MAX_CAL_TYPES)) {
+ pr_err("%s: cal type %d at index %d is Invalid!\n",
+ __func__, info[i].reg.cal_type, i);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cal_type[i] = create_cal_type_data(&info[i]);
+ if (cal_type[i] == NULL) {
+ pr_err("%s: Could not allocate cal_type of index %d!\n",
+ __func__, i);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = audio_cal_register(1, &info[i].reg);
+ if (ret < 0) {
+ pr_err("%s: audio_cal_register failed, ret = %d!\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ pr_debug("%s: cal type %d at index %d!\n",
+ __func__, info[i].reg.cal_type, i);
+ }
+done:
+ return ret;
+}
+
+static void delete_cal_block(struct cal_block_data *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_block == NULL)
+ goto done;
+
+ list_del(&cal_block->list);
+ kfree(cal_block->client_info);
+ cal_block->client_info = NULL;
+ kfree(cal_block->cal_info);
+ cal_block->cal_info = NULL;
+ if (cal_block->map_data.ion_client != NULL) {
+ msm_audio_ion_free(cal_block->map_data.ion_client,
+ cal_block->map_data.ion_handle);
+ cal_block->map_data.ion_client = NULL;
+ cal_block->map_data.ion_handle = NULL;
+ }
+ kfree(cal_block);
+done:
+ return;
+}
+
+static void destroy_all_cal_blocks(struct cal_type_data *cal_type)
+{
+ int ret = 0;
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block;
+
+ list_for_each_safe(ptr, next,
+ &cal_type->cal_blocks) {
+
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+
+ ret = unmap_memory(cal_type, cal_block);
+ if (ret < 0) {
+ pr_err("%s: unmap_memory failed, cal type %d, ret = %d!\n",
+ __func__,
+ cal_type->info.reg.cal_type,
+ ret);
+ }
+ delete_cal_block(cal_block);
+ cal_block = NULL;
+ }
+
+ return;
+}
+
+static void destroy_cal_type_data(struct cal_type_data *cal_type)
+{
+ if (cal_type == NULL)
+ goto done;
+
+ destroy_all_cal_blocks(cal_type);
+ list_del(&cal_type->cal_blocks);
+ kfree(cal_type);
+done:
+ return;
+}
+
+void cal_utils_destroy_cal_types(int num_cal_types,
+ struct cal_type_data **cal_type)
+{
+ int i;
+ pr_debug("%s\n", __func__);
+
+ if (cal_type == NULL) {
+ pr_err("%s: cal_type is NULL!\n", __func__);
+ goto done;
+ } else if ((num_cal_types <= 0) ||
+ (num_cal_types > MAX_CAL_TYPES)) {
+ pr_err("%s: num_cal_types of %d is Invalid!\n",
+ __func__, num_cal_types);
+ goto done;
+ }
+
+ for (i = 0; i < num_cal_types; i++) {
+ audio_cal_deregister(1, &cal_type[i]->info.reg);
+ destroy_cal_type_data(cal_type[i]);
+ cal_type[i] = NULL;
+ }
+done:
+ return;
+}
+
+struct cal_block_data *cal_utils_get_only_cal_block(
+ struct cal_type_data *cal_type)
+{
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block = NULL;
+
+ if (cal_type == NULL)
+ goto done;
+
+ list_for_each_safe(ptr, next,
+ &cal_type->cal_blocks) {
+
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+ break;
+ }
+done:
+ return cal_block;
+}
+
+bool cal_utils_match_buf_num(struct cal_block_data *cal_block,
+ void *user_data)
+{
+ bool ret = false;
+ struct audio_cal_type_basic *data = user_data;
+
+ if (cal_block->buffer_number == data->cal_hdr.buffer_number)
+ ret = true;
+
+ return ret;
+}
+
+static struct cal_block_data *get_matching_cal_block(
+ struct cal_type_data *cal_type,
+ void *data)
+{
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block = NULL;
+
+ list_for_each_safe(ptr, next,
+ &cal_type->cal_blocks) {
+
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+
+ if (cal_type->info.cal_util_callbacks.
+ match_block(cal_block, data))
+ return cal_block;
+ }
+
+ return NULL;
+}
+
+static int cal_block_ion_alloc(struct cal_block_data *cal_block)
+{
+ int ret = 0;
+
+ if (cal_block == NULL) {
+ pr_err("%s: cal_block is NULL!\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = msm_audio_ion_import("audio_cal_client",
+ &cal_block->map_data.ion_client,
+ &cal_block->map_data.ion_handle,
+ cal_block->map_data.ion_map_handle,
+ NULL, 0,
+ &cal_block->cal_data.paddr,
+ &cal_block->map_data.map_size,
+ &cal_block->cal_data.kvaddr);
+ if (ret) {
+ pr_err("%s: audio ION import failed, rc = %d\n",
+ __func__, ret);
+ ret = -ENOMEM;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static struct cal_block_data *create_cal_block(struct cal_type_data *cal_type,
+ struct audio_cal_type_basic *basic_cal,
+ size_t client_info_size, void *client_info)
+{
+ struct cal_block_data *cal_block = NULL;
+
+ if (cal_type == NULL) {
+ pr_err("%s: cal_type is NULL!\n", __func__);
+ goto done;
+ } else if (basic_cal == NULL) {
+ pr_err("%s: basic_cal is NULL!\n", __func__);
+ goto done;
+ }
+
+ cal_block = kzalloc(sizeof(*cal_block),
+ GFP_KERNEL);
+ if (cal_block == NULL) {
+ pr_err("%s: could not allocate cal_block!\n", __func__);
+ goto done;
+ }
+
+ INIT_LIST_HEAD(&cal_block->list);
+
+ cal_block->map_data.ion_map_handle = basic_cal->cal_data.mem_handle;
+ if (basic_cal->cal_data.mem_handle > 0) {
+ if (cal_block_ion_alloc(cal_block)) {
+ pr_err("%s: cal_block_ion_alloc failed!\n",
+ __func__);
+ goto err;
+ }
+ }
+ if (client_info_size > 0) {
+ cal_block->client_info_size = client_info_size;
+ cal_block->client_info = kmalloc(client_info_size, GFP_KERNEL);
+ if (cal_block->client_info == NULL) {
+ pr_err("%s: could not allocats client_info!\n",
+ __func__);
+ goto err;
+ }
+ if (client_info != NULL)
+ memcpy(cal_block->client_info, client_info,
+ client_info_size);
+ }
+
+ cal_block->cal_info = kzalloc(
+ get_cal_info_size(cal_type->info.reg.cal_type),
+ GFP_KERNEL);
+ if (cal_block->cal_info == NULL) {
+ pr_err("%s: could not allocats cal_info!\n",
+ __func__);
+ goto err;
+ }
+ cal_block->buffer_number = basic_cal->cal_hdr.buffer_number;
+ list_add_tail(&cal_block->list, &cal_type->cal_blocks);
+ pr_debug("%s: created block for cal type %d, buf num %d, map handle %d, map size %zd paddr 0x%pK!\n",
+ __func__, cal_type->info.reg.cal_type,
+ cal_block->buffer_number,
+ cal_block->map_data.ion_map_handle,
+ cal_block->map_data.map_size,
+ &cal_block->cal_data.paddr);
+done:
+ return cal_block;
+err:
+ kfree(cal_block->cal_info);
+ cal_block->cal_info = NULL;
+ kfree(cal_block->client_info);
+ cal_block->client_info = NULL;
+ kfree(cal_block);
+ cal_block = NULL;
+ return cal_block;
+}
+
+void cal_utils_clear_cal_block_q6maps(int num_cal_types,
+ struct cal_type_data **cal_type)
+{
+ int i = 0;
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block;
+ pr_debug("%s\n", __func__);
+
+ if (cal_type == NULL) {
+ pr_err("%s: cal_type is NULL!\n", __func__);
+ goto done;
+ } else if ((num_cal_types <= 0) ||
+ (num_cal_types > MAX_CAL_TYPES)) {
+ pr_err("%s: num_cal_types of %d is Invalid!\n",
+ __func__, num_cal_types);
+ goto done;
+ }
+
+ for (; i < num_cal_types; i++) {
+ if (cal_type[i] == NULL)
+ continue;
+
+ mutex_lock(&cal_type[i]->lock);
+ list_for_each_safe(ptr, next,
+ &cal_type[i]->cal_blocks) {
+
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+
+ cal_block->map_data.q6map_handle = 0;
+ }
+ mutex_unlock(&cal_type[i]->lock);
+ }
+done:
+ return;
+}
+
+
+
+static int realloc_memory(struct cal_block_data *cal_block)
+{
+ int ret = 0;
+
+ msm_audio_ion_free(cal_block->map_data.ion_client,
+ cal_block->map_data.ion_handle);
+ cal_block->map_data.ion_client = NULL;
+ cal_block->map_data.ion_handle = NULL;
+ cal_block->cal_data.size = 0;
+
+ ret = cal_block_ion_alloc(cal_block);
+ if (ret < 0)
+ pr_err("%s: realloc_memory failed!\n",
+ __func__);
+ return ret;
+}
+
+static int map_memory(struct cal_type_data *cal_type,
+ struct cal_block_data *cal_block)
+{
+ int ret = 0;
+
+
+ if (cal_type->info.cal_util_callbacks.map_cal != NULL) {
+ if ((cal_block->map_data.ion_map_handle < 0) ||
+ (cal_block->map_data.map_size <= 0) ||
+ (cal_block->map_data.q6map_handle != 0)) {
+ goto done;
+ }
+
+ pr_debug("%s: cal type %d call map\n",
+ __func__, cal_type->info.reg.cal_type);
+ ret = cal_type->info.cal_util_callbacks.
+ map_cal(cal_type->info.reg.cal_type, cal_block);
+ if (ret < 0) {
+ pr_err("%s: map_cal failed, cal type %d, ret = %d!\n",
+ __func__, cal_type->info.reg.cal_type,
+ ret);
+ goto done;
+ }
+ }
+done:
+ return ret;
+}
+
+static int unmap_memory(struct cal_type_data *cal_type,
+ struct cal_block_data *cal_block)
+{
+ int ret = 0;
+
+ if (cal_type->info.cal_util_callbacks.unmap_cal != NULL) {
+ if ((cal_block->map_data.ion_map_handle < 0) ||
+ (cal_block->map_data.map_size <= 0) ||
+ (cal_block->map_data.q6map_handle == 0)) {
+ goto done;
+ }
+ pr_debug("%s: cal type %d call unmap\n",
+ __func__, cal_type->info.reg.cal_type);
+ ret = cal_type->info.cal_util_callbacks.
+ unmap_cal(cal_type->info.reg.cal_type, cal_block);
+ if (ret < 0) {
+ pr_err("%s: unmap_cal failed, cal type %d, ret = %d!\n",
+ __func__, cal_type->info.reg.cal_type,
+ ret);
+ goto done;
+ }
+ }
+done:
+ return ret;
+}
+
+int cal_utils_alloc_cal(size_t data_size, void *data,
+ struct cal_type_data *cal_type,
+ size_t client_info_size, void *client_info)
+{
+ int ret = 0;
+ struct cal_block_data *cal_block;
+ struct audio_cal_type_alloc *alloc_data = data;
+ pr_debug("%s\n", __func__);
+
+ if (cal_type == NULL) {
+ pr_err("%s: cal_type is NULL!\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (data_size < sizeof(struct audio_cal_type_alloc)) {
+ pr_err("%s: data_size of %zd does not equal alloc struct size of %zd!\n",
+ __func__, data_size,
+ sizeof(struct audio_cal_type_alloc));
+ ret = -EINVAL;
+ goto done;
+ }
+ if ((client_info_size > 0) && (client_info == NULL)) {
+ pr_err("%s: User info pointer is NULL but size is %zd!\n",
+ __func__, client_info_size);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (alloc_data->cal_data.mem_handle < 0) {
+ pr_err("%s: mem_handle %d invalid!\n",
+ __func__, alloc_data->cal_data.mem_handle);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&cal_type->lock);
+
+ cal_block = get_matching_cal_block(cal_type,
+ data);
+ if (cal_block != NULL) {
+ ret = unmap_memory(cal_type, cal_block);
+ if (ret < 0)
+ goto err;
+ ret = realloc_memory(cal_block);
+ if (ret < 0)
+ goto err;
+ } else {
+ cal_block = create_cal_block(cal_type,
+ (struct audio_cal_type_basic *)alloc_data,
+ client_info_size, client_info);
+ if (cal_block == NULL) {
+ pr_err("%s: create_cal_block failed for %d!\n",
+ __func__, alloc_data->cal_data.mem_handle);
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ ret = map_memory(cal_type, cal_block);
+ if (ret < 0)
+ goto err;
+err:
+ mutex_unlock(&cal_type->lock);
+done:
+ return ret;
+}
+
+int cal_utils_dealloc_cal(size_t data_size, void *data,
+ struct cal_type_data *cal_type)
+{
+ int ret = 0;
+ struct cal_block_data *cal_block;
+ struct audio_cal_type_dealloc *dealloc_data = data;
+ pr_debug("%s\n", __func__);
+
+
+ if (cal_type == NULL) {
+ pr_err("%s: cal_type is NULL!\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (data_size < sizeof(struct audio_cal_type_dealloc)) {
+ pr_err("%s: data_size of %zd does not equal struct size of %zd!\n",
+ __func__, data_size,
+ sizeof(struct audio_cal_type_dealloc));
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((dealloc_data->cal_data.mem_handle == -1) &&
+ (dealloc_data->cal_hdr.buffer_number == ALL_CAL_BLOCKS)) {
+ destroy_all_cal_blocks(cal_type);
+ goto done;
+ }
+
+ if (dealloc_data->cal_data.mem_handle < 0) {
+ pr_err("%s: mem_handle %d invalid!\n",
+ __func__, dealloc_data->cal_data.mem_handle);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&cal_type->lock);
+ cal_block = get_matching_cal_block(
+ cal_type,
+ data);
+ if (cal_block == NULL) {
+ pr_err("%s: allocation does not exist for %d!\n",
+ __func__, dealloc_data->cal_data.mem_handle);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = unmap_memory(cal_type, cal_block);
+ if (ret < 0)
+ goto err;
+
+ delete_cal_block(cal_block);
+err:
+ mutex_unlock(&cal_type->lock);
+done:
+ return ret;
+}
+
+int cal_utils_set_cal(size_t data_size, void *data,
+ struct cal_type_data *cal_type,
+ size_t client_info_size, void *client_info)
+{
+ int ret = 0;
+ struct cal_block_data *cal_block;
+ struct audio_cal_type_basic *basic_data = data;
+ pr_debug("%s\n", __func__);
+
+ if (cal_type == NULL) {
+ pr_err("%s: cal_type is NULL!\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((client_info_size > 0) && (client_info == NULL)) {
+ pr_err("%s: User info pointer is NULL but size is %zd!\n",
+ __func__, client_info_size);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((data_size > get_user_cal_type_size(
+ cal_type->info.reg.cal_type)) || (data_size < 0)) {
+ pr_err("%s: cal_type %d, data_size of %zd is invalid, expecting %zd!\n",
+ __func__, cal_type->info.reg.cal_type, data_size,
+ get_user_cal_type_size(cal_type->info.reg.cal_type));
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&cal_type->lock);
+ cal_block = get_matching_cal_block(
+ cal_type,
+ data);
+ if (cal_block == NULL) {
+ if (basic_data->cal_data.mem_handle > 0) {
+ pr_err("%s: allocation does not exist for %d!\n",
+ __func__, basic_data->cal_data.mem_handle);
+ ret = -EINVAL;
+ goto err;
+ } else {
+ cal_block = create_cal_block(
+ cal_type,
+ basic_data,
+ client_info_size, client_info);
+ if (cal_block == NULL) {
+ pr_err("%s: create_cal_block failed for cal type %d!\n",
+ __func__,
+ cal_type->info.reg.cal_type);
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ }
+
+ ret = map_memory(cal_type, cal_block);
+ if (ret < 0)
+ goto err;
+
+ cal_block->cal_data.size = basic_data->cal_data.cal_size;
+
+ if (client_info_size > 0) {
+ memcpy(cal_block->client_info,
+ client_info,
+ client_info_size);
+ }
+
+ memcpy(cal_block->cal_info,
+ ((uint8_t *)data + sizeof(struct audio_cal_type_basic)),
+ data_size - sizeof(struct audio_cal_type_basic));
+
+err:
+ mutex_unlock(&cal_type->lock);
+done:
+ return ret;
+}
diff --git a/sound/soc/msm/qdsp6v2/audio_calibration.c b/sound/soc/msm/qdsp6v2/audio_calibration.c
new file mode 100644
index 000000000000..2a1b34776b68
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/audio_calibration.c
@@ -0,0 +1,628 @@
+/* Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/msm_ion.h>
+#include <linux/msm_audio_ion.h>
+#include <sound/audio_calibration.h>
+#include <sound/audio_cal_utils.h>
+
+struct audio_cal_client_info {
+ struct list_head list;
+ struct audio_cal_callbacks *callbacks;
+};
+
+struct audio_cal_info {
+ struct mutex common_lock;
+ struct mutex cal_mutex[MAX_CAL_TYPES];
+ struct list_head client_info[MAX_CAL_TYPES];
+ int ref_count;
+};
+
+static struct audio_cal_info audio_cal;
+
+
+static bool callbacks_are_equal(struct audio_cal_callbacks *callback1,
+ struct audio_cal_callbacks *callback2)
+{
+ bool ret = true;
+ struct audio_cal_callbacks *call1 = callback1;
+ struct audio_cal_callbacks *call2 = callback2;
+ pr_debug("%s\n", __func__);
+
+ if ((call1 == NULL) && (call2 == NULL))
+ ret = true;
+ else if ((call1 == NULL) || (call2 == NULL))
+ ret = false;
+ else if ((call1->alloc != call2->alloc) ||
+ (call1->dealloc != call2->dealloc) ||
+ (call1->pre_cal != call2->pre_cal) ||
+ (call1->set_cal != call2->set_cal) ||
+ (call1->get_cal != call2->get_cal) ||
+ (call1->post_cal != call2->post_cal))
+ ret = false;
+ return ret;
+}
+
+int audio_cal_deregister(int num_cal_types,
+ struct audio_cal_reg *reg_data)
+{
+ int ret = 0;
+ int i = 0;
+ struct list_head *ptr, *next;
+ struct audio_cal_client_info *client_info_node = NULL;
+ pr_debug("%s\n", __func__);
+
+ if (reg_data == NULL) {
+ pr_err("%s: reg_data is NULL!\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ } else if ((num_cal_types <= 0) ||
+ (num_cal_types > MAX_CAL_TYPES)) {
+ pr_err("%s: num_cal_types of %d is Invalid!\n",
+ __func__, num_cal_types);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ for (; i < num_cal_types; i++) {
+ if ((reg_data[i].cal_type < 0) ||
+ (reg_data[i].cal_type >= MAX_CAL_TYPES)) {
+ pr_err("%s: cal type %d at index %d is Invalid!\n",
+ __func__, reg_data[i].cal_type, i);
+ ret = -EINVAL;
+ continue;
+ }
+
+ mutex_lock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
+ list_for_each_safe(ptr, next,
+ &audio_cal.client_info[reg_data[i].cal_type]) {
+
+ client_info_node = list_entry(ptr,
+ struct audio_cal_client_info, list);
+ if (callbacks_are_equal(client_info_node->callbacks,
+ &reg_data[i].callbacks)) {
+ list_del(&client_info_node->list);
+ kfree(client_info_node->callbacks);
+ client_info_node->callbacks = NULL;
+ kfree(client_info_node);
+ client_info_node = NULL;
+ break;
+ }
+ }
+ mutex_unlock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
+ }
+done:
+ return ret;
+}
+
+
+int audio_cal_register(int num_cal_types,
+ struct audio_cal_reg *reg_data)
+{
+ int ret = 0;
+ int i = 0;
+ struct audio_cal_client_info *client_info_node = NULL;
+ struct audio_cal_callbacks *callback_node = NULL;
+ pr_debug("%s\n", __func__);
+
+ if (reg_data == NULL) {
+ pr_err("%s: callbacks are NULL!\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ } else if ((num_cal_types <= 0) ||
+ (num_cal_types > MAX_CAL_TYPES)) {
+ pr_err("%s: num_cal_types of %d is Invalid!\n",
+ __func__, num_cal_types);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ for (; i < num_cal_types; i++) {
+ if ((reg_data[i].cal_type < 0) ||
+ (reg_data[i].cal_type >= MAX_CAL_TYPES)) {
+ pr_err("%s: cal type %d at index %d is Invalid!\n",
+ __func__, reg_data[i].cal_type, i);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ client_info_node = kmalloc(sizeof(*client_info_node),
+ GFP_KERNEL);
+ if (client_info_node == NULL) {
+ pr_err("%s: could not allocated client_info_node!\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err;
+ }
+ INIT_LIST_HEAD(&client_info_node->list);
+
+ callback_node = kmalloc(sizeof(*callback_node),
+ GFP_KERNEL);
+ if (callback_node == NULL) {
+ pr_err("%s: could not allocated callback_node!\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ memcpy(callback_node, &reg_data[i].callbacks,
+ sizeof(*callback_node));
+ client_info_node->callbacks = callback_node;
+
+ mutex_lock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
+ list_add_tail(&client_info_node->list,
+ &audio_cal.client_info[reg_data[i].cal_type]);
+ mutex_unlock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
+ }
+done:
+ return ret;
+err:
+ audio_cal_deregister(num_cal_types, reg_data);
+ return ret;
+}
+
+static int call_allocs(int32_t cal_type,
+ size_t cal_type_size, void *data)
+{
+ int ret = 0;
+ int ret2 = 0;
+ struct list_head *ptr, *next;
+ struct audio_cal_client_info *client_info_node = NULL;
+ pr_debug("%s\n", __func__);
+
+ list_for_each_safe(ptr, next,
+ &audio_cal.client_info[cal_type]) {
+
+ client_info_node = list_entry(ptr,
+ struct audio_cal_client_info, list);
+
+ if (client_info_node->callbacks->alloc == NULL)
+ continue;
+
+ ret2 = client_info_node->callbacks->
+ alloc(cal_type, cal_type_size, data);
+ if (ret2 < 0) {
+ pr_err("%s: alloc failed!\n", __func__);
+ ret = ret2;
+ }
+ }
+ return ret;
+}
+
+static int call_deallocs(int32_t cal_type,
+ size_t cal_type_size, void *data)
+{
+ int ret = 0;
+ int ret2 = 0;
+ struct list_head *ptr, *next;
+ struct audio_cal_client_info *client_info_node = NULL;
+ pr_debug("%s cal type %d\n", __func__, cal_type);
+
+ list_for_each_safe(ptr, next,
+ &audio_cal.client_info[cal_type]) {
+
+ client_info_node = list_entry(ptr,
+ struct audio_cal_client_info, list);
+
+ if (client_info_node->callbacks->dealloc == NULL)
+ continue;
+
+ ret2 = client_info_node->callbacks->
+ dealloc(cal_type, cal_type_size, data);
+ if (ret2 < 0) {
+ pr_err("%s: dealloc failed!\n", __func__);
+ ret = ret2;
+ }
+ }
+ return ret;
+}
+
+static int call_pre_cals(int32_t cal_type,
+ size_t cal_type_size, void *data)
+{
+ int ret = 0;
+ int ret2 = 0;
+ struct list_head *ptr, *next;
+ struct audio_cal_client_info *client_info_node = NULL;
+ pr_debug("%s cal type %d\n", __func__, cal_type);
+
+ list_for_each_safe(ptr, next,
+ &audio_cal.client_info[cal_type]) {
+
+ client_info_node = list_entry(ptr,
+ struct audio_cal_client_info, list);
+
+ if (client_info_node->callbacks->pre_cal == NULL)
+ continue;
+
+ ret2 = client_info_node->callbacks->
+ pre_cal(cal_type, cal_type_size, data);
+ if (ret2 < 0) {
+ pr_err("%s: pre_cal failed!\n", __func__);
+ ret = ret2;
+ }
+ }
+ return ret;
+}
+
+static int call_post_cals(int32_t cal_type,
+ size_t cal_type_size, void *data)
+{
+ int ret = 0;
+ int ret2 = 0;
+ struct list_head *ptr, *next;
+ struct audio_cal_client_info *client_info_node = NULL;
+ pr_debug("%s cal type %d\n", __func__, cal_type);
+
+ list_for_each_safe(ptr, next,
+ &audio_cal.client_info[cal_type]) {
+
+ client_info_node = list_entry(ptr,
+ struct audio_cal_client_info, list);
+
+ if (client_info_node->callbacks->post_cal == NULL)
+ continue;
+
+ ret2 = client_info_node->callbacks->
+ post_cal(cal_type, cal_type_size, data);
+ if (ret2 < 0) {
+ pr_err("%s: post_cal failed!\n", __func__);
+ ret = ret2;
+ }
+ }
+ return ret;
+}
+
+static int call_set_cals(int32_t cal_type,
+ size_t cal_type_size, void *data)
+{
+ int ret = 0;
+ int ret2 = 0;
+ struct list_head *ptr, *next;
+ struct audio_cal_client_info *client_info_node = NULL;
+ pr_debug("%s cal type %d\n", __func__, cal_type);
+
+ list_for_each_safe(ptr, next,
+ &audio_cal.client_info[cal_type]) {
+
+ client_info_node = list_entry(ptr,
+ struct audio_cal_client_info, list);
+
+ if (client_info_node->callbacks->set_cal == NULL)
+ continue;
+
+ ret2 = client_info_node->callbacks->
+ set_cal(cal_type, cal_type_size, data);
+ if (ret2 < 0) {
+ pr_err("%s: set_cal failed!\n", __func__);
+ ret = ret2;
+ }
+ }
+ return ret;
+}
+
+static int call_get_cals(int32_t cal_type,
+ size_t cal_type_size, void *data)
+{
+ int ret = 0;
+ int ret2 = 0;
+ struct list_head *ptr, *next;
+ struct audio_cal_client_info *client_info_node = NULL;
+ pr_debug("%s cal type %d\n", __func__, cal_type);
+
+ list_for_each_safe(ptr, next,
+ &audio_cal.client_info[cal_type]) {
+
+ client_info_node = list_entry(ptr,
+ struct audio_cal_client_info, list);
+
+ if (client_info_node->callbacks->get_cal == NULL)
+ continue;
+
+ ret2 = client_info_node->callbacks->
+ get_cal(cal_type, cal_type_size, data);
+ if (ret2 < 0) {
+ pr_err("%s: get_cal failed!\n", __func__);
+ ret = ret2;
+ }
+ }
+ return ret;
+}
+
+static int audio_cal_open(struct inode *inode, struct file *f)
+{
+ int ret = 0;
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&audio_cal.common_lock);
+ audio_cal.ref_count++;
+ mutex_unlock(&audio_cal.common_lock);
+
+ return ret;
+}
+
+static void dealloc_all_clients(void)
+{
+ int i = 0;
+ struct audio_cal_type_dealloc dealloc_data;
+ pr_debug("%s\n", __func__);
+
+ dealloc_data.cal_hdr.version = VERSION_0_0;
+ dealloc_data.cal_hdr.buffer_number = ALL_CAL_BLOCKS;
+ dealloc_data.cal_data.mem_handle = -1;
+
+ for (; i < MAX_CAL_TYPES; i++)
+ call_deallocs(i, sizeof(dealloc_data), &dealloc_data);
+}
+
+static int audio_cal_release(struct inode *inode, struct file *f)
+{
+ int ret = 0;
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&audio_cal.common_lock);
+ audio_cal.ref_count--;
+ if (audio_cal.ref_count <= 0) {
+ audio_cal.ref_count = 0;
+ dealloc_all_clients();
+ }
+ mutex_unlock(&audio_cal.common_lock);
+
+ return ret;
+}
+
+static long audio_cal_shared_ioctl(struct file *file, unsigned int cmd,
+ void __user *arg)
+{
+ int ret = 0;
+ int32_t size;
+ struct audio_cal_basic *data = NULL;
+ pr_debug("%s\n", __func__);
+
+ switch (cmd) {
+ case AUDIO_ALLOCATE_CALIBRATION:
+ case AUDIO_DEALLOCATE_CALIBRATION:
+ case AUDIO_PREPARE_CALIBRATION:
+ case AUDIO_SET_CALIBRATION:
+ case AUDIO_GET_CALIBRATION:
+ case AUDIO_POST_CALIBRATION:
+ break;
+ default:
+ pr_err("%s: ioctl not found!\n", __func__);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (copy_from_user(&size, (void *)arg, sizeof(size))) {
+ pr_err("%s: Could not copy size value from user\n", __func__);
+ ret = -EFAULT;
+ goto done;
+ } else if ((size < sizeof(struct audio_cal_basic))
+ || (size > MAX_IOCTL_CMD_SIZE)) {
+ pr_err("%s: Invalid size sent to driver: %d, max size is %d, min size is %zd\n",
+ __func__, size, MAX_IOCTL_CMD_SIZE,
+ sizeof(struct audio_cal_basic));
+ ret = -EINVAL;
+ goto done;
+ }
+
+ data = kmalloc(size, GFP_KERNEL);
+ if (data == NULL) {
+ pr_err("%s: Could not allocate memory of size %d for ioctl\n",
+ __func__, size);
+ ret = -ENOMEM;
+ goto done;
+ } else if (copy_from_user(data, (void *)arg, size)) {
+ pr_err("%s: Could not copy data from user\n",
+ __func__);
+ ret = -EFAULT;
+ goto done;
+ } else if ((data->hdr.cal_type < 0) ||
+ (data->hdr.cal_type >= MAX_CAL_TYPES)) {
+ pr_err("%s: cal type %d is Invalid!\n",
+ __func__, data->hdr.cal_type);
+ ret = -EINVAL;
+ goto done;
+ } else if ((data->hdr.cal_type_size <
+ sizeof(struct audio_cal_type_basic)) ||
+ (data->hdr.cal_type_size >
+ get_user_cal_type_size(data->hdr.cal_type))) {
+ pr_err("%s: cal type size %d is Invalid! Max is %zd!\n",
+ __func__, data->hdr.cal_type_size,
+ get_user_cal_type_size(data->hdr.cal_type));
+ ret = -EINVAL;
+ goto done;
+ } else if (data->cal_type.cal_hdr.buffer_number < 0) {
+ pr_err("%s: cal type %d Invalid buffer number %d!\n",
+ __func__, data->hdr.cal_type,
+ data->cal_type.cal_hdr.buffer_number);
+ ret = -EINVAL;
+ goto done;
+ } else if ((data->hdr.cal_type_size + sizeof(data->hdr)) > size) {
+ pr_err("%s: cal type hdr size %zd + cal type size %d is greater than user buffer size %d\n",
+ __func__, sizeof(data->hdr), data->hdr.cal_type_size,
+ size);
+ ret = -EFAULT;
+ goto done;
+ }
+
+
+ mutex_lock(&audio_cal.cal_mutex[data->hdr.cal_type]);
+
+ switch (cmd) {
+ case AUDIO_ALLOCATE_CALIBRATION:
+ ret = call_allocs(data->hdr.cal_type,
+ data->hdr.cal_type_size, &data->cal_type);
+ break;
+ case AUDIO_DEALLOCATE_CALIBRATION:
+ ret = call_deallocs(data->hdr.cal_type,
+ data->hdr.cal_type_size, &data->cal_type);
+ break;
+ case AUDIO_PREPARE_CALIBRATION:
+ ret = call_pre_cals(data->hdr.cal_type,
+ data->hdr.cal_type_size, &data->cal_type);
+ break;
+ case AUDIO_SET_CALIBRATION:
+ ret = call_set_cals(data->hdr.cal_type,
+ data->hdr.cal_type_size, &data->cal_type);
+ break;
+ case AUDIO_GET_CALIBRATION:
+ ret = call_get_cals(data->hdr.cal_type,
+ data->hdr.cal_type_size, &data->cal_type);
+ break;
+ case AUDIO_POST_CALIBRATION:
+ ret = call_post_cals(data->hdr.cal_type,
+ data->hdr.cal_type_size, &data->cal_type);
+ break;
+ }
+
+ if (cmd == AUDIO_GET_CALIBRATION) {
+ if (data->hdr.cal_type_size == 0)
+ goto unlock;
+ if (data == NULL)
+ goto unlock;
+ if (copy_to_user(arg, data,
+ sizeof(data->hdr) + data->hdr.cal_type_size)) {
+ pr_err("%s: Could not copy cal type to user\n",
+ __func__);
+ ret = -EFAULT;
+ goto unlock;
+ }
+ }
+
+unlock:
+ mutex_unlock(&audio_cal.cal_mutex[data->hdr.cal_type]);
+done:
+ kfree(data);
+ return ret;
+}
+
+static long audio_cal_ioctl(struct file *f,
+ unsigned int cmd, unsigned long arg)
+{
+ return audio_cal_shared_ioctl(f, cmd, (void __user *)arg);
+}
+
+#ifdef CONFIG_COMPAT
+
+#define AUDIO_ALLOCATE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
+ 200, compat_uptr_t)
+#define AUDIO_DEALLOCATE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
+ 201, compat_uptr_t)
+#define AUDIO_PREPARE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
+ 202, compat_uptr_t)
+#define AUDIO_SET_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
+ 203, compat_uptr_t)
+#define AUDIO_GET_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
+ 204, compat_uptr_t)
+#define AUDIO_POST_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
+ 205, compat_uptr_t)
+
+static long audio_cal_compat_ioctl(struct file *f,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int cmd64;
+ int ret = 0;
+
+ switch (cmd) {
+ case AUDIO_ALLOCATE_CALIBRATION32:
+ cmd64 = AUDIO_ALLOCATE_CALIBRATION;
+ break;
+ case AUDIO_DEALLOCATE_CALIBRATION32:
+ cmd64 = AUDIO_DEALLOCATE_CALIBRATION;
+ break;
+ case AUDIO_PREPARE_CALIBRATION32:
+ cmd64 = AUDIO_PREPARE_CALIBRATION;
+ break;
+ case AUDIO_SET_CALIBRATION32:
+ cmd64 = AUDIO_SET_CALIBRATION;
+ break;
+ case AUDIO_GET_CALIBRATION32:
+ cmd64 = AUDIO_GET_CALIBRATION;
+ break;
+ case AUDIO_POST_CALIBRATION32:
+ cmd64 = AUDIO_POST_CALIBRATION;
+ break;
+ default:
+ pr_err("%s: ioctl not found!\n", __func__);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = audio_cal_shared_ioctl(f, cmd64, compat_ptr(arg));
+done:
+ return ret;
+}
+#endif
+
+static const struct file_operations audio_cal_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_cal_open,
+ .release = audio_cal_release,
+ .unlocked_ioctl = audio_cal_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = audio_cal_compat_ioctl,
+#endif
+};
+
+struct miscdevice audio_cal_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_audio_cal",
+ .fops = &audio_cal_fops,
+};
+
+static int __init audio_cal_init(void)
+{
+ int i = 0;
+ pr_debug("%s\n", __func__);
+
+ memset(&audio_cal, 0, sizeof(audio_cal));
+ mutex_init(&audio_cal.common_lock);
+ for (; i < MAX_CAL_TYPES; i++) {
+ INIT_LIST_HEAD(&audio_cal.client_info[i]);
+ mutex_init(&audio_cal.cal_mutex[i]);
+ }
+
+ return misc_register(&audio_cal_misc);
+}
+
+static void __exit audio_cal_exit(void)
+{
+ int i = 0;
+ struct list_head *ptr, *next;
+ struct audio_cal_client_info *client_info_node;
+
+ for (; i < MAX_CAL_TYPES; i++) {
+ list_for_each_safe(ptr, next,
+ &audio_cal.client_info[i]) {
+ client_info_node = list_entry(ptr,
+ struct audio_cal_client_info, list);
+ list_del(&client_info_node->list);
+ kfree(client_info_node->callbacks);
+ client_info_node->callbacks = NULL;
+ kfree(client_info_node);
+ client_info_node = NULL;
+ }
+ }
+}
+
+subsys_initcall(audio_cal_init);
+module_exit(audio_cal_exit);
+
+MODULE_DESCRIPTION("SoC QDSP6v2 Audio Calibration driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/audio_slimslave.c b/sound/soc/msm/qdsp6v2/audio_slimslave.c
new file mode 100644
index 000000000000..e9ecfd5b2e44
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/audio_slimslave.c
@@ -0,0 +1,177 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/miscdevice.h>
+#include <sound/audio_slimslave.h>
+#include <linux/slimbus/slimbus.h>
+#include <linux/pm_runtime.h>
+
+static struct slim_device *slim;
+static int vote_count;
+struct mutex suspend_lock;
+bool suspend;
+
+static int audio_slim_open(struct inode *inode, struct file *file)
+{
+ pr_debug("%s:\n", __func__);
+
+ if (vote_count) {
+ pr_debug("%s: unvote: vote_count=%d\n", __func__, vote_count);
+ pm_runtime_mark_last_busy(slim->dev.parent);
+ pm_runtime_put(slim->dev.parent);
+ vote_count--;
+ }
+ return 0;
+};
+
+static int audio_slim_release(struct inode *inode, struct file *file)
+{
+ pr_debug("%s:\n", __func__);
+
+ if (vote_count) {
+ pr_debug("%s: unvote: vote_count=%d\n", __func__, vote_count);
+ pm_runtime_mark_last_busy(slim->dev.parent);
+ pm_runtime_put(slim->dev.parent);
+ vote_count--;
+ } else {
+ pr_debug("%s: vote: vote_count=%d\n", __func__, vote_count);
+ pm_runtime_get_sync(slim->dev.parent);
+ vote_count++;
+ }
+ return 0;
+};
+
+static long audio_slim_ioctl(struct file *file, unsigned int cmd,
+ unsigned long u_arg)
+{
+ switch (cmd) {
+ case AUDIO_SLIMSLAVE_VOTE:
+ mutex_lock(&suspend_lock);
+ if (!vote_count && !suspend) {
+ pr_debug("%s:AUDIO_SLIMSLAVE_VOTE\n", __func__);
+ pm_runtime_get_sync(slim->dev.parent);
+ vote_count++;
+ } else {
+ pr_err("%s:Invalid vote: vote_count=%d suspend=%d\n",
+ __func__, vote_count, suspend);
+ }
+ mutex_unlock(&suspend_lock);
+ break;
+ case AUDIO_SLIMSLAVE_UNVOTE:
+ mutex_lock(&suspend_lock);
+ if (vote_count && !suspend) {
+ pr_debug("%s:AUDIO_SLIMSLAVE_UNVOTE\n", __func__);
+ pm_runtime_mark_last_busy(slim->dev.parent);
+ pm_runtime_put(slim->dev.parent);
+ vote_count--;
+ } else {
+ pr_err("%s:Invalid unvote: vote_count=%d suspend=%d\n",
+ __func__, vote_count, suspend);
+ }
+ mutex_unlock(&suspend_lock);
+ break;
+ default:
+ pr_debug("%s: Invalid ioctl cmd: %d\n", __func__, cmd);
+ break;
+ }
+ return 0;
+}
+
+static const struct file_operations audio_slimslave_fops = {
+ .open = audio_slim_open,
+ .unlocked_ioctl = audio_slim_ioctl,
+ .release = audio_slim_release,
+};
+
+struct miscdevice audio_slimslave_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = AUDIO_SLIMSLAVE_IOCTL_NAME,
+ .fops = &audio_slimslave_fops,
+};
+
+static int audio_slimslave_probe(struct slim_device *audio_slim)
+{
+ pr_debug("%s:\n", __func__);
+
+ mutex_init(&suspend_lock);
+ suspend = false;
+ slim = audio_slim;
+ misc_register(&audio_slimslave_misc);
+ return 0;
+}
+
+static int audio_slimslave_remove(struct slim_device *audio_slim)
+{
+ pr_debug("%s:\n", __func__);
+
+ misc_deregister(&audio_slimslave_misc);
+ return 0;
+}
+
+static int audio_slimslave_resume(struct slim_device *audio_slim)
+{
+ pr_debug("%s:\n", __func__);
+
+ mutex_lock(&suspend_lock);
+ suspend = false;
+ mutex_unlock(&suspend_lock);
+ return 0;
+}
+
+static int audio_slimslave_suspend(struct slim_device *audio_slim,
+ pm_message_t pmesg)
+{
+ pr_debug("%s:\n", __func__);
+
+ mutex_lock(&suspend_lock);
+ suspend = true;
+ mutex_unlock(&suspend_lock);
+ return 0;
+}
+
+static const struct slim_device_id audio_slimslave_dt_match[] = {
+ {"audio-slimslave", 0},
+ {}
+};
+
+static struct slim_driver audio_slimslave_driver = {
+ .driver = {
+ .name = "audio-slimslave",
+ .owner = THIS_MODULE,
+ },
+ .probe = audio_slimslave_probe,
+ .remove = audio_slimslave_remove,
+ .id_table = audio_slimslave_dt_match,
+ .resume = audio_slimslave_resume,
+ .suspend = audio_slimslave_suspend,
+};
+
+static int __init audio_slimslave_init(void)
+{
+ return slim_driver_register(&audio_slimslave_driver);
+}
+module_init(audio_slimslave_init);
+
+static void __exit audio_slimslave_exit(void)
+{
+
+}
+module_exit(audio_slimslave_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("Audio side Slimbus slave driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c
new file mode 100644
index 000000000000..131a9bd87183
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c
@@ -0,0 +1,1339 @@
+/* Copyright (c) 2013-2017, 2019 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/ratelimit.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/compress_params.h>
+#include <sound/msm-audio-effects-q6-v2.h>
+#include <sound/devdep_params.h>
+#include <sound/q6common.h>
+
+#define MAX_ENABLE_CMD_SIZE 32
+
+#define GET_NEXT(ptr, upper_limit, rc) \
+({ \
+ if (((ptr) + 1) > (upper_limit)) { \
+ pr_err_ratelimited("%s: param list out of boundary\n", \
+ __func__); \
+ (rc) = -EINVAL; \
+ } \
+ ((rc) == 0) ? *(ptr)++ : -EINVAL; \
+})
+
+#define CHECK_PARAM_LEN(len, max_len, tag, rc) \
+do { \
+ if ((len) > (max_len)) { \
+ pr_err_ratelimited("%s: params length overflows\n", \
+ (tag)); \
+ (rc) = -EINVAL; \
+ } \
+} while (0)
+
+
+bool msm_audio_effects_is_effmodule_supp_in_top(int effect_module,
+ int topology)
+{
+ switch (effect_module) {
+ case VIRTUALIZER_MODULE:
+ case REVERB_MODULE:
+ case BASS_BOOST_MODULE:
+ case PBE_MODULE:
+ case EQ_MODULE:
+ switch (topology) {
+ case ASM_STREAM_POSTPROC_TOPO_ID_SA_PLUS:
+ return true;
+ default:
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+int msm_audio_effects_enable_extn(struct audio_client *ac,
+ struct msm_nt_eff_all_config *effects,
+ bool flag)
+{
+ u32 flag_param = flag ? 1 : 0;
+ struct param_hdr_v3 param_hdr = {0};
+ int rc = 0;
+
+ pr_debug("%s\n", __func__);
+ param_hdr.module_id = AUDPROC_MODULE_ID_VIRTUALIZER;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE;
+ param_hdr.param_size = VIRTUALIZER_ENABLE_PARAM_SZ;
+ if (effects->virtualizer.enable_flag)
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_hdr,
+ (u8 *) &flag_param);
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_BASS_BOOST;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE;
+ param_hdr.param_size = BASS_BOOST_ENABLE_PARAM_SZ;
+ if (effects->bass_boost.enable_flag)
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_hdr,
+ (u8 *) &flag_param);
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_POPLESS_EQUALIZER;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE;
+ param_hdr.param_size = EQ_ENABLE_PARAM_SZ;
+ if (effects->equalizer.enable_flag)
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_hdr,
+ (u8 *) &flag_param);
+
+ return rc;
+}
+
+int msm_audio_effects_virtualizer_handler(struct audio_client *ac,
+ struct virtualizer_params *virtualizer,
+ long *values)
+{
+ long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+ char *params = NULL;
+ u8 *updt_params;
+ int rc = 0;
+ int devices = GET_NEXT(values, param_max_offset, rc);
+ int num_commands = GET_NEXT(values, param_max_offset, rc);
+ int i, prev_enable_flag;
+ uint32_t max_params_length = 0;
+ uint32_t params_length = 0;
+ struct param_hdr_v3 param_hdr = {0};
+ u8 *param_data = NULL;
+ u32 packed_data_size = 0;
+
+ pr_debug("%s\n", __func__);
+ if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+ pr_err("%s: cannot set audio effects\n", __func__);
+ return -EINVAL;
+ }
+ params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL);
+ if (!params) {
+ pr_err("%s, params memory alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ pr_debug("%s: device: %d\n", __func__, devices);
+ updt_params = (u8 *) params;
+ /* Set MID and IID once at top and only update param specific fields*/
+ param_hdr.module_id = AUDPROC_MODULE_ID_VIRTUALIZER;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ for (i = 0; i < num_commands; i++) {
+ uint32_t command_id =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t command_config_state =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t index_offset =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t length =
+ GET_NEXT(values, param_max_offset, rc);
+ switch (command_id) {
+ case VIRTUALIZER_ENABLE:
+ if (length != 1 || index_offset != 0) {
+ pr_err("VIRT ENABLE:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ prev_enable_flag = virtualizer->enable_flag;
+ virtualizer->enable_flag =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s:VIRT ENABLE prev:%d, new:%d\n", __func__,
+ prev_enable_flag, virtualizer->enable_flag);
+ if (prev_enable_flag == virtualizer->enable_flag)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ VIRTUALIZER_ENABLE_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "VIRT ENABLE", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id =
+ AUDPROC_PARAM_ID_VIRTUALIZER_ENABLE;
+ param_hdr.param_size = VIRTUALIZER_ENABLE_PARAM_SZ;
+ param_data = (u8 *) &virtualizer->enable_flag;
+ break;
+ case VIRTUALIZER_STRENGTH:
+ if (length != 1 || index_offset != 0) {
+ pr_err("VIRT STRENGTH:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ virtualizer->strength =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: VIRT STRENGTH val: %d\n",
+ __func__, virtualizer->strength);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ VIRTUALIZER_STRENGTH_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "VIRT STRENGTH", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id =
+ AUDPROC_PARAM_ID_VIRTUALIZER_STRENGTH;
+ param_hdr.param_size = VIRTUALIZER_STRENGTH_PARAM_SZ;
+ param_data = (u8 *) &virtualizer->strength;
+ break;
+ case VIRTUALIZER_OUT_TYPE:
+ if (length != 1 || index_offset != 0) {
+ pr_err("VIRT OUT_TYPE:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ virtualizer->out_type =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: VIRT OUT_TYPE val:%d\n",
+ __func__, virtualizer->out_type);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ VIRTUALIZER_OUT_TYPE_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "VIRT OUT_TYPE", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id =
+ AUDPROC_PARAM_ID_VIRTUALIZER_OUT_TYPE;
+ param_hdr.param_size = VIRTUALIZER_OUT_TYPE_PARAM_SZ;
+ param_data = (u8 *) &virtualizer->out_type;
+ break;
+ case VIRTUALIZER_GAIN_ADJUST:
+ if (length != 1 || index_offset != 0) {
+ pr_err("VIRT GAIN_ADJUST: invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ virtualizer->gain_adjust =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: VIRT GAIN_ADJUST val:%d\n",
+ __func__, virtualizer->gain_adjust);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ VIRTUALIZER_GAIN_ADJUST_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "VIRT GAIN_ADJUST", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id =
+ AUDPROC_PARAM_ID_VIRTUALIZER_GAIN_ADJUST;
+ param_hdr.param_size = VIRTUALIZER_GAIN_ADJUST_PARAM_SZ;
+ param_data = (u8 *) &virtualizer->gain_adjust;
+ break;
+ default:
+ pr_err_ratelimited("%s: Invalid command to set config\n",
+ __func__);
+ continue;
+ }
+ if (rc)
+ goto invalid_config;
+
+ rc = q6common_pack_pp_params(updt_params, &param_hdr,
+ param_data, &packed_data_size);
+ if (rc) {
+ pr_err("%s: Failed to pack params, error %d\n",
+ __func__, rc);
+ goto invalid_config;
+ }
+
+ updt_params += packed_data_size;
+ params_length += packed_data_size;
+ }
+ if (params_length && (rc == 0))
+ q6asm_set_pp_params(ac, NULL, params, params_length);
+ else
+ pr_debug("%s: did not send pp params\n", __func__);
+invalid_config:
+ kfree(params);
+ return rc;
+}
+
+int msm_audio_effects_reverb_handler(struct audio_client *ac,
+ struct reverb_params *reverb,
+ long *values)
+{
+ long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+ char *params = NULL;
+ u8 *updt_params;
+ int rc = 0;
+ int devices = GET_NEXT(values, param_max_offset, rc);
+ int num_commands = GET_NEXT(values, param_max_offset, rc);
+ int i, prev_enable_flag;
+ uint32_t max_params_length = 0;
+ uint32_t params_length = 0;
+ struct param_hdr_v3 param_hdr = {0};
+ u8 *param_data = NULL;
+ u32 packed_data_size = 0;
+
+ pr_debug("%s\n", __func__);
+ if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+ pr_err("%s: cannot set audio effects\n", __func__);
+ return -EINVAL;
+ }
+ params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL);
+ if (!params) {
+ pr_err("%s, params memory alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ pr_debug("%s: device: %d\n", __func__, devices);
+ updt_params = (u8 *) params;
+ /* Set MID and IID once at top and only update param specific fields*/
+ param_hdr.module_id = AUDPROC_MODULE_ID_REVERB;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ for (i = 0; i < num_commands; i++) {
+ uint32_t command_id =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t command_config_state =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t index_offset =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t length =
+ GET_NEXT(values, param_max_offset, rc);
+ switch (command_id) {
+ case REVERB_ENABLE:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_ENABLE:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ prev_enable_flag = reverb->enable_flag;
+ reverb->enable_flag =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s:REVERB_ENABLE prev:%d,new:%d\n", __func__,
+ prev_enable_flag, reverb->enable_flag);
+ if (prev_enable_flag == reverb->enable_flag)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_ENABLE_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_ENABLE", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_ENABLE;
+ param_hdr.param_size = REVERB_ENABLE_PARAM_SZ;
+ param_data = (u8 *) &reverb->enable_flag;
+ break;
+ case REVERB_MODE:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_MODE:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->mode =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_MODE val:%d\n",
+ __func__, reverb->mode);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_MODE_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_MODE", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_MODE;
+ param_hdr.param_size = REVERB_MODE_PARAM_SZ;
+ param_data = (u8 *) &reverb->mode;
+ break;
+ case REVERB_PRESET:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_PRESET:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->preset =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_PRESET val:%d\n",
+ __func__, reverb->preset);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_PRESET_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_PRESET", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_PRESET;
+ param_hdr.param_size = REVERB_PRESET_PARAM_SZ;
+ param_data = (u8 *) &reverb->preset;
+ break;
+ case REVERB_WET_MIX:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_WET_MIX:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->wet_mix =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_WET_MIX val:%d\n",
+ __func__, reverb->wet_mix);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_WET_MIX_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_WET_MIX", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_WET_MIX;
+ param_hdr.param_size = REVERB_WET_MIX_PARAM_SZ;
+ param_data = (u8 *) &reverb->wet_mix;
+ break;
+ case REVERB_GAIN_ADJUST:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_GAIN_ADJUST:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->gain_adjust =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_GAIN_ADJUST val:%d\n",
+ __func__, reverb->gain_adjust);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_GAIN_ADJUST_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_GAIN_ADJUST", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id =
+ AUDPROC_PARAM_ID_REVERB_GAIN_ADJUST;
+ param_hdr.param_size = REVERB_GAIN_ADJUST_PARAM_SZ;
+ param_data = (u8 *) &reverb->gain_adjust;
+ break;
+ case REVERB_ROOM_LEVEL:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_ROOM_LEVEL:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->room_level =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_ROOM_LEVEL val:%d\n",
+ __func__, reverb->room_level);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_ROOM_LEVEL_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_ROOM_LEVEL", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_ROOM_LEVEL;
+ param_hdr.param_size = REVERB_ROOM_LEVEL_PARAM_SZ;
+ param_data = (u8 *) &reverb->room_level;
+ break;
+ case REVERB_ROOM_HF_LEVEL:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_ROOM_HF_LEVEL:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->room_hf_level =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_ROOM_HF_LEVEL val%d\n",
+ __func__, reverb->room_hf_level);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_ROOM_HF_LEVEL_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_ROOM_HF_LEVEL", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id =
+ AUDPROC_PARAM_ID_REVERB_ROOM_HF_LEVEL;
+ param_hdr.param_size = REVERB_ROOM_HF_LEVEL_PARAM_SZ;
+ param_data = (u8 *) &reverb->room_hf_level;
+ break;
+ case REVERB_DECAY_TIME:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_DECAY_TIME:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->decay_time =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_DECAY_TIME val:%d\n",
+ __func__, reverb->decay_time);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_DECAY_TIME_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_DECAY_TIME", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_DECAY_TIME;
+ param_hdr.param_size = REVERB_DECAY_TIME_PARAM_SZ;
+ param_data = (u8 *) &reverb->decay_time;
+ break;
+ case REVERB_DECAY_HF_RATIO:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_DECAY_HF_RATIOinvalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->decay_hf_ratio =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_DECAY_HF_RATIO val%d\n",
+ __func__, reverb->decay_hf_ratio);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_DECAY_HF_RATIO_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_DECAY_HF_RATIO", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id =
+ AUDPROC_PARAM_ID_REVERB_DECAY_HF_RATIO;
+ param_hdr.param_size = REVERB_DECAY_HF_RATIO_PARAM_SZ;
+ param_data = (u8 *) &reverb->decay_hf_ratio;
+ break;
+ case REVERB_REFLECTIONS_LEVEL:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_REFLECTION_LVLinvalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->reflections_level =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_REFLECTIONS_LEVEL val:%d\n",
+ __func__, reverb->reflections_level);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_REFLECTIONS_LEVEL_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_REFLECTIONS_LEVEL", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id =
+ AUDPROC_PARAM_ID_REVERB_REFLECTIONS_LEVEL;
+ param_hdr.param_size =
+ REVERB_REFLECTIONS_LEVEL_PARAM_SZ;
+ param_data = (u8 *) &reverb->reflections_level;
+ break;
+ case REVERB_REFLECTIONS_DELAY:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_REFLECTION_DLYinvalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->reflections_delay =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_REFLECTIONS_DELAY val:%d\n",
+ __func__, reverb->reflections_delay);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_REFLECTIONS_DELAY_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_REFLECTIONS_DELAY", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id =
+ AUDPROC_PARAM_ID_REVERB_REFLECTIONS_DELAY;
+ param_hdr.param_size =
+ REVERB_REFLECTIONS_DELAY_PARAM_SZ;
+ param_data = (u8 *) &reverb->reflections_delay;
+ break;
+ case REVERB_LEVEL:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_LEVEL:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->level =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_LEVEL val:%d\n",
+ __func__, reverb->level);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_LEVEL_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_LEVEL", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_LEVEL;
+ param_hdr.param_size = REVERB_LEVEL_PARAM_SZ;
+ param_data = (u8 *) &reverb->level;
+ break;
+ case REVERB_DELAY:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_DELAY:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->delay =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s:REVERB_DELAY val:%d\n",
+ __func__, reverb->delay);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_DELAY_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_DELAY", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_DELAY;
+ param_hdr.param_size = REVERB_DELAY_PARAM_SZ;
+ param_data = (u8 *) &reverb->delay;
+ break;
+ case REVERB_DIFFUSION:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_DIFFUSION:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->diffusion =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_DIFFUSION val:%d\n",
+ __func__, reverb->diffusion);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_DIFFUSION_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_DIFFUSION", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_DIFFUSION;
+ param_hdr.param_size = REVERB_DIFFUSION_PARAM_SZ;
+ param_data = (u8 *) &reverb->diffusion;
+ break;
+ case REVERB_DENSITY:
+ if (length != 1 || index_offset != 0) {
+ pr_err("REVERB_DENSITY:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ reverb->density =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: REVERB_DENSITY val:%d\n",
+ __func__, reverb->density);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ REVERB_DENSITY_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "REVERB_DENSITY", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_DENSITY;
+ param_hdr.param_size = REVERB_DENSITY_PARAM_SZ;
+ param_data = (u8 *) &reverb->density;
+ break;
+ default:
+ pr_err_ratelimited("%s: Invalid command to set config\n",
+ __func__);
+ continue;
+ }
+ if (rc)
+ goto invalid_config;
+
+ rc = q6common_pack_pp_params(updt_params, &param_hdr,
+ param_data, &packed_data_size);
+ if (rc) {
+ pr_err("%s: Failed to pack params, error %d\n",
+ __func__, rc);
+ goto invalid_config;
+ }
+
+ updt_params += packed_data_size;
+ params_length += packed_data_size;
+ }
+ if (params_length && (rc == 0))
+ q6asm_set_pp_params(ac, NULL, params, params_length);
+ else
+ pr_debug("%s: did not send pp params\n", __func__);
+invalid_config:
+ kfree(params);
+ return rc;
+}
+
+int msm_audio_effects_bass_boost_handler(struct audio_client *ac,
+ struct bass_boost_params *bass_boost,
+ long *values)
+{
+ long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+ char *params = NULL;
+ u8 *updt_params;
+ int rc = 0;
+ int devices = GET_NEXT(values, param_max_offset, rc);
+ int num_commands = GET_NEXT(values, param_max_offset, rc);
+ int i, prev_enable_flag;
+ uint32_t max_params_length = 0;
+ uint32_t params_length = 0;
+ struct param_hdr_v3 param_hdr = {0};
+ u8 *param_data = NULL;
+ u32 packed_data_size = 0;
+
+ pr_debug("%s\n", __func__);
+ if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+ pr_err("%s: cannot set audio effects\n", __func__);
+ return -EINVAL;
+ }
+ params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL);
+ if (!params) {
+ pr_err("%s, params memory alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ pr_debug("%s: device: %d\n", __func__, devices);
+ updt_params = (u8 *) params;
+ /* Set MID and IID once at top and only update param specific fields*/
+ param_hdr.module_id = AUDPROC_MODULE_ID_BASS_BOOST;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ for (i = 0; i < num_commands; i++) {
+ uint32_t command_id =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t command_config_state =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t index_offset =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t length =
+ GET_NEXT(values, param_max_offset, rc);
+ switch (command_id) {
+ case BASS_BOOST_ENABLE:
+ if (length != 1 || index_offset != 0) {
+ pr_err("BASS_BOOST_ENABLE:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ prev_enable_flag = bass_boost->enable_flag;
+ bass_boost->enable_flag =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: BASS_BOOST_ENABLE prev:%d new:%d\n",
+ __func__, prev_enable_flag,
+ bass_boost->enable_flag);
+ if (prev_enable_flag == bass_boost->enable_flag)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ BASS_BOOST_ENABLE_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "BASS_BOOST_ENABLE", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_BASS_BOOST_ENABLE;
+ param_hdr.param_size = BASS_BOOST_ENABLE_PARAM_SZ;
+ param_data = (u8 *) &bass_boost->enable_flag;
+ break;
+ case BASS_BOOST_MODE:
+ if (length != 1 || index_offset != 0) {
+ pr_err("BASS_BOOST_MODE:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ bass_boost->mode =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: BASS_BOOST_MODE val:%d\n",
+ __func__, bass_boost->mode);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ BASS_BOOST_MODE_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "BASS_BOOST_MODE", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_BASS_BOOST_MODE;
+ param_hdr.param_size = BASS_BOOST_MODE_PARAM_SZ;
+ param_data = (u8 *) &bass_boost->mode;
+ break;
+ case BASS_BOOST_STRENGTH:
+ if (length != 1 || index_offset != 0) {
+ pr_err("BASS_BOOST_STRENGTH:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ bass_boost->strength =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: BASS_BOOST_STRENGTH val:%d\n",
+ __func__, bass_boost->strength);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ BASS_BOOST_STRENGTH_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "BASS_BOOST_STRENGTH", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id =
+ AUDPROC_PARAM_ID_BASS_BOOST_STRENGTH;
+ param_hdr.param_size = BASS_BOOST_STRENGTH_PARAM_SZ;
+ param_data = (u8 *) &bass_boost->strength;
+ break;
+ default:
+ pr_err_ratelimited("%s: Invalid command to set config\n",
+ __func__);
+ continue;
+ }
+ if (rc)
+ goto invalid_config;
+
+ rc = q6common_pack_pp_params(updt_params, &param_hdr,
+ param_data, &packed_data_size);
+ if (rc) {
+ pr_err("%s: Failed to pack params, error %d\n",
+ __func__, rc);
+ goto invalid_config;
+ }
+
+ updt_params += packed_data_size;
+ params_length += packed_data_size;
+ }
+ if (params_length && (rc == 0))
+ q6asm_set_pp_params(ac, NULL, params, params_length);
+ else
+ pr_debug("%s: did not send pp params\n", __func__);
+invalid_config:
+ kfree(params);
+ return rc;
+}
+
+int msm_audio_effects_pbe_handler(struct audio_client *ac,
+ struct pbe_params *pbe,
+ long *values)
+{
+ long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+ char *params = NULL;
+ u8 *updt_params;
+ int rc = 0;
+ int devices = GET_NEXT(values, param_max_offset, rc);
+ int num_commands = GET_NEXT(values, param_max_offset, rc);
+ int i, prev_enable_flag;
+ uint32_t max_params_length = 0;
+ uint32_t params_length = 0;
+ struct param_hdr_v3 param_hdr = {0};
+ u8 *param_data = NULL;
+ u32 packed_data_size = 0;
+
+ pr_debug("%s\n", __func__);
+ if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+ pr_err("%s: cannot set audio effects\n", __func__);
+ return -EINVAL;
+ }
+ params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL);
+ if (!params) {
+ pr_err("%s, params memory alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ pr_debug("%s: device: %d\n", __func__, devices);
+ updt_params = (u8 *) params;
+ /* Set MID and IID once at top and only update param specific fields*/
+ param_hdr.module_id = AUDPROC_MODULE_ID_PBE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ for (i = 0; i < num_commands; i++) {
+ uint32_t command_id =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t command_config_state =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t index_offset =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t length =
+ GET_NEXT(values, param_max_offset, rc);
+ switch (command_id) {
+ case PBE_ENABLE:
+ pr_debug("%s: PBE_ENABLE\n", __func__);
+ if (length != 1 || index_offset != 0) {
+ pr_err("no valid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ prev_enable_flag = pbe->enable_flag;
+ pbe->enable_flag =
+ GET_NEXT(values, param_max_offset, rc);
+ if (prev_enable_flag == pbe->enable_flag)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ PBE_ENABLE_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "PBE_ENABLE", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_PBE_ENABLE;
+ param_hdr.param_size = PBE_ENABLE_PARAM_SZ;
+ param_data = (u8 *) &pbe->enable_flag;
+ break;
+ case PBE_CONFIG:
+ pr_debug("%s: PBE_PARAM length %u\n", __func__, length);
+ if (length > sizeof(struct pbe_config_t) ||
+ length < PBE_CONFIG_PARAM_LEN ||
+ index_offset != 0) {
+ pr_err("no valid params, len %d\n", length);
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length =
+ params_length + COMMAND_IID_PAYLOAD_SZ + length;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "PBE_PARAM", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_PBE_PARAM_CONFIG;
+ param_hdr.param_size = length;
+ param_data = (u8 *) values;
+ break;
+ default:
+ pr_err_ratelimited("%s: Invalid command to set config\n",
+ __func__);
+ continue;
+ }
+ if (rc)
+ goto invalid_config;
+
+ rc = q6common_pack_pp_params(updt_params, &param_hdr,
+ param_data, &packed_data_size);
+ if (rc) {
+ pr_err("%s: Failed to pack params, error %d\n",
+ __func__, rc);
+ goto invalid_config;
+ }
+
+ updt_params += packed_data_size;
+ params_length += packed_data_size;
+ }
+ if (params_length && (rc == 0))
+ q6asm_set_pp_params(ac, NULL, params, params_length);
+invalid_config:
+ kfree(params);
+ return rc;
+}
+
+int msm_audio_effects_popless_eq_handler(struct audio_client *ac,
+ struct eq_params *eq,
+ long *values)
+{
+ long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+ char *params = NULL;
+ u8 *updt_params = NULL;
+ int rc = 0;
+ int devices = GET_NEXT(values, param_max_offset, rc);
+ int num_commands = GET_NEXT(values, param_max_offset, rc);
+ int i, prev_enable_flag;
+ uint32_t max_params_length = 0;
+ uint32_t params_length = 0;
+ struct param_hdr_v3 param_hdr = {0};
+ u8 *param_data = NULL;
+ u32 packed_data_size = 0;
+ u8 *eq_config_data = NULL;
+ u32 *updt_config_data = NULL;
+ int config_param_length;
+
+ pr_debug("%s\n", __func__);
+ if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+ pr_err("%s: cannot set audio effects\n", __func__);
+ return -EINVAL;
+ }
+ params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL);
+ if (!params) {
+ pr_err("%s, params memory alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ pr_debug("%s: device: %d\n", __func__, devices);
+ updt_params = (u8 *) params;
+ /* Set MID and IID once at top and only update param specific fields*/
+ param_hdr.module_id = AUDPROC_MODULE_ID_POPLESS_EQUALIZER;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ for (i = 0; i < num_commands; i++) {
+ uint32_t command_id =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t command_config_state =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t index_offset =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t length =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t idx;
+ int j;
+ switch (command_id) {
+ case EQ_ENABLE:
+ if (length != 1 || index_offset != 0) {
+ pr_err("EQ_ENABLE:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ prev_enable_flag = eq->enable_flag;
+ eq->enable_flag =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: EQ_ENABLE prev:%d new:%d\n", __func__,
+ prev_enable_flag, eq->enable_flag);
+ if (prev_enable_flag == eq->enable_flag)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ EQ_ENABLE_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "EQ_ENABLE", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_EQ_ENABLE;
+ param_hdr.param_size = EQ_ENABLE_PARAM_SZ;
+ param_data = (u8 *) &eq->enable_flag;
+ break;
+ case EQ_CONFIG:
+ if (length < EQ_CONFIG_PARAM_LEN || index_offset != 0) {
+ pr_err("EQ_CONFIG:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ pr_debug("%s: EQ_CONFIG bands:%d, pgain:%d, pset:%d\n",
+ __func__, eq->config.num_bands,
+ eq->config.eq_pregain, eq->config.preset_id);
+ for (idx = 0; idx < MAX_EQ_BANDS; idx++)
+ eq->per_band_cfg[idx].band_idx = -1;
+ eq->config.eq_pregain =
+ GET_NEXT(values, param_max_offset, rc);
+ eq->config.preset_id =
+ GET_NEXT(values, param_max_offset, rc);
+ eq->config.num_bands =
+ GET_NEXT(values, param_max_offset, rc);
+ if (eq->config.num_bands > MAX_EQ_BANDS) {
+ pr_err("EQ_CONFIG:invalid num of bands\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ if (eq->config.num_bands &&
+ (((length - EQ_CONFIG_PARAM_LEN)/
+ EQ_CONFIG_PER_BAND_PARAM_LEN)
+ != eq->config.num_bands)) {
+ pr_err("EQ_CONFIG:invalid length per band\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ for (j = 0; j < eq->config.num_bands; j++) {
+ idx = GET_NEXT(values, param_max_offset, rc);
+ if (idx >= MAX_EQ_BANDS) {
+ pr_err("EQ_CONFIG:invalid band index\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ eq->per_band_cfg[idx].band_idx = idx;
+ eq->per_band_cfg[idx].filter_type =
+ GET_NEXT(values, param_max_offset, rc);
+ eq->per_band_cfg[idx].freq_millihertz =
+ GET_NEXT(values, param_max_offset, rc);
+ eq->per_band_cfg[idx].gain_millibels =
+ GET_NEXT(values, param_max_offset, rc);
+ eq->per_band_cfg[idx].quality_factor =
+ GET_NEXT(values, param_max_offset, rc);
+ }
+ if (command_config_state != CONFIG_SET)
+ break;
+ config_param_length = EQ_CONFIG_PARAM_SZ +
+ (EQ_CONFIG_PER_BAND_PARAM_SZ *
+ eq->config.num_bands);
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ config_param_length;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "EQ_CONFIG", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_EQ_CONFIG;
+ param_hdr.param_size = config_param_length;
+
+ if (!eq_config_data)
+ eq_config_data = kzalloc(config_param_length,
+ GFP_KERNEL);
+ else
+ memset(eq_config_data, 0, config_param_length);
+ if (!eq_config_data)
+ return -ENOMEM;
+ param_data = eq_config_data;
+ updt_config_data = (u32 *) eq_config_data;
+ *updt_config_data++ = eq->config.eq_pregain;
+ *updt_config_data++ = eq->config.preset_id;
+ *updt_config_data++ = eq->config.num_bands;
+ for (idx = 0; idx < MAX_EQ_BANDS; idx++) {
+ if (eq->per_band_cfg[idx].band_idx < 0)
+ continue;
+ *updt_config_data++ =
+ eq->per_band_cfg[idx].filter_type;
+ *updt_config_data++ =
+ eq->per_band_cfg[idx].freq_millihertz;
+ *updt_config_data++ =
+ eq->per_band_cfg[idx].gain_millibels;
+ *updt_config_data++ =
+ eq->per_band_cfg[idx].quality_factor;
+ *updt_config_data++ =
+ eq->per_band_cfg[idx].band_idx;
+ }
+ break;
+ case EQ_BAND_INDEX:
+ if (length != 1 || index_offset != 0) {
+ pr_err("EQ_BAND_INDEX:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ idx = GET_NEXT(values, param_max_offset, rc);
+ if (idx > MAX_EQ_BANDS) {
+ pr_err("EQ_BAND_INDEX:invalid band index\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ eq->band_index = idx;
+ pr_debug("%s: EQ_BAND_INDEX val:%d\n",
+ __func__, eq->band_index);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ EQ_BAND_INDEX_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "EQ_BAND_INDEX", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id = AUDPROC_PARAM_ID_EQ_BAND_INDEX;
+ param_hdr.param_size = EQ_BAND_INDEX_PARAM_SZ;
+ param_data = (u8 *) &eq->band_index;
+ break;
+ case EQ_SINGLE_BAND_FREQ:
+ if (length != 1 || index_offset != 0) {
+ pr_err("EQ_SINGLE_BAND_FREQ:invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ if (eq->band_index > MAX_EQ_BANDS) {
+ pr_err("EQ_SINGLE_BAND_FREQ:invalid index\n");
+ break;
+ }
+ eq->freq_millihertz =
+ GET_NEXT(values, param_max_offset, rc);
+ pr_debug("%s: EQ_SINGLE_BAND_FREQ idx:%d, val:%d\n",
+ __func__, eq->band_index, eq->freq_millihertz);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ EQ_SINGLE_BAND_FREQ_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "EQ_SINGLE_BAND_FREQ", rc);
+ if (rc != 0)
+ break;
+ param_hdr.param_id =
+ AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ;
+ param_hdr.param_size = EQ_SINGLE_BAND_FREQ_PARAM_SZ;
+ param_data = (u8 *) &eq->freq_millihertz;
+ break;
+ default:
+ pr_err_ratelimited("%s: Invalid command to set config\n",
+ __func__);
+ continue;
+ }
+ if (rc)
+ goto invalid_config;
+
+ rc = q6common_pack_pp_params(updt_params, &param_hdr,
+ param_data, &packed_data_size);
+ if (rc) {
+ pr_err("%s: Failed to pack params, error %d\n",
+ __func__, rc);
+ goto invalid_config;
+ }
+
+ updt_params += packed_data_size;
+ params_length += packed_data_size;
+ }
+ if (params_length && (rc == 0))
+ q6asm_set_pp_params(ac, NULL, params, params_length);
+ else
+ pr_debug("%s: did not send pp params\n", __func__);
+invalid_config:
+ kfree(params);
+ kfree(eq_config_data);
+ return rc;
+}
+
+static int __msm_audio_effects_volume_handler(struct audio_client *ac,
+ struct soft_volume_params *vol,
+ long *values,
+ int instance)
+{
+ int devices;
+ int num_commands;
+ char *params = NULL;
+ u8 *updt_params;
+ int i;
+ uint32_t vol_gain_2ch = 0;
+ uint32_t max_params_length = 0;
+ uint32_t params_length = 0;
+ struct param_hdr_v3 param_hdr = {0};
+ u32 packed_data_size = 0;
+ long *param_max_offset;
+ int rc = 0;
+
+ pr_debug("%s: instance: %d\n", __func__, instance);
+ if (!values) {
+ pr_err("%s: set audio effects failed, no valid data\n",
+ __func__);
+ return -EINVAL;
+ }
+ param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+ devices = GET_NEXT(values, param_max_offset, rc);
+ num_commands = GET_NEXT(values, param_max_offset, rc);
+ if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+ pr_err("%s: cannot set audio effects\n", __func__);
+ return -EINVAL;
+ }
+ params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL);
+ if (!params) {
+ pr_err("%s, params memory alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ updt_params = (u8 *) params;
+ /* Set MID and IID once at top and only update param specific fields*/
+ q6asm_set_soft_volume_module_instance_ids(instance, &param_hdr);
+ for (i = 0; i < num_commands; i++) {
+ uint32_t command_id =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t command_config_state =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t index_offset =
+ GET_NEXT(values, param_max_offset, rc);
+ uint32_t length =
+ GET_NEXT(values, param_max_offset, rc);
+ switch (command_id) {
+ case SOFT_VOLUME_GAIN_2CH:
+ case SOFT_VOLUME2_GAIN_2CH:
+ if (length != 2 || index_offset != 0) {
+ pr_err("VOLUME_GAIN_2CH: invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ vol->left_gain = GET_NEXT(values, param_max_offset, rc);
+ vol->right_gain =
+ GET_NEXT(values, param_max_offset, rc);
+ vol->master_gain = 0x2000;
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ SOFT_VOLUME_GAIN_2CH_PARAM_SZ +
+ COMMAND_IID_PAYLOAD_SZ +
+ SOFT_VOLUME_GAIN_MASTER_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "VOLUME/VOLUME2_GAIN_2CH", rc);
+ break;
+ case SOFT_VOLUME_GAIN_MASTER:
+ case SOFT_VOLUME2_GAIN_MASTER:
+ if (length != 1 || index_offset != 0) {
+ pr_err("VOLUME_GAIN_MASTER: invalid params\n");
+ rc = -EINVAL;
+ goto invalid_config;
+ }
+ vol->left_gain = 0x2000;
+ vol->right_gain = 0x2000;
+ vol->master_gain =
+ GET_NEXT(values, param_max_offset, rc);
+ if (command_config_state != CONFIG_SET)
+ break;
+ max_params_length = params_length +
+ COMMAND_IID_PAYLOAD_SZ +
+ SOFT_VOLUME_GAIN_2CH_PARAM_SZ +
+ COMMAND_IID_PAYLOAD_SZ +
+ SOFT_VOLUME_GAIN_MASTER_PARAM_SZ;
+ CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ,
+ "VOLUME/VOLUME2_GAIN_MASTER", rc);
+ break;
+ default:
+ pr_err_ratelimited("%s: Invalid command id: %d to set config\n",
+ __func__, command_id);
+ continue;
+ }
+ if (rc)
+ continue;
+
+ /* Set Volume Control for Left/Right */
+ param_hdr.param_id = ASM_PARAM_ID_VOL_CTRL_LR_CHANNEL_GAIN;
+ param_hdr.param_size = SOFT_VOLUME_GAIN_2CH_PARAM_SZ;
+ vol_gain_2ch = (vol->left_gain << 16) | vol->right_gain;
+ rc = q6common_pack_pp_params(updt_params, &param_hdr,
+ (u8 *) &vol_gain_2ch,
+ &packed_data_size);
+ if (rc) {
+ pr_err("%s: Failed to pack params, error %d\n",
+ __func__, rc);
+ goto invalid_config;
+ }
+
+ updt_params += packed_data_size;
+ params_length += packed_data_size;
+
+ /* Set Master Volume Control */
+ param_hdr.param_id = ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN;
+ param_hdr.param_size = SOFT_VOLUME_GAIN_MASTER_PARAM_SZ;
+ rc = q6common_pack_pp_params(updt_params, &param_hdr,
+ (u8 *) &vol->master_gain,
+ &packed_data_size);
+ if (rc) {
+ pr_err("%s: Failed to pack params, error %d\n",
+ __func__, rc);
+ goto invalid_config;
+ }
+
+ updt_params += packed_data_size;
+ params_length += packed_data_size;
+ }
+ if (params_length && (rc == 0))
+ q6asm_set_pp_params(ac, NULL, params, params_length);
+invalid_config:
+ kfree(params);
+ return rc;
+}
+
+int msm_audio_effects_volume_handler(struct audio_client *ac,
+ struct soft_volume_params *vol,
+ long *values)
+{
+ return __msm_audio_effects_volume_handler(ac, vol, values,
+ SOFT_VOLUME_INSTANCE_1);
+}
+
+int msm_audio_effects_volume_handler_v2(struct audio_client *ac,
+ struct soft_volume_params *vol,
+ long *values, int instance)
+{
+ return __msm_audio_effects_volume_handler(ac, vol, values, instance);
+}
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
new file mode 100644
index 000000000000..7a2c50c28e77
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -0,0 +1,4993 @@
+/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6asm-v2.h>
+#include <sound/pcm_params.h>
+#include <sound/audio_effects.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/msm_audio.h>
+
+#include <sound/timer.h>
+#include <sound/tlv.h>
+
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6core.h>
+#include <sound/compress_params.h>
+#include <sound/compress_offload.h>
+#include <sound/compress_driver.h>
+#include <sound/msm-audio-effects-q6-v2.h>
+#include "msm-pcm-routing-v2.h"
+#include "msm-qti-pp-config.h"
+
+#define DSP_PP_BUFFERING_IN_MSEC 25
+#define PARTIAL_DRAIN_ACK_EARLY_BY_MSEC 150
+#define MP3_OUTPUT_FRAME_SZ 1152
+#define AAC_OUTPUT_FRAME_SZ 1024
+#define AC3_OUTPUT_FRAME_SZ 1536
+#define EAC3_OUTPUT_FRAME_SZ 1536
+#define DSP_NUM_OUTPUT_FRAME_BUFFERED 2
+#define FLAC_BLK_SIZE_LIMIT 65535
+
+/* Timestamp mode payload offsets */
+#define CAPTURE_META_DATA_TS_OFFSET_LSW 6
+#define CAPTURE_META_DATA_TS_OFFSET_MSW 7
+
+/* decoder parameter length */
+#define DDP_DEC_MAX_NUM_PARAM 18
+
+/* Default values used if user space does not set */
+#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024)
+#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)
+#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4)
+#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4)
+
+#define COMPRESSED_LR_VOL_MAX_STEPS 0x2000
+const DECLARE_TLV_DB_LINEAR(msm_compr_vol_gain, 0,
+ COMPRESSED_LR_VOL_MAX_STEPS);
+
+/* Stream id switches between 1 and 2 */
+#define NEXT_STREAM_ID(stream_id) ((stream_id & 1) + 1)
+
+#define STREAM_ARRAY_INDEX(stream_id) (stream_id - 1)
+
+#define MAX_NUMBER_OF_STREAMS 2
+
+struct msm_compr_gapless_state {
+ bool set_next_stream_id;
+ int32_t stream_opened[MAX_NUMBER_OF_STREAMS];
+ uint32_t initial_samples_drop;
+ uint32_t trailing_samples_drop;
+ uint32_t gapless_transition;
+ bool use_dsp_gapless_mode;
+ union snd_codec_options codec_options;
+};
+
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000,
+ 88200, 96000, 128000, 144000, 176400, 192000, 352800, 384000, 2822400,
+ 5644800
+};
+
+struct msm_compr_pdata {
+ struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX];
+ uint32_t volume[MSM_FRONTEND_DAI_MAX][2]; /* For both L & R */
+ struct msm_compr_audio_effects *audio_effects[MSM_FRONTEND_DAI_MAX];
+ bool use_dsp_gapless_mode;
+ bool use_legacy_api; /* indicates use older asm apis*/
+ struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX];
+ struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX];
+ int32_t ion_fd[MSM_FRONTEND_DAI_MAX];
+ bool is_in_use[MSM_FRONTEND_DAI_MAX];
+ bool avs_ver;
+ struct mutex lock;
+};
+
+struct msm_compr_audio {
+ struct snd_compr_stream *cstream;
+ struct snd_compr_caps compr_cap;
+ struct snd_compr_codec_caps codec_caps;
+ struct snd_compr_params codec_param;
+ struct audio_client *audio_client;
+
+ uint32_t codec;
+ uint32_t compr_passthr;
+ void *buffer; /* virtual address */
+ phys_addr_t buffer_paddr; /* physical address */
+ uint32_t app_pointer;
+ uint32_t buffer_size;
+ uint32_t byte_offset;
+ uint64_t copied_total; /* bytes consumed by DSP */
+ uint64_t bytes_received; /* from userspace */
+ uint64_t bytes_sent; /* to DSP */
+
+ uint64_t received_total; /* bytes received from DSP */
+ uint64_t bytes_copied; /* to userspace */
+ uint64_t bytes_read; /* from DSP */
+ uint32_t bytes_read_offset; /* bytes read offset */
+
+ uint32_t ts_header_offset; /* holds the timestamp header offset */
+
+ int32_t first_buffer;
+ int32_t last_buffer;
+ int32_t partial_drain_delay;
+
+ uint16_t session_id;
+
+ uint32_t sample_rate;
+ uint32_t num_channels;
+
+ /*
+ * convention - commands coming from the same thread
+ * can use the common cmd_ack var. Others (e.g drain/EOS)
+ * must use separate vars to track command status.
+ */
+ uint32_t cmd_ack;
+ uint32_t cmd_interrupt;
+ uint32_t drain_ready;
+ uint32_t eos_ack;
+
+ uint32_t stream_available;
+ uint32_t next_stream;
+
+ uint32_t run_mode;
+ uint32_t start_delay_lsw;
+ uint32_t start_delay_msw;
+
+ int32_t shm_ion_fd;
+ struct ion_client *lib_ion_client;
+ struct ion_client *shm_ion_client;
+ struct ion_handle *lib_ion_handle;
+ struct ion_handle *shm_ion_handle;
+
+ uint64_t marker_timestamp;
+
+ struct msm_compr_gapless_state gapless_state;
+
+ atomic_t start;
+ atomic_t eos;
+ atomic_t drain;
+ atomic_t xrun;
+ atomic_t close;
+ atomic_t wait_on_close;
+ atomic_t error;
+
+ wait_queue_head_t eos_wait;
+ wait_queue_head_t drain_wait;
+ wait_queue_head_t close_wait;
+ wait_queue_head_t wait_for_stream_avail;
+
+ spinlock_t lock;
+};
+
+const u32 compr_codecs[] = {
+ SND_AUDIOCODEC_AC3, SND_AUDIOCODEC_EAC3, SND_AUDIOCODEC_DTS,
+ SND_AUDIOCODEC_DSD, SND_AUDIOCODEC_TRUEHD, SND_AUDIOCODEC_IEC61937};
+
+struct query_audio_effect {
+ uint32_t mod_id;
+ uint32_t parm_id;
+ uint32_t size;
+ uint32_t offset;
+ uint32_t device;
+};
+
+struct msm_compr_audio_effects {
+ struct bass_boost_params bass_boost;
+ struct pbe_params pbe;
+ struct virtualizer_params virtualizer;
+ struct reverb_params reverb;
+ struct eq_params equalizer;
+ struct soft_volume_params volume;
+ struct query_audio_effect query;
+};
+
+struct msm_compr_dec_params {
+ struct snd_dec_ddp ddp_params;
+};
+
+struct msm_compr_ch_map {
+ bool set_ch_map;
+ char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL];
+};
+
+static int msm_compr_send_dec_params(struct snd_compr_stream *cstream,
+ struct msm_compr_dec_params *dec_params,
+ int stream_id);
+
+static int msm_compr_set_render_mode(struct msm_compr_audio *prtd,
+ uint32_t render_mode) {
+ int ret = -EINVAL;
+ struct audio_client *ac = prtd->audio_client;
+
+ pr_debug("%s, got render mode %u\n", __func__, render_mode);
+
+ if (render_mode == SNDRV_COMPRESS_RENDER_MODE_AUDIO_MASTER) {
+ render_mode = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT;
+ } else if (render_mode == SNDRV_COMPRESS_RENDER_MODE_STC_MASTER) {
+ render_mode = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC;
+ prtd->run_mode = ASM_SESSION_CMD_RUN_STARTIME_RUN_WITH_DELAY;
+ } else {
+ pr_err("%s, Invalid render mode %u\n", __func__,
+ render_mode);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = q6asm_send_mtmx_strtr_render_mode(ac, render_mode);
+ if (ret) {
+ pr_err("%s, Render mode can't be set error %d\n", __func__,
+ ret);
+ }
+exit:
+ return ret;
+}
+
+static int msm_compr_set_clk_rec_mode(struct audio_client *ac,
+ uint32_t clk_rec_mode) {
+ int ret = -EINVAL;
+
+ pr_debug("%s, got clk rec mode %u\n", __func__, clk_rec_mode);
+
+ if (clk_rec_mode == SNDRV_COMPRESS_CLK_REC_MODE_NONE) {
+ clk_rec_mode = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_NONE;
+ } else if (clk_rec_mode == SNDRV_COMPRESS_CLK_REC_MODE_AUTO) {
+ clk_rec_mode = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_AUTO;
+ } else {
+ pr_err("%s, Invalid clk rec_mode mode %u\n", __func__,
+ clk_rec_mode);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = q6asm_send_mtmx_strtr_clk_rec_mode(ac, clk_rec_mode);
+ if (ret) {
+ pr_err("%s, clk rec mode can't be set, error %d\n", __func__,
+ ret);
+ }
+
+exit:
+ return ret;
+}
+
+static int msm_compr_set_render_window(struct audio_client *ac,
+ uint32_t ws_lsw, uint32_t ws_msw,
+ uint32_t we_lsw, uint32_t we_msw)
+{
+ int ret = -EINVAL;
+ struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window;
+ uint32_t param_id;
+
+ pr_debug("%s, ws_lsw 0x%x ws_msw 0x%x we_lsw 0x%x we_ms 0x%x\n",
+ __func__, ws_lsw, ws_msw, we_lsw, we_msw);
+
+ memset(&asm_mtmx_strtr_window, 0,
+ sizeof(struct asm_session_mtmx_strtr_param_window_v2_t));
+ asm_mtmx_strtr_window.window_lsw = ws_lsw;
+ asm_mtmx_strtr_window.window_msw = ws_msw;
+ param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2;
+ ret = q6asm_send_mtmx_strtr_window(ac, &asm_mtmx_strtr_window,
+ param_id);
+ if (ret) {
+ pr_err("%s, start window can't be set error %d\n", __func__,
+ ret);
+ goto exit;
+ }
+
+ asm_mtmx_strtr_window.window_lsw = we_lsw;
+ asm_mtmx_strtr_window.window_msw = we_msw;
+ param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2;
+ ret = q6asm_send_mtmx_strtr_window(ac, &asm_mtmx_strtr_window,
+ param_id);
+ if (ret) {
+ pr_err("%s, end window can't be set error %d\n", __func__,
+ ret);
+ }
+
+exit:
+ return ret;
+}
+
+static int msm_compr_enable_adjust_session_clock(struct audio_client *ac,
+ bool enable)
+{
+ int ret;
+
+ pr_debug("%s, enable adjust_session %d\n", __func__, enable);
+
+ ret = q6asm_send_mtmx_strtr_enable_adjust_session_clock(ac, enable);
+ if (ret)
+ pr_err("%s, adjust session clock can't be set error %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_compr_adjust_session_clock(struct audio_client *ac,
+ uint32_t adjust_session_lsw, uint32_t adjust_session_msw)
+{
+ int ret;
+
+ pr_debug("%s, adjust_session_time_msw 0x%x adjust_session_time_lsw 0x%x\n",
+ __func__, adjust_session_msw, adjust_session_lsw);
+
+ ret = q6asm_adjust_session_clock(ac,
+ adjust_session_lsw,
+ adjust_session_msw);
+ if (ret)
+ pr_err("%s, adjust session clock can't be set error %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_compr_set_volume(struct snd_compr_stream *cstream,
+ uint32_t volume_l, uint32_t volume_r)
+{
+ struct msm_compr_audio *prtd;
+ int rc = 0;
+ uint32_t avg_vol, gain_list[VOLUME_CONTROL_MAX_CHANNELS];
+ uint32_t num_channels;
+ struct snd_soc_pcm_runtime *rtd;
+ struct msm_compr_pdata *pdata;
+ bool use_default = true;
+ u8 *chmap = NULL;
+
+ pr_debug("%s: volume_l %d volume_r %d\n",
+ __func__, volume_l, volume_r);
+ if (!cstream || !cstream->runtime) {
+ pr_err("%s: session not active\n", __func__);
+ return -EPERM;
+ }
+ rtd = cstream->private_data;
+ prtd = cstream->runtime->private_data;
+
+ if (!rtd || !rtd->platform || !prtd || !prtd->audio_client) {
+ pr_err("%s: invalid rtd, prtd or audio client", __func__);
+ return rc;
+ }
+ pdata = snd_soc_platform_get_drvdata(rtd->platform);
+
+ if (prtd->compr_passthr != LEGACY_PCM) {
+ pr_debug("%s: No volume config for passthrough %d\n",
+ __func__, prtd->compr_passthr);
+ return rc;
+ }
+
+ use_default = !(pdata->ch_map[rtd->dai_link->be_id]->set_ch_map);
+ chmap = pdata->ch_map[rtd->dai_link->be_id]->channel_map;
+ num_channels = prtd->num_channels;
+
+ if (prtd->num_channels > 2) {
+ /*
+ * Currently the left and right gains are averaged an applied
+ * to all channels. This might not be desirable. But currently,
+ * there exists no API in userspace to send a list of gains for
+ * each channel either. If such an API does become available,
+ * the mixer control must be updated to accept more than 2
+ * channel gains.
+ *
+ */
+ avg_vol = (volume_l + volume_r) / 2;
+ rc = q6asm_set_volume(prtd->audio_client, avg_vol);
+ } else {
+ gain_list[0] = volume_l;
+ gain_list[1] = volume_r;
+ gain_list[2] = volume_l;
+ num_channels = 3;
+ use_default = true;
+ rc = q6asm_set_multich_gain(prtd->audio_client, num_channels,
+ gain_list, chmap, use_default);
+ }
+
+ if (rc < 0)
+ pr_err("%s: Send vol gain command failed rc=%d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+static int msm_compr_send_ddp_cfg(struct audio_client *ac,
+ struct snd_dec_ddp *ddp,
+ int stream_id)
+{
+ int i, rc;
+ pr_debug("%s\n", __func__);
+ for (i = 0; i < ddp->params_length; i++) {
+ rc = q6asm_ds1_set_stream_endp_params(ac, ddp->params_id[i],
+ ddp->params_value[i],
+ stream_id);
+ if (rc) {
+ pr_err("sending params_id: %d failed\n",
+ ddp->params_id[i]);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static int msm_compr_send_buffer(struct msm_compr_audio *prtd)
+{
+ int buffer_length;
+ uint64_t bytes_available;
+ struct audio_aio_write_param param;
+ struct snd_codec_metadata *buff_addr;
+
+ if (!atomic_read(&prtd->start)) {
+ pr_err("%s: stream is not in started state\n", __func__);
+ return -EINVAL;
+ }
+
+
+ if (atomic_read(&prtd->xrun)) {
+ WARN(1, "%s called while xrun is true", __func__);
+ return -EPERM;
+ }
+
+ pr_debug("%s: bytes_received = %llu copied_total = %llu\n",
+ __func__, prtd->bytes_received, prtd->copied_total);
+ if (prtd->first_buffer && prtd->gapless_state.use_dsp_gapless_mode &&
+ prtd->compr_passthr == LEGACY_PCM)
+ q6asm_stream_send_meta_data(prtd->audio_client,
+ prtd->audio_client->stream_id,
+ prtd->gapless_state.initial_samples_drop,
+ prtd->gapless_state.trailing_samples_drop);
+
+ buffer_length = prtd->codec_param.buffer.fragment_size;
+ bytes_available = prtd->bytes_received - prtd->copied_total;
+ if (bytes_available < prtd->codec_param.buffer.fragment_size)
+ buffer_length = bytes_available;
+
+ if (prtd->byte_offset + buffer_length > prtd->buffer_size) {
+ buffer_length = (prtd->buffer_size - prtd->byte_offset);
+ pr_debug("wrap around situation, send partial data %d now", buffer_length);
+ }
+
+ if (buffer_length) {
+ param.paddr = prtd->buffer_paddr + prtd->byte_offset;
+ WARN(prtd->byte_offset % 32 != 0, "offset %x not multiple of 32",
+ prtd->byte_offset);
+ }
+ else
+ param.paddr = prtd->buffer_paddr;
+ param.len = buffer_length;
+ if (prtd->ts_header_offset) {
+ buff_addr = (struct snd_codec_metadata *)
+ (prtd->buffer + prtd->byte_offset);
+ param.len = buff_addr->length;
+ param.msw_ts = (uint32_t)
+ ((buff_addr->timestamp & 0xFFFFFFFF00000000LL) >> 32);
+ param.lsw_ts = (uint32_t) (buff_addr->timestamp & 0xFFFFFFFFLL);
+ param.paddr += prtd->ts_header_offset;
+ param.flags = SET_TIMESTAMP;
+ param.metadata_len = prtd->ts_header_offset;
+ } else {
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.metadata_len = 0;
+ }
+ param.uid = buffer_length;
+ param.last_buffer = prtd->last_buffer;
+
+ pr_debug("%s: sending %d bytes to DSP byte_offset = %d\n",
+ __func__, param.len, prtd->byte_offset);
+ if (q6asm_async_write(prtd->audio_client, &param) < 0) {
+ pr_err("%s:q6asm_async_write failed\n", __func__);
+ } else {
+ prtd->bytes_sent += buffer_length;
+ if (prtd->first_buffer)
+ prtd->first_buffer = 0;
+ }
+
+ return 0;
+}
+
+static int msm_compr_read_buffer(struct msm_compr_audio *prtd)
+{
+ int buffer_length;
+ uint64_t bytes_available;
+ uint64_t buffer_sent;
+ struct audio_aio_read_param param;
+ int ret;
+
+ if (!atomic_read(&prtd->start)) {
+ pr_err("%s: stream is not in started state\n", __func__);
+ return -EINVAL;
+ }
+
+ buffer_length = prtd->codec_param.buffer.fragment_size -
+ prtd->ts_header_offset;
+ bytes_available = prtd->received_total - prtd->bytes_copied;
+ buffer_sent = prtd->bytes_read - prtd->bytes_copied;
+ if (buffer_sent + buffer_length + prtd->ts_header_offset
+ > prtd->buffer_size) {
+ pr_debug(" %s : Buffer is Full bytes_available: %llu\n",
+ __func__, bytes_available);
+ return 0;
+ }
+
+ memset(&param, 0x0, sizeof(struct audio_aio_read_param));
+ param.paddr = prtd->buffer_paddr + prtd->bytes_read_offset +
+ prtd->ts_header_offset;
+ param.len = buffer_length;
+ param.uid = buffer_length;
+ param.flags = prtd->codec_param.codec.flags;
+
+ pr_debug("%s: reading %d bytes from DSP byte_offset = %llu\n",
+ __func__, buffer_length, prtd->bytes_read);
+ ret = q6asm_async_read(prtd->audio_client, &param);
+ if (ret < 0) {
+ pr_err("%s: q6asm_async_read failed - %d\n",
+ __func__, ret);
+ return ret;
+ }
+ prtd->bytes_read += buffer_length;
+ prtd->bytes_read_offset += buffer_length;
+ if (prtd->bytes_read_offset >= prtd->buffer_size)
+ prtd->bytes_read_offset -= prtd->buffer_size;
+
+ return 0;
+}
+
+static void compr_event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_compr_audio *prtd = priv;
+ struct snd_compr_stream *cstream;
+ struct audio_client *ac;
+ uint32_t chan_mode = 0;
+ uint32_t sample_rate = 0;
+ uint64_t bytes_available;
+ int stream_id;
+ uint32_t stream_index;
+ unsigned long flags;
+ uint64_t read_size;
+ uint32_t *buff_addr;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret = 0;
+
+ if (!prtd) {
+ pr_err("%s: prtd is NULL\n", __func__);
+ return;
+ }
+ cstream = prtd->cstream;
+ if (!cstream) {
+ pr_err("%s: cstream is NULL\n", __func__);
+ return;
+ }
+
+ ac = prtd->audio_client;
+
+ /*
+ * Token for rest of the compressed commands use to set
+ * session id, stream id, dir etc.
+ */
+ stream_id = q6asm_get_stream_id_from_token(token);
+
+ pr_debug("%s opcode =%08x\n", __func__, opcode);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2:
+ spin_lock_irqsave(&prtd->lock, flags);
+
+ if (payload[3]) {
+ pr_err("%s WRITE FAILED w/ err 0x%x !, paddr 0x%x, byte_offset=%d,copied_total=%llu,token=%d\n",
+ __func__,
+ payload[3],
+ payload[0],
+ prtd->byte_offset,
+ prtd->copied_total, token);
+
+ if (atomic_read(&prtd->drain) && prtd->last_buffer) {
+ pr_debug("wake up on drain\n");
+ prtd->drain_ready = 1;
+ wake_up(&prtd->drain_wait);
+ atomic_set(&prtd->drain, 0);
+ prtd->last_buffer = 0;
+ } else {
+ atomic_set(&prtd->start, 0);
+ }
+ } else {
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2 offset %d, length %d\n",
+ prtd->byte_offset, token);
+ }
+
+ /*
+ * Token for WRITE command represents the amount of data
+ * written to ADSP in the last write, update offset and
+ * total copied data accordingly.
+ */
+ if (prtd->ts_header_offset) {
+ /* Always assume that the data will be sent to DSP on
+ * frame boundary.
+ * i.e, one frame of userspace write will result in
+ * one kernel write to DSP. This is needed as
+ * timestamp will be sent per frame.
+ */
+ prtd->byte_offset +=
+ prtd->codec_param.buffer.fragment_size;
+ prtd->copied_total +=
+ prtd->codec_param.buffer.fragment_size;
+ } else {
+ prtd->byte_offset += token;
+ prtd->copied_total += token;
+ }
+ if (prtd->byte_offset >= prtd->buffer_size)
+ prtd->byte_offset -= prtd->buffer_size;
+
+ snd_compr_fragment_elapsed(cstream);
+
+ if (!atomic_read(&prtd->start)) {
+ /* Writes must be restarted from _copy() */
+ pr_debug("write_done received while not started, treat as xrun");
+ atomic_set(&prtd->xrun, 1);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+
+ bytes_available = prtd->bytes_received - prtd->copied_total;
+ if (bytes_available < cstream->runtime->fragment_size) {
+ pr_debug("WRITE_DONE Insufficient data to send. break out\n");
+ atomic_set(&prtd->xrun, 1);
+
+ if (prtd->last_buffer)
+ prtd->last_buffer = 0;
+ if (atomic_read(&prtd->drain)) {
+ pr_debug("wake up on drain\n");
+ prtd->drain_ready = 1;
+ wake_up(&prtd->drain_wait);
+ atomic_set(&prtd->drain, 0);
+ }
+ } else if ((bytes_available == cstream->runtime->fragment_size)
+ && atomic_read(&prtd->drain)) {
+ prtd->last_buffer = 1;
+ msm_compr_send_buffer(prtd);
+ prtd->last_buffer = 0;
+ } else
+ msm_compr_send_buffer(prtd);
+
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+
+ case ASM_DATA_EVENT_READ_DONE_V2:
+ spin_lock_irqsave(&prtd->lock, flags);
+
+ pr_debug("ASM_DATA_EVENT_READ_DONE_V2 offset %d, length %d\n",
+ prtd->byte_offset, payload[4]);
+
+ if (prtd->ts_header_offset) {
+ /* Update the header for received buffer */
+ buff_addr = prtd->buffer + prtd->byte_offset;
+ /* Write the length of the buffer */
+ *buff_addr = prtd->codec_param.buffer.fragment_size
+ - prtd->ts_header_offset;
+ buff_addr++;
+ /* Write the offset */
+ *buff_addr = prtd->ts_header_offset;
+ buff_addr++;
+ /* Write the TS LSW */
+ *buff_addr = payload[CAPTURE_META_DATA_TS_OFFSET_LSW];
+ buff_addr++;
+ /* Write the TS MSW */
+ *buff_addr = payload[CAPTURE_META_DATA_TS_OFFSET_MSW];
+ }
+ /* Always assume read_size is same as fragment_size */
+ read_size = prtd->codec_param.buffer.fragment_size;
+ prtd->byte_offset += read_size;
+ prtd->received_total += read_size;
+ if (prtd->byte_offset >= prtd->buffer_size)
+ prtd->byte_offset -= prtd->buffer_size;
+
+ snd_compr_fragment_elapsed(cstream);
+
+ if (!atomic_read(&prtd->start)) {
+ pr_debug("read_done received while not started, treat as xrun");
+ atomic_set(&prtd->xrun, 1);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+ msm_compr_read_buffer(prtd);
+
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ spin_lock_irqsave(&prtd->lock, flags);
+ pr_debug("%s: ASM_DATA_CMDRSP_EOS token 0x%x,stream id %d\n",
+ __func__, token, stream_id);
+ if (atomic_read(&prtd->eos) &&
+ !prtd->gapless_state.set_next_stream_id) {
+ pr_debug("ASM_DATA_CMDRSP_EOS wake up\n");
+ prtd->eos_ack = 1;
+ wake_up(&prtd->eos_wait);
+ }
+ atomic_set(&prtd->eos, 0);
+ stream_index = STREAM_ARRAY_INDEX(stream_id);
+ if (stream_index >= MAX_NUMBER_OF_STREAMS ||
+ stream_index < 0) {
+ pr_err("%s: Invalid stream index %d", __func__,
+ stream_index);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+
+ if (prtd->gapless_state.set_next_stream_id &&
+ prtd->gapless_state.stream_opened[stream_index]) {
+ pr_debug("%s: CMD_CLOSE stream_id %d\n",
+ __func__, stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_CLOSE, stream_id);
+ atomic_set(&prtd->close, 1);
+ prtd->gapless_state.stream_opened[stream_index] = 0;
+ prtd->gapless_state.set_next_stream_id = false;
+ }
+ if (prtd->gapless_state.gapless_transition)
+ prtd->gapless_state.gapless_transition = 0;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ case ASM_STREAM_PP_EVENT:
+ case ASM_STREAM_CMD_ENCDEC_EVENTS:
+ pr_debug("%s: ASM_STREAM_EVENT(0x%x)\n", __func__, opcode);
+ rtd = cstream->private_data;
+ if (!rtd) {
+ pr_err("%s: rtd is NULL\n", __func__);
+ return;
+ }
+
+ ret = msm_adsp_inform_mixer_ctl(rtd, payload);
+ if (ret) {
+ pr_err("%s: failed to inform mixer ctrl. err = %d\n",
+ __func__, ret);
+ return;
+ }
+ break;
+ case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+ case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: {
+ pr_debug("ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY\n");
+ chan_mode = payload[1] >> 16;
+ sample_rate = payload[2] >> 16;
+ if (prtd && (chan_mode != prtd->num_channels ||
+ sample_rate != prtd->sample_rate)) {
+ prtd->num_channels = chan_mode;
+ prtd->sample_rate = sample_rate;
+ }
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2:
+ /* check if the first buffer need to be sent to DSP */
+ pr_debug("ASM_SESSION_CMD_RUN_V2\n");
+
+ /* FIXME: A state is a better way, dealing with this*/
+ spin_lock_irqsave(&prtd->lock, flags);
+
+ if (cstream->direction == SND_COMPRESS_CAPTURE) {
+ atomic_set(&prtd->start, 1);
+ msm_compr_read_buffer(prtd);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+
+ if (!prtd->bytes_sent) {
+ bytes_available = prtd->bytes_received - prtd->copied_total;
+ if (bytes_available < cstream->runtime->fragment_size) {
+ pr_debug("CMD_RUN_V2 Insufficient data to send. break out\n");
+ atomic_set(&prtd->xrun, 1);
+ } else
+ msm_compr_send_buffer(prtd);
+ }
+
+ /*
+ * The condition below ensures playback finishes in the
+ * follow cornercase
+ * WRITE(last buffer)
+ * WAIT_FOR_DRAIN
+ * PAUSE
+ * WRITE_DONE(X)
+ * RESUME
+ */
+ if ((prtd->copied_total == prtd->bytes_sent) &&
+ atomic_read(&prtd->drain)) {
+ pr_debug("RUN ack, wake up & continue pending drain\n");
+
+ if (prtd->last_buffer)
+ prtd->last_buffer = 0;
+
+ prtd->drain_ready = 1;
+ wake_up(&prtd->drain_wait);
+ atomic_set(&prtd->drain, 0);
+ }
+
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ case ASM_STREAM_CMD_FLUSH:
+ pr_debug("%s: ASM_STREAM_CMD_FLUSH:", __func__);
+ pr_debug("token 0x%x, stream id %d\n", token,
+ stream_id);
+ prtd->cmd_ack = 1;
+ break;
+ case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:
+ pr_debug("%s: ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:",
+ __func__);
+ pr_debug("token 0x%x, stream id = %d\n", token,
+ stream_id);
+ break;
+ case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:
+ pr_debug("%s: ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:",
+ __func__);
+ pr_debug("token = 0x%x, stream id = %d\n", token,
+ stream_id);
+ break;
+ case ASM_STREAM_CMD_CLOSE:
+ pr_debug("%s: ASM_DATA_CMD_CLOSE:", __func__);
+ pr_debug("token 0x%x, stream id %d\n", token,
+ stream_id);
+ /*
+ * wakeup wait for stream avail on stream 3
+ * after stream 1 ends.
+ */
+ if (prtd->next_stream) {
+ pr_debug("%s:CLOSE:wakeup wait for stream\n",
+ __func__);
+ prtd->stream_available = 1;
+ wake_up(&prtd->wait_for_stream_avail);
+ prtd->next_stream = 0;
+ }
+ if (atomic_read(&prtd->close) &&
+ atomic_read(&prtd->wait_on_close)) {
+ prtd->cmd_ack = 1;
+ wake_up(&prtd->close_wait);
+ }
+ atomic_set(&prtd->close, 0);
+ break;
+ case ASM_STREAM_CMD_REGISTER_PP_EVENTS:
+ pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS:",
+ __func__);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3:
+ pr_debug("%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3\n",
+ __func__);
+ break;
+ case RESET_EVENTS:
+ pr_err("%s: Received reset events CB, move to error state",
+ __func__);
+ spin_lock_irqsave(&prtd->lock, flags);
+ /*
+ * Since ADSP is down, let this driver pretend that it copied
+ * all the bytes received, so that next write will be triggered
+ */
+ prtd->copied_total = prtd->bytes_received;
+ snd_compr_fragment_elapsed(cstream);
+ atomic_set(&prtd->error, 1);
+ wake_up(&prtd->drain_wait);
+ if (atomic_cmpxchg(&prtd->eos, 1, 0)) {
+ pr_debug("%s:unblock eos wait queues", __func__);
+ wake_up(&prtd->eos_wait);
+ }
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ default:
+ pr_debug("%s: Not Supported Event opcode[0x%x]\n",
+ __func__, opcode);
+ break;
+ }
+}
+
+static int msm_compr_get_partial_drain_delay(int frame_sz, int sample_rate)
+{
+ int delay_time_ms = 0;
+
+ delay_time_ms = ((DSP_NUM_OUTPUT_FRAME_BUFFERED * frame_sz * 1000) /
+ sample_rate) + DSP_PP_BUFFERING_IN_MSEC;
+ delay_time_ms = delay_time_ms > PARTIAL_DRAIN_ACK_EARLY_BY_MSEC ?
+ delay_time_ms - PARTIAL_DRAIN_ACK_EARLY_BY_MSEC : 0;
+
+ pr_debug("%s: frame_sz %d, sample_rate %d, partial drain delay %d\n",
+ __func__, frame_sz, sample_rate, delay_time_ms);
+ return delay_time_ms;
+}
+
+static void populate_codec_list(struct msm_compr_audio *prtd)
+{
+ pr_debug("%s\n", __func__);
+ prtd->compr_cap.direction = SND_COMPRESS_PLAYBACK;
+ prtd->compr_cap.min_fragment_size =
+ COMPR_PLAYBACK_MIN_FRAGMENT_SIZE;
+ prtd->compr_cap.max_fragment_size =
+ COMPR_PLAYBACK_MAX_FRAGMENT_SIZE;
+ prtd->compr_cap.min_fragments =
+ COMPR_PLAYBACK_MIN_NUM_FRAGMENTS;
+ prtd->compr_cap.max_fragments =
+ COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
+ prtd->compr_cap.num_codecs = 17;
+ prtd->compr_cap.codecs[0] = SND_AUDIOCODEC_MP3;
+ prtd->compr_cap.codecs[1] = SND_AUDIOCODEC_AAC;
+ prtd->compr_cap.codecs[2] = SND_AUDIOCODEC_AC3;
+ prtd->compr_cap.codecs[3] = SND_AUDIOCODEC_EAC3;
+ prtd->compr_cap.codecs[4] = SND_AUDIOCODEC_MP2;
+ prtd->compr_cap.codecs[5] = SND_AUDIOCODEC_PCM;
+ prtd->compr_cap.codecs[6] = SND_AUDIOCODEC_WMA;
+ prtd->compr_cap.codecs[7] = SND_AUDIOCODEC_WMA_PRO;
+ prtd->compr_cap.codecs[8] = SND_AUDIOCODEC_FLAC;
+ prtd->compr_cap.codecs[9] = SND_AUDIOCODEC_VORBIS;
+ prtd->compr_cap.codecs[10] = SND_AUDIOCODEC_ALAC;
+ prtd->compr_cap.codecs[11] = SND_AUDIOCODEC_APE;
+ prtd->compr_cap.codecs[12] = SND_AUDIOCODEC_DTS;
+ prtd->compr_cap.codecs[13] = SND_AUDIOCODEC_DSD;
+ prtd->compr_cap.codecs[14] = SND_AUDIOCODEC_APTX;
+ prtd->compr_cap.codecs[15] = SND_AUDIOCODEC_TRUEHD;
+ prtd->compr_cap.codecs[16] = SND_AUDIOCODEC_IEC61937;
+ prtd->compr_cap.codecs[17] = SND_AUDIOCODEC_APTXHD;
+}
+
+static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream,
+ int stream_id,
+ bool use_gapless_codec_options)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_compr_audio *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct msm_compr_pdata *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct asm_aac_cfg aac_cfg;
+ struct asm_wma_cfg wma_cfg;
+ struct asm_wmapro_cfg wma_pro_cfg;
+ struct asm_flac_cfg flac_cfg;
+ struct asm_vorbis_cfg vorbis_cfg;
+ struct asm_alac_cfg alac_cfg;
+ struct asm_ape_cfg ape_cfg;
+ struct asm_dsd_cfg dsd_cfg;
+ struct aptx_dec_bt_addr_cfg aptx_cfg;
+ union snd_codec_options *codec_options;
+
+ int ret = 0;
+ uint16_t bit_width;
+ bool use_default_chmap = true;
+ char *chmap = NULL;
+ uint16_t sample_word_size;
+
+ pr_debug("%s: use_gapless_codec_options %d\n",
+ __func__, use_gapless_codec_options);
+
+ if (use_gapless_codec_options)
+ codec_options = &(prtd->gapless_state.codec_options);
+ else
+ codec_options = &(prtd->codec_param.codec.options);
+
+ if (!codec_options) {
+ pr_err("%s: codec_options is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (prtd->codec) {
+ case FORMAT_LINEAR_PCM:
+ pr_debug("SND_AUDIOCODEC_PCM\n");
+ if (pdata->ch_map[rtd->dai_link->be_id]) {
+ use_default_chmap =
+ !(pdata->ch_map[rtd->dai_link->be_id]->set_ch_map);
+ chmap =
+ pdata->ch_map[rtd->dai_link->be_id]->channel_map;
+ }
+
+ switch (prtd->codec_param.codec.format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bit_width = 32;
+ sample_word_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bit_width = 24;
+ sample_word_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bit_width = 24;
+ sample_word_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bit_width = 16;
+ sample_word_size = 16;
+ break;
+ }
+ if (pdata->avs_ver &&
+ (q6core_get_avs_version() == Q6_SUBSYS_AVS2_7))
+ ret = q6asm_media_format_block_pcm_format_support_v3(
+ prtd->audio_client,
+ prtd->sample_rate,
+ prtd->num_channels,
+ bit_width, stream_id,
+ use_default_chmap,
+ chmap,
+ sample_word_size);
+ else
+ ret = q6asm_media_format_block_pcm_format_support_v4(
+ prtd->audio_client,
+ prtd->sample_rate,
+ prtd->num_channels,
+ bit_width, stream_id,
+ use_default_chmap,
+ chmap,
+ sample_word_size,
+ ASM_LITTLE_ENDIAN,
+ DEFAULT_QF);
+ if (ret < 0)
+ pr_err("%s: CMD Format block failed\n", __func__);
+
+ break;
+ case FORMAT_MP3:
+ pr_debug("SND_AUDIOCODEC_MP3\n");
+ /* no media format block needed */
+ break;
+ case FORMAT_MPEG4_AAC:
+ pr_debug("SND_AUDIOCODEC_AAC\n");
+ memset(&aac_cfg, 0x0, sizeof(struct asm_aac_cfg));
+ aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
+ if (prtd->codec_param.codec.format ==
+ SND_AUDIOSTREAMFORMAT_MP4ADTS)
+ aac_cfg.format = 0x0;
+ else if (prtd->codec_param.codec.format ==
+ SND_AUDIOSTREAMFORMAT_MP4LATM)
+ aac_cfg.format = 0x04;
+ else
+ aac_cfg.format = 0x03;
+ aac_cfg.ch_cfg = prtd->num_channels;
+ aac_cfg.sample_rate = prtd->sample_rate;
+ ret = q6asm_stream_media_format_block_aac(prtd->audio_client,
+ &aac_cfg, stream_id);
+ if (ret < 0)
+ pr_err("%s: CMD Format block failed\n", __func__);
+ break;
+ case FORMAT_AC3:
+ pr_debug("SND_AUDIOCODEC_AC3\n");
+ break;
+ case FORMAT_EAC3:
+ pr_debug("SND_AUDIOCODEC_EAC3\n");
+ break;
+ case FORMAT_WMA_V9:
+ pr_debug("SND_AUDIOCODEC_WMA\n");
+ memset(&wma_cfg, 0x0, sizeof(struct asm_wma_cfg));
+ wma_cfg.format_tag = prtd->codec_param.codec.format;
+ wma_cfg.ch_cfg = prtd->codec_param.codec.ch_in;
+ wma_cfg.sample_rate = prtd->sample_rate;
+ wma_cfg.avg_bytes_per_sec = codec_options->wma.avg_bit_rate/8;
+ wma_cfg.block_align = codec_options->wma.super_block_align;
+ wma_cfg.valid_bits_per_sample =
+ codec_options->wma.bits_per_sample;
+ wma_cfg.ch_mask = codec_options->wma.channelmask;
+ wma_cfg.encode_opt = codec_options->wma.encodeopt;
+ ret = q6asm_media_format_block_wma(prtd->audio_client,
+ &wma_cfg, stream_id);
+ if (ret < 0)
+ pr_err("%s: CMD Format block failed\n", __func__);
+ break;
+ case FORMAT_WMA_V10PRO:
+ pr_debug("SND_AUDIOCODEC_WMA_PRO\n");
+ memset(&wma_pro_cfg, 0x0, sizeof(struct asm_wmapro_cfg));
+ wma_pro_cfg.format_tag = prtd->codec_param.codec.format;
+ wma_pro_cfg.ch_cfg = prtd->codec_param.codec.ch_in;
+ wma_pro_cfg.sample_rate = prtd->sample_rate;
+ wma_cfg.avg_bytes_per_sec = codec_options->wma.avg_bit_rate/8;
+ wma_pro_cfg.block_align = codec_options->wma.super_block_align;
+ wma_pro_cfg.valid_bits_per_sample =
+ codec_options->wma.bits_per_sample;
+ wma_pro_cfg.ch_mask = codec_options->wma.channelmask;
+ wma_pro_cfg.encode_opt = codec_options->wma.encodeopt;
+ wma_pro_cfg.adv_encode_opt = codec_options->wma.encodeopt1;
+ wma_pro_cfg.adv_encode_opt2 = codec_options->wma.encodeopt2;
+ ret = q6asm_media_format_block_wmapro(prtd->audio_client,
+ &wma_pro_cfg, stream_id);
+ if (ret < 0)
+ pr_err("%s: CMD Format block failed\n", __func__);
+ break;
+ case FORMAT_MP2:
+ pr_debug("%s: SND_AUDIOCODEC_MP2\n", __func__);
+ break;
+ case FORMAT_FLAC:
+ pr_debug("%s: SND_AUDIOCODEC_FLAC\n", __func__);
+ memset(&flac_cfg, 0x0, sizeof(struct asm_flac_cfg));
+ flac_cfg.ch_cfg = prtd->num_channels;
+ flac_cfg.sample_rate = prtd->sample_rate;
+ flac_cfg.stream_info_present = 1;
+ flac_cfg.sample_size = codec_options->flac_dec.sample_size;
+ flac_cfg.min_blk_size = codec_options->flac_dec.min_blk_size;
+ flac_cfg.max_blk_size = codec_options->flac_dec.max_blk_size;
+ flac_cfg.max_frame_size =
+ codec_options->flac_dec.max_frame_size;
+ flac_cfg.min_frame_size =
+ codec_options->flac_dec.min_frame_size;
+
+ ret = q6asm_stream_media_format_block_flac(prtd->audio_client,
+ &flac_cfg, stream_id);
+ if (ret < 0)
+ pr_err("%s: CMD Format block failed ret %d\n",
+ __func__, ret);
+
+ break;
+ case FORMAT_VORBIS:
+ pr_debug("%s: SND_AUDIOCODEC_VORBIS\n", __func__);
+ memset(&vorbis_cfg, 0x0, sizeof(struct asm_vorbis_cfg));
+ vorbis_cfg.bit_stream_fmt =
+ codec_options->vorbis_dec.bit_stream_fmt;
+
+ ret = q6asm_stream_media_format_block_vorbis(
+ prtd->audio_client, &vorbis_cfg,
+ stream_id);
+ if (ret < 0)
+ pr_err("%s: CMD Format block failed ret %d\n",
+ __func__, ret);
+
+ break;
+ case FORMAT_ALAC:
+ pr_debug("%s: SND_AUDIOCODEC_ALAC\n", __func__);
+ memset(&alac_cfg, 0x0, sizeof(struct asm_alac_cfg));
+ alac_cfg.num_channels = prtd->num_channels;
+ alac_cfg.sample_rate = prtd->sample_rate;
+ alac_cfg.frame_length = codec_options->alac.frame_length;
+ alac_cfg.compatible_version =
+ codec_options->alac.compatible_version;
+ alac_cfg.bit_depth = codec_options->alac.bit_depth;
+ alac_cfg.pb = codec_options->alac.pb;
+ alac_cfg.mb = codec_options->alac.mb;
+ alac_cfg.kb = codec_options->alac.kb;
+ alac_cfg.max_run = codec_options->alac.max_run;
+ alac_cfg.max_frame_bytes = codec_options->alac.max_frame_bytes;
+ alac_cfg.avg_bit_rate = codec_options->alac.avg_bit_rate;
+ alac_cfg.channel_layout_tag =
+ codec_options->alac.channel_layout_tag;
+
+ ret = q6asm_media_format_block_alac(prtd->audio_client,
+ &alac_cfg, stream_id);
+ if (ret < 0)
+ pr_err("%s: CMD Format block failed ret %d\n",
+ __func__, ret);
+ break;
+ case FORMAT_APE:
+ pr_debug("%s: SND_AUDIOCODEC_APE\n", __func__);
+ memset(&ape_cfg, 0x0, sizeof(struct asm_ape_cfg));
+ ape_cfg.num_channels = prtd->num_channels;
+ ape_cfg.sample_rate = prtd->sample_rate;
+ ape_cfg.compatible_version =
+ codec_options->ape.compatible_version;
+ ape_cfg.compression_level =
+ codec_options->ape.compression_level;
+ ape_cfg.format_flags = codec_options->ape.format_flags;
+ ape_cfg.blocks_per_frame = codec_options->ape.blocks_per_frame;
+ ape_cfg.final_frame_blocks =
+ codec_options->ape.final_frame_blocks;
+ ape_cfg.total_frames = codec_options->ape.total_frames;
+ ape_cfg.bits_per_sample = codec_options->ape.bits_per_sample;
+ ape_cfg.seek_table_present =
+ codec_options->ape.seek_table_present;
+
+ ret = q6asm_media_format_block_ape(prtd->audio_client,
+ &ape_cfg, stream_id);
+
+ if (ret < 0)
+ pr_err("%s: CMD Format block failed ret %d\n",
+ __func__, ret);
+ break;
+ case FORMAT_DTS:
+ pr_debug("SND_AUDIOCODEC_DTS\n");
+ /* no media format block needed */
+ break;
+ case FORMAT_DSD:
+ pr_debug("%s: SND_AUDIOCODEC_DSD\n", __func__);
+ memset(&dsd_cfg, 0x0, sizeof(struct asm_dsd_cfg));
+ dsd_cfg.num_channels = prtd->num_channels;
+ dsd_cfg.dsd_data_rate = prtd->sample_rate;
+ dsd_cfg.num_version = 0;
+ dsd_cfg.is_bitwise_big_endian = 1;
+ dsd_cfg.dsd_channel_block_size = 1;
+ ret = q6asm_media_format_block_dsd(prtd->audio_client,
+ &dsd_cfg, stream_id);
+ if (ret < 0)
+ pr_err("%s: CMD DSD Format block failed ret %d\n",
+ __func__, ret);
+ break;
+ case FORMAT_TRUEHD:
+ pr_debug("SND_AUDIOCODEC_TRUEHD\n");
+ /* no media format block needed */
+ break;
+ case FORMAT_IEC61937:
+ pr_debug("SND_AUDIOCODEC_IEC61937\n");
+ ret = q6asm_media_format_block_iec(prtd->audio_client,
+ prtd->sample_rate,
+ prtd->num_channels);
+ if (ret < 0)
+ pr_err("%s: CMD IEC61937 Format block failed ret %d\n",
+ __func__, ret);
+ break;
+ case FORMAT_APTXHD:
+ pr_debug("SND_AUDIOCODEC_APTXHD\n");
+ case FORMAT_APTX:
+ pr_debug("SND_AUDIOCODEC_APTX\n");
+ memset(&aptx_cfg, 0x0, sizeof(struct aptx_dec_bt_addr_cfg));
+ ret = q6asm_stream_media_format_block_aptx_dec(
+ prtd->audio_client,
+ prtd->sample_rate,
+ stream_id);
+ if (ret >= 0) {
+ aptx_cfg.nap = codec_options->aptx_dec.nap;
+ aptx_cfg.uap = codec_options->aptx_dec.uap;
+ aptx_cfg.lap = codec_options->aptx_dec.lap;
+ q6asm_set_aptx_dec_bt_addr(prtd->audio_client,
+ &aptx_cfg);
+ } else {
+ pr_err("%s: CMD Format block failed ret %d\n",
+ __func__, ret);
+ }
+ break;
+ default:
+ pr_debug("%s, unsupported format, skip", __func__);
+ break;
+ }
+ return ret;
+}
+
+static int msm_compr_init_pp_params(struct snd_compr_stream *cstream,
+ struct audio_client *ac)
+{
+ int ret = 0;
+ struct asm_softvolume_params softvol = {
+ .period = SOFT_VOLUME_PERIOD,
+ .step = SOFT_VOLUME_STEP,
+ .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+ };
+
+ switch (ac->topology) {
+ default:
+ ret = q6asm_set_softvolume_v2(ac, &softvol,
+ SOFT_VOLUME_INSTANCE_1);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+ __func__, ret);
+
+ break;
+ }
+ return ret;
+}
+
+static int msm_compr_configure_dsp_for_playback
+ (struct snd_compr_stream *cstream)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_compr_audio *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data;
+ uint16_t bits_per_sample = 16;
+ int dir = IN, ret = 0;
+ struct audio_client *ac = prtd->audio_client;
+ struct msm_compr_pdata *pdata =
+ snd_soc_platform_get_drvdata(soc_prtd->platform);
+ uint32_t stream_index;
+ struct asm_softpause_params softpause = {
+ .enable = SOFT_PAUSE_ENABLE,
+ .period = SOFT_PAUSE_PERIOD,
+ .step = SOFT_PAUSE_STEP,
+ .rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
+ };
+ struct asm_softvolume_params softvol = {
+ .period = SOFT_VOLUME_PERIOD,
+ .step = SOFT_VOLUME_STEP,
+ .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+ };
+
+ pr_debug("%s: stream_id %d\n", __func__, ac->stream_id);
+ stream_index = STREAM_ARRAY_INDEX(ac->stream_id);
+ if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) {
+ pr_err("%s: Invalid stream index:%d", __func__, stream_index);
+ return -EINVAL;
+ }
+
+ if ((prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) ||
+ (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_3LE))
+ bits_per_sample = 24;
+ else if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S32_LE)
+ bits_per_sample = 32;
+
+ if (prtd->compr_passthr != LEGACY_PCM) {
+ ret = q6asm_open_write_compressed(ac, prtd->codec,
+ prtd->compr_passthr);
+ if (ret < 0) {
+ pr_err("%s:ASM open write err[%d] for compr_type[%d]\n",
+ __func__, ret, prtd->compr_passthr);
+ return ret;
+ }
+ prtd->gapless_state.stream_opened[stream_index] = 1;
+
+ ret = msm_pcm_routing_reg_phy_compr_stream(
+ soc_prtd->dai_link->be_id,
+ ac->perf_mode,
+ prtd->session_id,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ prtd->compr_passthr);
+ if (ret) {
+ pr_err("%s: compr stream reg failed:%d\n", __func__,
+ ret);
+ return ret;
+ }
+ } else {
+ pr_debug("%s: stream_id %d bits_per_sample %d\n",
+ __func__, ac->stream_id, bits_per_sample);
+ if (pdata->avs_ver &&
+ (q6core_get_avs_version() == Q6_SUBSYS_AVS2_7))
+ ret = q6asm_stream_open_write_v3(ac,
+ prtd->codec, bits_per_sample,
+ ac->stream_id,
+ prtd->gapless_state.use_dsp_gapless_mode);
+ else
+ ret = q6asm_stream_open_write_v4(ac,
+ prtd->codec, bits_per_sample,
+ ac->stream_id,
+ prtd->gapless_state.use_dsp_gapless_mode);
+ if (ret < 0) {
+ pr_err("%s:ASM open write err[%d] for compr type[%d]\n",
+ __func__, ret, prtd->compr_passthr);
+ return -ENOMEM;
+ }
+ prtd->gapless_state.stream_opened[stream_index] = 1;
+
+ pr_debug("%s: be_id %d\n", __func__, soc_prtd->dai_link->be_id);
+ ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ ac->perf_mode,
+ prtd->session_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret) {
+ pr_err("%s: stream reg failed:%d\n", __func__, ret);
+ return ret;
+ }
+ }
+
+ ret = msm_compr_set_volume(cstream, 0, 0);
+ if (ret < 0)
+ pr_err("%s : Set Volume failed : %d", __func__, ret);
+
+ if (prtd->compr_passthr != LEGACY_PCM) {
+ pr_debug("%s : Don't send cal and PP params for compress path",
+ __func__);
+ } else {
+ ret = q6asm_send_cal(ac);
+ if (ret < 0)
+ pr_debug("%s : Send cal failed : %d", __func__, ret);
+
+ ret = q6asm_set_softpause(ac, &softpause);
+ if (ret < 0)
+ pr_err("%s: Send SoftPause Param failed ret=%d\n",
+ __func__, ret);
+
+ ret = q6asm_set_softvolume(ac, &softvol);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+ __func__, ret);
+ }
+ ret = q6asm_set_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE));
+ if (ret < 0) {
+ pr_err("%s: Set IO mode failed\n", __func__);
+ return -EINVAL;
+ }
+
+ runtime->fragments = prtd->codec_param.buffer.fragments;
+ runtime->fragment_size = prtd->codec_param.buffer.fragment_size;
+ pr_debug("allocate %d buffers each of size %d\n",
+ runtime->fragments,
+ runtime->fragment_size);
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir, ac,
+ runtime->fragment_size,
+ runtime->fragments);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed rc = %d\n", ret);
+ return -ENOMEM;
+ }
+
+ prtd->byte_offset = 0;
+ prtd->copied_total = 0;
+ prtd->app_pointer = 0;
+ prtd->bytes_received = 0;
+ prtd->bytes_sent = 0;
+ prtd->buffer = ac->port[dir].buf[0].data;
+ prtd->buffer_paddr = ac->port[dir].buf[0].phys;
+ prtd->buffer_size = runtime->fragments * runtime->fragment_size;
+
+ /* Bit-0 of flags represent timestamp mode */
+ if (prtd->codec_param.codec.flags & COMPRESSED_TIMESTAMP_FLAG)
+ prtd->ts_header_offset = sizeof(struct snd_codec_metadata);
+ else
+ prtd->ts_header_offset = 0;
+
+ ret = msm_compr_send_media_format_block(cstream, ac->stream_id, false);
+ if (ret < 0) {
+ pr_err("%s, failed to send media format block\n", __func__);
+ }
+
+ return ret;
+}
+
+static int msm_compr_configure_dsp_for_capture(struct snd_compr_stream *cstream)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_compr_audio *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data;
+ uint16_t bits_per_sample;
+ uint16_t sample_word_size;
+ int dir = OUT, ret = 0;
+ struct audio_client *ac = prtd->audio_client;
+ uint32_t stream_index;
+
+ switch (prtd->codec_param.codec.format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits_per_sample = 24;
+ sample_word_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bits_per_sample = 24;
+ sample_word_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bits_per_sample = 32;
+ sample_word_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bits_per_sample = 16;
+ sample_word_size = 16;
+ break;
+ }
+
+ pr_debug("%s: stream_id %d bits_per_sample %d\n",
+ __func__, ac->stream_id, bits_per_sample);
+
+ if (prtd->codec_param.codec.flags & COMPRESSED_TIMESTAMP_FLAG) {
+ ret = q6asm_open_read_v4(prtd->audio_client, FORMAT_LINEAR_PCM,
+ bits_per_sample, true);
+ } else {
+ ret = q6asm_open_read_v4(prtd->audio_client, FORMAT_LINEAR_PCM,
+ bits_per_sample, false);
+ }
+ if (ret < 0) {
+ pr_err("%s: q6asm_open_read failed:%d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ ac->perf_mode,
+ prtd->session_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret) {
+ pr_err("%s: stream reg failed:%d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = q6asm_set_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE));
+ if (ret < 0) {
+ pr_err("%s: Set IO mode failed\n", __func__);
+ return -EINVAL;
+ }
+
+ stream_index = STREAM_ARRAY_INDEX(ac->stream_id);
+ if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) {
+ pr_err("%s: Invalid stream index:%d", __func__, stream_index);
+ return -EINVAL;
+ }
+
+ runtime->fragments = prtd->codec_param.buffer.fragments;
+ runtime->fragment_size = prtd->codec_param.buffer.fragment_size;
+ pr_debug("%s: allocate %d buffers each of size %d\n",
+ __func__, runtime->fragments,
+ runtime->fragment_size);
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir, ac,
+ runtime->fragment_size,
+ runtime->fragments);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed rc = %d\n", ret);
+ return -ENOMEM;
+ }
+
+ prtd->byte_offset = 0;
+ prtd->received_total = 0;
+ prtd->app_pointer = 0;
+ prtd->bytes_copied = 0;
+ prtd->bytes_read = 0;
+ prtd->bytes_read_offset = 0;
+ prtd->buffer = ac->port[dir].buf[0].data;
+ prtd->buffer_paddr = ac->port[dir].buf[0].phys;
+ prtd->buffer_size = runtime->fragments * runtime->fragment_size;
+
+ /* Bit-0 of flags represent timestamp mode */
+ if (prtd->codec_param.codec.flags & COMPRESSED_TIMESTAMP_FLAG)
+ prtd->ts_header_offset = sizeof(struct snd_codec_metadata);
+ else
+ prtd->ts_header_offset = 0;
+
+ pr_debug("%s: sample_rate = %d channels = %d bps = %d sample_word_size = %d\n",
+ __func__, prtd->sample_rate, prtd->num_channels,
+ bits_per_sample, sample_word_size);
+ ret = q6asm_enc_cfg_blk_pcm_format_support_v3(prtd->audio_client,
+ prtd->sample_rate, prtd->num_channels,
+ bits_per_sample, sample_word_size);
+
+ return ret;
+}
+
+static int msm_compr_map_ion_fd(struct msm_compr_audio *prtd, int fd)
+{
+ ion_phys_addr_t paddr = 0;
+ size_t pa_len = 0;
+ int ret = 0;
+
+ ret = msm_audio_ion_phys_assign("audio_lib_mem_client",
+ &prtd->lib_ion_client,
+ &prtd->lib_ion_handle,
+ fd, &paddr, &pa_len, HLOS_TO_ADSP);
+ if (ret) {
+ pr_err("%s: audio lib ION phys failed, rc = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = q6core_add_remove_pool_pages(paddr, pa_len,
+ ADSP_MEMORY_MAP_HLOS_PHYSPOOL, true);
+ if (ret) {
+ pr_err("%s: add remove pages failed, rc = %d\n", __func__, ret);
+ /* Assign back to HLOS if add pages cmd failed */
+ msm_audio_ion_phys_free(prtd->lib_ion_client,
+ prtd->lib_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ }
+
+done:
+ return ret;
+}
+
+static int msm_compr_unmap_ion_fd(struct msm_compr_audio *prtd)
+{
+ ion_phys_addr_t paddr = 0;
+ size_t pa_len = 0;
+ int ret = 0;
+
+ if (!prtd->lib_ion_client || !prtd->lib_ion_handle) {
+ pr_err("%s: ion_client or ion_handle is NULL", __func__);
+ return -EINVAL;
+ }
+
+ ret = msm_audio_ion_phys_free(prtd->lib_ion_client,
+ prtd->lib_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ if (ret) {
+ pr_err("%s: audio lib ION phys failed, rc = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = q6core_add_remove_pool_pages(paddr, pa_len,
+ ADSP_MEMORY_MAP_HLOS_PHYSPOOL, false);
+ if (ret)
+ pr_err("%s: add remove pages failed, rc = %d\n", __func__, ret);
+
+done:
+ return ret;
+}
+
+static int msm_compr_playback_open(struct snd_compr_stream *cstream)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct msm_compr_audio *prtd = NULL;
+ struct msm_compr_pdata *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ if (pdata->is_in_use[rtd->dai_link->be_id] == true) {
+ pr_err("%s: %s is already in use,err: %d ",
+ __func__, rtd->dai_link->cpu_dai_name, -EBUSY);
+ return -EBUSY;
+ }
+ prtd = kzalloc(sizeof(struct msm_compr_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_compr_audio\n");
+ return -ENOMEM;
+ }
+
+ runtime->private_data = NULL;
+ prtd->cstream = cstream;
+ pdata->cstream[rtd->dai_link->be_id] = cstream;
+ pdata->audio_effects[rtd->dai_link->be_id] =
+ kzalloc(sizeof(struct msm_compr_audio_effects), GFP_KERNEL);
+ if (pdata->audio_effects[rtd->dai_link->be_id] == NULL) {
+ pr_err("%s: Could not allocate memory for effects\n", __func__);
+ ret = -ENOMEM;
+ goto effect_err;
+ }
+ pdata->dec_params[rtd->dai_link->be_id] =
+ kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL);
+ if (pdata->dec_params[rtd->dai_link->be_id] == NULL) {
+ pr_err("%s: Could not allocate memory for dec params\n",
+ __func__);
+ ret = -ENOMEM;
+ goto param_err;
+ }
+ prtd->codec = FORMAT_MP3;
+ prtd->bytes_received = 0;
+ prtd->bytes_sent = 0;
+ prtd->copied_total = 0;
+ prtd->byte_offset = 0;
+ prtd->sample_rate = 44100;
+ prtd->num_channels = 2;
+ prtd->drain_ready = 0;
+ prtd->last_buffer = 0;
+ prtd->first_buffer = 1;
+ prtd->partial_drain_delay = 0;
+ prtd->next_stream = 0;
+ memset(&prtd->gapless_state, 0, sizeof(struct msm_compr_gapless_state));
+ /*
+ * Update the use_dsp_gapless_mode from gapless struture with the value
+ * part of platform data.
+ */
+ prtd->gapless_state.use_dsp_gapless_mode = pdata->use_dsp_gapless_mode;
+
+ pr_debug("%s: gapless mode %d", __func__, pdata->use_dsp_gapless_mode);
+
+ spin_lock_init(&prtd->lock);
+
+ atomic_set(&prtd->eos, 0);
+ atomic_set(&prtd->start, 0);
+ atomic_set(&prtd->drain, 0);
+ atomic_set(&prtd->xrun, 0);
+ atomic_set(&prtd->close, 0);
+ atomic_set(&prtd->wait_on_close, 0);
+ atomic_set(&prtd->error, 0);
+
+ init_waitqueue_head(&prtd->eos_wait);
+ init_waitqueue_head(&prtd->drain_wait);
+ init_waitqueue_head(&prtd->close_wait);
+ init_waitqueue_head(&prtd->wait_for_stream_avail);
+
+ runtime->private_data = prtd;
+ populate_codec_list(prtd);
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)compr_event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_err("%s: Could not allocate memory for client\n", __func__);
+ ret = -ENOMEM;
+ goto ac_err;
+ }
+ pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+ prtd->audio_client->perf_mode = false;
+ prtd->session_id = prtd->audio_client->session;
+ msm_adsp_init_mixer_ctl_pp_event_queue(rtd);
+ if (pdata->ion_fd[rtd->dai_link->be_id] > 0) {
+ ret = msm_compr_map_ion_fd(prtd,
+ pdata->ion_fd[rtd->dai_link->be_id]);
+ if (ret < 0)
+ goto map_err;
+ }
+ pdata->is_in_use[rtd->dai_link->be_id] = true;
+ return 0;
+
+map_err:
+ q6asm_audio_client_free(prtd->audio_client);
+ac_err:
+ kfree(pdata->dec_params[rtd->dai_link->be_id]);
+ pdata->dec_params[rtd->dai_link->be_id] = NULL;
+param_err:
+ kfree(pdata->audio_effects[rtd->dai_link->be_id]);
+ pdata->audio_effects[rtd->dai_link->be_id] = NULL;
+effect_err:
+ pdata->cstream[rtd->dai_link->be_id] = NULL;
+ runtime->private_data = NULL;
+ kfree(prtd);
+ return ret;
+}
+
+static int msm_compr_capture_open(struct snd_compr_stream *cstream)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct msm_compr_audio *prtd;
+ struct msm_compr_pdata *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct msm_compr_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_compr_audio\n");
+ return -ENOMEM;
+ }
+
+ runtime->private_data = NULL;
+ prtd->cstream = cstream;
+ pdata->cstream[rtd->dai_link->be_id] = cstream;
+
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)compr_event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_err("%s: Could not allocate memory for client\n", __func__);
+ pdata->cstream[rtd->dai_link->be_id] = NULL;
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+ prtd->audio_client->perf_mode = false;
+ prtd->session_id = prtd->audio_client->session;
+ prtd->codec = FORMAT_LINEAR_PCM;
+ prtd->bytes_copied = 0;
+ prtd->bytes_read = 0;
+ prtd->bytes_read_offset = 0;
+ prtd->received_total = 0;
+ prtd->byte_offset = 0;
+ prtd->sample_rate = 48000;
+ prtd->num_channels = 2;
+ prtd->first_buffer = 0;
+
+ spin_lock_init(&prtd->lock);
+
+ atomic_set(&prtd->eos, 0);
+ atomic_set(&prtd->start, 0);
+ atomic_set(&prtd->drain, 0);
+ atomic_set(&prtd->xrun, 0);
+ atomic_set(&prtd->close, 0);
+ atomic_set(&prtd->wait_on_close, 0);
+ atomic_set(&prtd->error, 0);
+
+ runtime->private_data = prtd;
+
+ return 0;
+}
+
+static int msm_compr_open(struct snd_compr_stream *cstream)
+{
+ int ret = 0;
+
+ if (cstream->direction == SND_COMPRESS_PLAYBACK)
+ ret = msm_compr_playback_open(cstream);
+ else if (cstream->direction == SND_COMPRESS_CAPTURE)
+ ret = msm_compr_capture_open(cstream);
+ return ret;
+}
+
+static int msm_compr_playback_free(struct snd_compr_stream *cstream)
+{
+ struct snd_compr_runtime *runtime;
+ struct msm_compr_audio *prtd;
+ struct snd_soc_pcm_runtime *soc_prtd;
+ struct msm_compr_pdata *pdata;
+ struct audio_client *ac;
+ int dir = IN, ret = 0, stream_id;
+ unsigned long flags;
+ uint32_t stream_index;
+ ion_phys_addr_t paddr = 0;
+ size_t pa_len = 0;
+
+ pr_debug("%s\n", __func__);
+
+ if (!cstream) {
+ pr_err("%s cstream is null\n", __func__);
+ return 0;
+ }
+ runtime = cstream->runtime;
+ soc_prtd = cstream->private_data;
+ if (!runtime || !soc_prtd || !(soc_prtd->platform)) {
+ pr_err("%s runtime or soc_prtd or platform is null\n",
+ __func__);
+ return 0;
+ }
+ prtd = runtime->private_data;
+ if (!prtd) {
+ pr_err("%s prtd is null\n", __func__);
+ return 0;
+ }
+ prtd->cmd_interrupt = 1;
+ wake_up(&prtd->drain_wait);
+ pdata = snd_soc_platform_get_drvdata(soc_prtd->platform);
+ ac = prtd->audio_client;
+ if (!pdata || !ac) {
+ pr_err("%s pdata or ac is null\n", __func__);
+ return 0;
+ }
+ if (atomic_read(&prtd->eos)) {
+ ret = wait_event_timeout(prtd->eos_wait,
+ prtd->eos_ack, 5 * HZ);
+ if (!ret)
+ pr_err("%s: CMD_EOS failed\n", __func__);
+ }
+ if (atomic_read(&prtd->close)) {
+ prtd->cmd_ack = 0;
+ atomic_set(&prtd->wait_on_close, 1);
+ ret = wait_event_timeout(prtd->close_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (!ret)
+ pr_err("%s: CMD_CLOSE failed\n", __func__);
+ }
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ stream_id = ac->stream_id;
+ stream_index = STREAM_ARRAY_INDEX(NEXT_STREAM_ID(stream_id));
+
+ if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) &&
+ (prtd->gapless_state.stream_opened[stream_index])) {
+ prtd->gapless_state.stream_opened[stream_index] = 0;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ pr_debug(" close stream %d", NEXT_STREAM_ID(stream_id));
+ q6asm_stream_cmd(ac, CMD_CLOSE, NEXT_STREAM_ID(stream_id));
+ spin_lock_irqsave(&prtd->lock, flags);
+ }
+
+ stream_index = STREAM_ARRAY_INDEX(stream_id);
+ if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) &&
+ (prtd->gapless_state.stream_opened[stream_index])) {
+ prtd->gapless_state.stream_opened[stream_index] = 0;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ pr_debug("close stream %d", stream_id);
+ q6asm_stream_cmd(ac, CMD_CLOSE, stream_id);
+ spin_lock_irqsave(&prtd->lock, flags);
+ }
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ mutex_lock(&pdata->lock);
+ pdata->cstream[soc_prtd->dai_link->be_id] = NULL;
+ if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+
+ q6asm_audio_client_buf_free_contiguous(dir, ac);
+ if (prtd->shm_ion_fd > 0)
+ msm_audio_ion_phys_free(prtd->shm_ion_client,
+ prtd->shm_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ if (pdata->ion_fd[soc_prtd->dai_link->be_id] > 0) {
+ msm_compr_unmap_ion_fd(prtd);
+ pdata->ion_fd[soc_prtd->dai_link->be_id] = 0;
+ }
+
+ q6asm_audio_client_free(ac);
+ msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd);
+ if (pdata->audio_effects[soc_prtd->dai_link->be_id] != NULL) {
+ kfree(pdata->audio_effects[soc_prtd->dai_link->be_id]);
+ pdata->audio_effects[soc_prtd->dai_link->be_id] = NULL;
+ }
+ if (pdata->dec_params[soc_prtd->dai_link->be_id] != NULL) {
+ kfree(pdata->dec_params[soc_prtd->dai_link->be_id]);
+ pdata->dec_params[soc_prtd->dai_link->be_id] = NULL;
+ }
+ pdata->is_in_use[soc_prtd->dai_link->be_id] = false;
+ kfree(prtd);
+ runtime->private_data = NULL;
+ mutex_unlock(&pdata->lock);
+
+ return 0;
+}
+
+static int msm_compr_capture_free(struct snd_compr_stream *cstream)
+{
+ struct snd_compr_runtime *runtime;
+ struct msm_compr_audio *prtd;
+ struct snd_soc_pcm_runtime *soc_prtd;
+ struct msm_compr_pdata *pdata;
+ struct audio_client *ac;
+ int dir = OUT, stream_id;
+ unsigned long flags;
+ uint32_t stream_index;
+
+ if (!cstream) {
+ pr_err("%s cstream is null\n", __func__);
+ return 0;
+ }
+ runtime = cstream->runtime;
+ soc_prtd = cstream->private_data;
+ if (!runtime || !soc_prtd || !(soc_prtd->platform)) {
+ pr_err("%s runtime or soc_prtd or platform is null\n",
+ __func__);
+ return 0;
+ }
+ prtd = runtime->private_data;
+ if (!prtd) {
+ pr_err("%s prtd is null\n", __func__);
+ return 0;
+ }
+ pdata = snd_soc_platform_get_drvdata(soc_prtd->platform);
+ ac = prtd->audio_client;
+ if (!pdata || !ac) {
+ pr_err("%s pdata or ac is null\n", __func__);
+ return 0;
+ }
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ stream_id = ac->stream_id;
+
+ stream_index = STREAM_ARRAY_INDEX(stream_id);
+ if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0)) {
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ pr_debug("close stream %d", stream_id);
+ q6asm_stream_cmd(ac, CMD_CLOSE, stream_id);
+ spin_lock_irqsave(&prtd->lock, flags);
+ }
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ mutex_lock(&pdata->lock);
+ pdata->cstream[soc_prtd->dai_link->be_id] = NULL;
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+
+ q6asm_audio_client_buf_free_contiguous(dir, ac);
+
+ q6asm_audio_client_free(ac);
+
+ kfree(prtd);
+ runtime->private_data = NULL;
+ mutex_unlock(&pdata->lock);
+
+ return 0;
+}
+
+static int msm_compr_free(struct snd_compr_stream *cstream)
+{
+ int ret = 0;
+
+ if (cstream->direction == SND_COMPRESS_PLAYBACK)
+ ret = msm_compr_playback_free(cstream);
+ else if (cstream->direction == SND_COMPRESS_CAPTURE)
+ ret = msm_compr_capture_free(cstream);
+ return ret;
+}
+
+static bool msm_compr_validate_codec_compr(__u32 codec_id)
+{
+ int32_t i;
+
+ for (i = 0; i < ARRAY_SIZE(compr_codecs); i++) {
+ if (compr_codecs[i] == codec_id)
+ return true;
+ }
+ return false;
+}
+
+/* compress stream operations */
+static int msm_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_compr_audio *prtd = runtime->private_data;
+ int ret = 0, frame_sz = 0;
+ int i, num_rates;
+ bool is_format_gapless = false;
+
+ pr_debug("%s\n", __func__);
+
+ num_rates = sizeof(supported_sample_rates)/sizeof(unsigned int);
+ for (i = 0; i < num_rates; i++)
+ if (params->codec.sample_rate == supported_sample_rates[i])
+ break;
+ if (i == num_rates)
+ return -EINVAL;
+
+ memcpy(&prtd->codec_param, params, sizeof(struct snd_compr_params));
+ /* ToDo: remove duplicates */
+ prtd->num_channels = prtd->codec_param.codec.ch_in;
+ prtd->sample_rate = prtd->codec_param.codec.sample_rate;
+ pr_debug("%s: sample_rate %d\n", __func__, prtd->sample_rate);
+
+ if ((prtd->codec_param.codec.compr_passthr >= LEGACY_PCM &&
+ prtd->codec_param.
+ codec.compr_passthr <= COMPRESSED_PASSTHROUGH_DSD) ||
+ (prtd->codec_param.
+ codec.compr_passthr == COMPRESSED_PASSTHROUGH_IEC61937))
+ prtd->compr_passthr = prtd->codec_param.codec.compr_passthr;
+ else
+ prtd->compr_passthr = LEGACY_PCM;
+ pr_debug("%s: compr_passthr = %d", __func__, prtd->compr_passthr);
+ if (prtd->compr_passthr != LEGACY_PCM) {
+ pr_debug("%s: Reset gapless mode playback for compr_type[%d]\n",
+ __func__, prtd->compr_passthr);
+ prtd->gapless_state.use_dsp_gapless_mode = 0;
+ if (!msm_compr_validate_codec_compr(params->codec.id)) {
+ pr_err("%s codec not supported in passthrough,id =%d\n",
+ __func__, params->codec.id);
+ return -EINVAL;
+ }
+ }
+
+ switch (params->codec.id) {
+ case SND_AUDIOCODEC_PCM: {
+ pr_debug("SND_AUDIOCODEC_PCM\n");
+ prtd->codec = FORMAT_LINEAR_PCM;
+ is_format_gapless = true;
+ break;
+ }
+
+ case SND_AUDIOCODEC_MP3: {
+ pr_debug("SND_AUDIOCODEC_MP3\n");
+ prtd->codec = FORMAT_MP3;
+ frame_sz = MP3_OUTPUT_FRAME_SZ;
+ is_format_gapless = true;
+ break;
+ }
+
+ case SND_AUDIOCODEC_AAC: {
+ pr_debug("SND_AUDIOCODEC_AAC\n");
+ prtd->codec = FORMAT_MPEG4_AAC;
+ frame_sz = AAC_OUTPUT_FRAME_SZ;
+ is_format_gapless = true;
+ break;
+ }
+
+ case SND_AUDIOCODEC_AC3: {
+ pr_debug("SND_AUDIOCODEC_AC3\n");
+ prtd->codec = FORMAT_AC3;
+ frame_sz = AC3_OUTPUT_FRAME_SZ;
+ is_format_gapless = true;
+ break;
+ }
+
+ case SND_AUDIOCODEC_EAC3: {
+ pr_debug("SND_AUDIOCODEC_EAC3\n");
+ prtd->codec = FORMAT_EAC3;
+ frame_sz = EAC3_OUTPUT_FRAME_SZ;
+ is_format_gapless = true;
+ break;
+ }
+
+ case SND_AUDIOCODEC_MP2: {
+ pr_debug("SND_AUDIOCODEC_MP2\n");
+ prtd->codec = FORMAT_MP2;
+ break;
+ }
+
+ case SND_AUDIOCODEC_WMA: {
+ pr_debug("SND_AUDIOCODEC_WMA\n");
+ prtd->codec = FORMAT_WMA_V9;
+ break;
+ }
+
+ case SND_AUDIOCODEC_WMA_PRO: {
+ pr_debug("SND_AUDIOCODEC_WMA_PRO\n");
+ prtd->codec = FORMAT_WMA_V10PRO;
+ break;
+ }
+
+ case SND_AUDIOCODEC_FLAC: {
+ pr_debug("%s: SND_AUDIOCODEC_FLAC\n", __func__);
+ prtd->codec = FORMAT_FLAC;
+ /*
+ * DSP bufferring is based on blk size,
+ * consider mininum buffering to rule out any false wait
+ */
+ frame_sz =
+ prtd->codec_param.codec.options.flac_dec.min_blk_size;
+ is_format_gapless = true;
+ break;
+ }
+
+ case SND_AUDIOCODEC_VORBIS: {
+ pr_debug("%s: SND_AUDIOCODEC_VORBIS\n", __func__);
+ prtd->codec = FORMAT_VORBIS;
+ break;
+ }
+
+ case SND_AUDIOCODEC_ALAC: {
+ pr_debug("%s: SND_AUDIOCODEC_ALAC\n", __func__);
+ prtd->codec = FORMAT_ALAC;
+ break;
+ }
+
+ case SND_AUDIOCODEC_APE: {
+ pr_debug("%s: SND_AUDIOCODEC_APE\n", __func__);
+ prtd->codec = FORMAT_APE;
+ break;
+ }
+
+ case SND_AUDIOCODEC_DTS: {
+ pr_debug("%s: SND_AUDIOCODEC_DTS\n", __func__);
+ prtd->codec = FORMAT_DTS;
+ break;
+ }
+
+ case SND_AUDIOCODEC_DSD: {
+ pr_debug("%s: SND_AUDIOCODEC_DSD\n", __func__);
+ prtd->codec = FORMAT_DSD;
+ break;
+ }
+
+ case SND_AUDIOCODEC_TRUEHD: {
+ pr_debug("%s: SND_AUDIOCODEC_TRUEHD\n", __func__);
+ prtd->codec = FORMAT_TRUEHD;
+ break;
+ }
+
+ case SND_AUDIOCODEC_IEC61937: {
+ pr_debug("%s: SND_AUDIOCODEC_IEC61937\n", __func__);
+ prtd->codec = FORMAT_IEC61937;
+ break;
+ }
+
+ case SND_AUDIOCODEC_APTX: {
+ pr_debug("%s: SND_AUDIOCODEC_APTX\n", __func__);
+ prtd->codec = FORMAT_APTX;
+ break;
+ }
+
+ case SND_AUDIOCODEC_APTXHD: {
+ pr_debug("%s: SND_AUDIOCODEC_APTXHD\n", __func__);
+ prtd->codec = FORMAT_APTXHD;
+ break;
+ }
+
+ default:
+ pr_err("codec not supported, id =%d\n", params->codec.id);
+ return -EINVAL;
+ }
+
+ if (!is_format_gapless)
+ prtd->gapless_state.use_dsp_gapless_mode = false;
+
+ prtd->partial_drain_delay =
+ msm_compr_get_partial_drain_delay(frame_sz, prtd->sample_rate);
+
+ if (cstream->direction == SND_COMPRESS_PLAYBACK)
+ ret = msm_compr_configure_dsp_for_playback(cstream);
+ else if (cstream->direction == SND_COMPRESS_CAPTURE)
+ ret = msm_compr_configure_dsp_for_capture(cstream);
+
+ return ret;
+}
+
+static int msm_compr_drain_buffer(struct msm_compr_audio *prtd,
+ unsigned long *flags)
+{
+ int rc = 0;
+
+ atomic_set(&prtd->drain, 1);
+ prtd->drain_ready = 0;
+ spin_unlock_irqrestore(&prtd->lock, *flags);
+ pr_debug("%s: wait for buffer to be drained\n", __func__);
+ rc = wait_event_interruptible(prtd->drain_wait,
+ prtd->drain_ready ||
+ prtd->cmd_interrupt ||
+ atomic_read(&prtd->xrun) ||
+ atomic_read(&prtd->error));
+ pr_debug("%s: out of buffer drain wait with ret %d\n", __func__, rc);
+ spin_lock_irqsave(&prtd->lock, *flags);
+ if (prtd->cmd_interrupt) {
+ pr_debug("%s: buffer drain interrupted by flush)\n", __func__);
+ rc = -EINTR;
+ prtd->cmd_interrupt = 0;
+ }
+ if (atomic_read(&prtd->error)) {
+ pr_err("%s: Got RESET EVENTS notification, return\n",
+ __func__);
+ rc = -ENETRESET;
+ }
+ return rc;
+}
+
+static int msm_compr_wait_for_stream_avail(struct msm_compr_audio *prtd,
+ unsigned long *flags)
+{
+ int rc = 0;
+ pr_debug("next session is already in opened state\n");
+ prtd->next_stream = 1;
+ prtd->cmd_interrupt = 0;
+ spin_unlock_irqrestore(&prtd->lock, *flags);
+ /*
+ * Wait for stream to be available, or the wait to be interrupted by
+ * commands like flush or till a timeout of one second.
+ */
+ rc = wait_event_timeout(prtd->wait_for_stream_avail,
+ prtd->stream_available || prtd->cmd_interrupt, 1 * HZ);
+ pr_err("%s:prtd->stream_available %d, prtd->cmd_interrupt %d rc %d\n",
+ __func__, prtd->stream_available, prtd->cmd_interrupt, rc);
+
+ spin_lock_irqsave(&prtd->lock, *flags);
+ if (rc == 0) {
+ pr_err("%s: wait_for_stream_avail timed out\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ } else if (prtd->cmd_interrupt == 1) {
+ /*
+ * This scenario might not happen as we do not allow
+ * flush in transition state.
+ */
+ pr_debug("%s: wait_for_stream_avail interrupted\n", __func__);
+ prtd->cmd_interrupt = 0;
+ prtd->stream_available = 0;
+ rc = -EINTR;
+ } else {
+ prtd->stream_available = 0;
+ rc = 0;
+ }
+ pr_debug("%s : rc = %d", __func__, rc);
+ return rc;
+}
+
+static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_compr_audio *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct msm_compr_pdata *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ uint32_t *volume = pdata->volume[rtd->dai_link->be_id];
+ struct audio_client *ac = prtd->audio_client;
+ unsigned long fe_id = rtd->dai_link->be_id;
+ int rc = 0;
+ int bytes_to_write;
+ unsigned long flags;
+ int stream_id;
+ uint32_t stream_index;
+ uint16_t bits_per_sample = 16;
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ if (atomic_read(&prtd->error)) {
+ pr_err("%s Got RESET EVENTS notification, return immediately",
+ __func__);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__);
+ atomic_set(&prtd->start, 1);
+
+ /*
+ * compr_set_volume and compr_init_pp_params
+ * are used to configure ASM volume hence not
+ * needed for compress passthrough playback.
+ *
+ * compress passthrough volume is controlled in
+ * ADM by adm_send_compressed_device_mute()
+ */
+ if (prtd->compr_passthr == LEGACY_PCM &&
+ cstream->direction == SND_COMPRESS_PLAYBACK) {
+ /* set volume for the stream before RUN */
+ rc = msm_compr_set_volume(cstream,
+ volume[0], volume[1]);
+ if (rc)
+ pr_err("%s : Set Volume failed : %d\n",
+ __func__, rc);
+
+ rc = msm_compr_init_pp_params(cstream, ac);
+ if (rc)
+ pr_err("%s : init PP params failed : %d\n",
+ __func__, rc);
+ } else {
+ msm_compr_read_buffer(prtd);
+ }
+ /* issue RUN command for the stream */
+ q6asm_run_nowait(prtd->audio_client, prtd->run_mode,
+ prtd->start_delay_msw, prtd->start_delay_lsw);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ spin_lock_irqsave(&prtd->lock, flags);
+ pr_debug("%s: SNDRV_PCM_TRIGGER_STOP transition %d\n", __func__,
+ prtd->gapless_state.gapless_transition);
+ stream_id = ac->stream_id;
+ atomic_set(&prtd->start, 0);
+ if (cstream->direction == SND_COMPRESS_CAPTURE) {
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->xrun, 0);
+ prtd->received_total = 0;
+ prtd->bytes_copied = 0;
+ prtd->bytes_read = 0;
+ prtd->bytes_read_offset = 0;
+ prtd->byte_offset = 0;
+ prtd->app_pointer = 0;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+ if (prtd->next_stream) {
+ pr_debug("%s: interrupt next track wait queues\n",
+ __func__);
+ prtd->cmd_interrupt = 1;
+ wake_up(&prtd->wait_for_stream_avail);
+ prtd->next_stream = 0;
+ }
+ if (atomic_read(&prtd->eos)) {
+ pr_debug("%s: interrupt eos wait queues", __func__);
+ /*
+ * Gapless playback does not wait for eos, do not set
+ * cmd_int and do not wake up eos_wait during gapless
+ * transition
+ */
+ if (!prtd->gapless_state.gapless_transition) {
+ prtd->cmd_interrupt = 1;
+ wake_up(&prtd->eos_wait);
+ }
+ atomic_set(&prtd->eos, 0);
+ }
+ if (atomic_read(&prtd->drain)) {
+ pr_debug("%s: interrupt drain wait queues", __func__);
+ prtd->cmd_interrupt = 1;
+ prtd->drain_ready = 1;
+ wake_up(&prtd->drain_wait);
+ atomic_set(&prtd->drain, 0);
+ }
+ prtd->last_buffer = 0;
+ prtd->cmd_ack = 0;
+ if (!prtd->gapless_state.gapless_transition) {
+ pr_debug("issue CMD_FLUSH stream_id %d\n", stream_id);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ q6asm_stream_cmd(
+ prtd->audio_client, CMD_FLUSH, stream_id);
+ spin_lock_irqsave(&prtd->lock, flags);
+ } else {
+ prtd->first_buffer = 0;
+ }
+ /* FIXME. only reset if flush was successful */
+ prtd->byte_offset = 0;
+ prtd->copied_total = 0;
+ prtd->app_pointer = 0;
+ prtd->bytes_received = 0;
+ prtd->bytes_sent = 0;
+ prtd->marker_timestamp = 0;
+
+ atomic_set(&prtd->xrun, 0);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE_PUSH transition %d\n",
+ prtd->gapless_state.gapless_transition);
+ if (!prtd->gapless_state.gapless_transition) {
+ pr_debug("issue CMD_PAUSE stream_id %d\n",
+ ac->stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id);
+ atomic_set(&prtd->start, 0);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE_RELEASE transition %d\n",
+ prtd->gapless_state.gapless_transition);
+ if (!prtd->gapless_state.gapless_transition) {
+ atomic_set(&prtd->start, 1);
+ q6asm_run_nowait(prtd->audio_client, prtd->run_mode,
+ 0, 0);
+ }
+ break;
+ case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+ pr_debug("%s: SND_COMPR_TRIGGER_PARTIAL_DRAIN\n", __func__);
+ if (!prtd->gapless_state.use_dsp_gapless_mode) {
+ pr_debug("%s: set partial drain as drain\n", __func__);
+ cmd = SND_COMPR_TRIGGER_DRAIN;
+ }
+ case SND_COMPR_TRIGGER_DRAIN:
+ pr_debug("%s: SNDRV_COMPRESS_DRAIN\n", __func__);
+ /* Make sure all the data is sent to DSP before sending EOS */
+ spin_lock_irqsave(&prtd->lock, flags);
+
+ if (!atomic_read(&prtd->start)) {
+ pr_err("%s: stream is not in started state\n",
+ __func__);
+ rc = -EPERM;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+ if (prtd->bytes_received > prtd->copied_total) {
+ pr_debug("%s: wait till all the data is sent to dsp\n",
+ __func__);
+ rc = msm_compr_drain_buffer(prtd, &flags);
+ if (rc || !atomic_read(&prtd->start)) {
+ if (rc != -ENETRESET)
+ rc = -EINTR;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+ /*
+ * FIXME: Bug.
+ * Write(32767)
+ * Start
+ * Drain <- Indefinite wait
+ * sol1 : if (prtd->copied_total) then wait?
+ * sol2 : (prtd->cmd_interrupt || prtd->drain_ready || atomic_read(xrun)
+ */
+ bytes_to_write = prtd->bytes_received
+ - prtd->copied_total;
+ WARN(bytes_to_write > runtime->fragment_size,
+ "last write %d cannot be > than fragment_size",
+ bytes_to_write);
+
+ if (bytes_to_write > 0) {
+ pr_debug("%s: send %d partial bytes at the end",
+ __func__, bytes_to_write);
+ atomic_set(&prtd->xrun, 0);
+ prtd->last_buffer = 1;
+ msm_compr_send_buffer(prtd);
+ }
+ }
+
+ if ((cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN) &&
+ (prtd->gapless_state.set_next_stream_id)) {
+ /* wait for the last buffer to be returned */
+
+ if (prtd->last_buffer) {
+ pr_debug("%s: last buffer drain\n", __func__);
+ rc = msm_compr_drain_buffer(prtd, &flags);
+ if (rc || !atomic_read(&prtd->start)) {
+ spin_unlock_irqrestore(&prtd->lock,
+ flags);
+ break;
+ }
+ }
+ /* send EOS */
+ prtd->eos_ack = 0;
+ atomic_set(&prtd->eos, 1);
+ pr_debug("issue CMD_EOS stream_id %d\n", ac->stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id);
+ pr_info("PARTIAL DRAIN, do not wait for EOS ack\n");
+
+ /* send a zero length buffer */
+ atomic_set(&prtd->xrun, 0);
+ msm_compr_send_buffer(prtd);
+
+ /* wait for the zero length buffer to be returned */
+ pr_debug("%s: zero length buffer drain\n", __func__);
+ rc = msm_compr_drain_buffer(prtd, &flags);
+ if (rc || !atomic_read(&prtd->start)) {
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+
+ /* sleep for additional duration partial drain */
+ atomic_set(&prtd->drain, 1);
+ prtd->drain_ready = 0;
+ pr_debug("%s, additional sleep: %d\n", __func__,
+ prtd->partial_drain_delay);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ rc = wait_event_timeout(prtd->drain_wait,
+ prtd->drain_ready || prtd->cmd_interrupt,
+ msecs_to_jiffies(prtd->partial_drain_delay));
+ pr_debug("%s: out of additional wait for low sample rate\n",
+ __func__);
+ spin_lock_irqsave(&prtd->lock, flags);
+ if (prtd->cmd_interrupt) {
+ pr_debug("%s: additional wait interrupted by flush)\n",
+ __func__);
+ rc = -EINTR;
+ prtd->cmd_interrupt = 0;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+
+ /* move to next stream and reset vars */
+ pr_debug("%s: Moving to next stream in gapless\n",
+ __func__);
+ ac->stream_id = NEXT_STREAM_ID(ac->stream_id);
+ prtd->byte_offset = 0;
+ prtd->app_pointer = 0;
+ prtd->first_buffer = 1;
+ prtd->last_buffer = 0;
+ /*
+ * Set gapless transition flag only if EOS hasn't been
+ * acknowledged already.
+ */
+ if (atomic_read(&prtd->eos))
+ prtd->gapless_state.gapless_transition = 1;
+ prtd->marker_timestamp = 0;
+
+ /*
+ Don't reset these as these vars map to
+ total_bytes_transferred and total_bytes_available
+ directly, only total_bytes_transferred will be updated
+ in the next avail() ioctl
+ prtd->copied_total = 0;
+ prtd->bytes_received = 0;
+ */
+ atomic_set(&prtd->drain, 0);
+ atomic_set(&prtd->xrun, 1);
+ pr_debug("%s: issue CMD_RUN", __func__);
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+ /*
+ moving to next stream failed, so reset the gapless state
+ set next stream id for the same session so that the same
+ stream can be used for gapless playback
+ */
+ prtd->gapless_state.set_next_stream_id = false;
+ prtd->gapless_state.gapless_transition = 0;
+ pr_debug("%s:CMD_EOS stream_id %d\n", __func__, ac->stream_id);
+
+ prtd->eos_ack = 0;
+ atomic_set(&prtd->eos, 1);
+ q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id);
+
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+
+ /* Wait indefinitely for DRAIN. Flush can also signal this*/
+ rc = wait_event_interruptible(prtd->eos_wait,
+ (prtd->eos_ack ||
+ prtd->cmd_interrupt ||
+ atomic_read(&prtd->error)));
+
+ if (rc < 0)
+ pr_err("%s: EOS wait failed\n", __func__);
+
+ pr_debug("%s: SNDRV_COMPRESS_DRAIN out of wait for EOS\n",
+ __func__);
+
+ if (prtd->cmd_interrupt)
+ rc = -EINTR;
+
+ if (atomic_read(&prtd->error)) {
+ pr_err("%s: Got RESET EVENTS notification, return\n",
+ __func__);
+ rc = -ENETRESET;
+ }
+
+ /*FIXME : what if a flush comes while PC is here */
+ if (rc == 0) {
+ /*
+ * Failed to open second stream in DSP for gapless
+ * so prepare the current stream in session
+ * for gapless playback
+ */
+ spin_lock_irqsave(&prtd->lock, flags);
+ pr_debug("%s:issue CMD_PAUSE stream_id %d",
+ __func__, ac->stream_id);
+ q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id);
+ prtd->cmd_ack = 0;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ /*
+ * Cache this time as last known time
+ */
+ if (pdata->use_legacy_api)
+ q6asm_get_session_time_legacy(
+ prtd->audio_client,
+ &prtd->marker_timestamp);
+ else
+ q6asm_get_session_time(prtd->audio_client,
+ &prtd->marker_timestamp);
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ /*
+ * Don't reset these as these vars map to
+ * total_bytes_transferred and total_bytes_available.
+ * Just total_bytes_transferred will be updated
+ * in the next avail() ioctl.
+ * prtd->copied_total = 0;
+ * prtd->bytes_received = 0;
+ * do not reset prtd->bytes_sent as well as the same
+ * session is used for gapless playback
+ */
+ prtd->byte_offset = 0;
+
+ prtd->app_pointer = 0;
+ prtd->first_buffer = 1;
+ prtd->last_buffer = 0;
+ atomic_set(&prtd->drain, 0);
+ atomic_set(&prtd->xrun, 1);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ pr_debug("%s:issue CMD_FLUSH ac->stream_id %d",
+ __func__, ac->stream_id);
+ q6asm_stream_cmd(ac, CMD_FLUSH, ac->stream_id);
+
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ }
+ prtd->cmd_interrupt = 0;
+ break;
+ case SND_COMPR_TRIGGER_NEXT_TRACK:
+ if (!prtd->gapless_state.use_dsp_gapless_mode) {
+ pr_debug("%s: ignore trigger next track\n", __func__);
+ rc = 0;
+ break;
+ }
+ pr_debug("%s: SND_COMPR_TRIGGER_NEXT_TRACK\n", __func__);
+ spin_lock_irqsave(&prtd->lock, flags);
+ rc = 0;
+ /* next stream in gapless */
+ stream_id = NEXT_STREAM_ID(ac->stream_id);
+ /*
+ * Wait if stream 1 has not completed before honoring next
+ * track for stream 3. Scenario happens if second clip is
+ * small and fills in one buffer so next track will be
+ * called immediately.
+ */
+ stream_index = STREAM_ARRAY_INDEX(stream_id);
+ if (stream_index >= MAX_NUMBER_OF_STREAMS ||
+ stream_index < 0) {
+ pr_err("%s: Invalid stream index: %d", __func__,
+ stream_index);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (prtd->gapless_state.stream_opened[stream_index]) {
+ if (prtd->gapless_state.gapless_transition) {
+ rc = msm_compr_wait_for_stream_avail(prtd,
+ &flags);
+ } else {
+ /*
+ * If session is already opened break out if
+ * the state is not gapless transition. This
+ * is when seek happens after the last buffer
+ * is sent to the driver. Next track would be
+ * called again after last buffer is sent.
+ */
+ pr_debug("next session is in opened state\n");
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ if (rc < 0) {
+ /*
+ * if return type EINTR then reset to zero. Tiny
+ * compress treats EINTR as error and prevents PARTIAL
+ * DRAIN. EINTR is not an error. wait for stream avail
+ * is interrupted by some other command like FLUSH.
+ */
+ if (rc == -EINTR) {
+ pr_debug("%s: EINTR reset rc to 0\n", __func__);
+ rc = 0;
+ }
+ break;
+ }
+
+ if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE)
+ bits_per_sample = 24;
+ else if (prtd->codec_param.codec.format ==
+ SNDRV_PCM_FORMAT_S32_LE)
+ bits_per_sample = 32;
+
+ pr_debug("%s: open_write stream_id %d bits_per_sample %d",
+ __func__, stream_id, bits_per_sample);
+ if (pdata->avs_ver &&
+ (q6core_get_avs_version() == Q6_SUBSYS_AVS2_7))
+ rc = q6asm_stream_open_write_v3(prtd->audio_client,
+ prtd->codec, bits_per_sample,
+ stream_id,
+ prtd->gapless_state.use_dsp_gapless_mode);
+ else
+ rc = q6asm_stream_open_write_v4(prtd->audio_client,
+ prtd->codec, bits_per_sample,
+ stream_id,
+ prtd->gapless_state.use_dsp_gapless_mode);
+ if (rc < 0) {
+ pr_err("%s: Session out open failed for gapless\n",
+ __func__);
+ break;
+ }
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ prtd->gapless_state.stream_opened[stream_index] = 1;
+ prtd->gapless_state.set_next_stream_id = true;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ rc = msm_compr_send_media_format_block(cstream,
+ stream_id, false);
+ if (rc < 0) {
+ pr_err("%s, failed to send media format block\n",
+ __func__);
+ break;
+ }
+ msm_compr_send_dec_params(cstream, pdata->dec_params[fe_id],
+ stream_id);
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *arg)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct msm_compr_audio *prtd = runtime->private_data;
+ struct msm_compr_pdata *pdata = NULL;
+ struct snd_compr_tstamp tstamp;
+ uint64_t timestamp = 0;
+ int rc = 0, first_buffer;
+ unsigned long flags;
+ uint32_t gapless_transition;
+
+ pdata = snd_soc_platform_get_drvdata(rtd->platform);
+ pr_debug("%s\n", __func__);
+ memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ tstamp.sampling_rate = prtd->sample_rate;
+ tstamp.byte_offset = prtd->byte_offset;
+ if (cstream->direction == SND_COMPRESS_PLAYBACK)
+ tstamp.copied_total = prtd->copied_total;
+ else if (cstream->direction == SND_COMPRESS_CAPTURE)
+ tstamp.copied_total = prtd->received_total;
+ first_buffer = prtd->first_buffer;
+ if (atomic_read(&prtd->error)) {
+ pr_err_ratelimited("%s Got RESET EVENTS notification, return error\n",
+ __func__);
+ if (cstream->direction == SND_COMPRESS_PLAYBACK)
+ runtime->total_bytes_transferred = tstamp.copied_total;
+ else
+ runtime->total_bytes_available = tstamp.copied_total;
+ tstamp.pcm_io_frames = 0;
+ memcpy(arg, &tstamp, sizeof(struct snd_compr_tstamp));
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ return -ENETRESET;
+ }
+ if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+
+ gapless_transition = prtd->gapless_state.gapless_transition;
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ if (gapless_transition)
+ pr_debug("%s session time in gapless transition",
+ __func__);
+ /*
+ *- Do not query if no buffer has been given.
+ *- Do not query on a gapless transition.
+ * Playback for the 2nd stream can start (thus returning time
+ * starting from 0) before the driver knows about EOS of first
+ * stream.
+ */
+ if (!first_buffer || gapless_transition) {
+
+ if (pdata->use_legacy_api)
+ rc = q6asm_get_session_time_legacy(
+ prtd->audio_client, &prtd->marker_timestamp);
+ else
+ rc = q6asm_get_session_time(
+ prtd->audio_client, &prtd->marker_timestamp);
+ if (rc < 0) {
+ pr_err("%s: Get Session Time return =%lld\n",
+ __func__, timestamp);
+ if (atomic_read(&prtd->error))
+ return -ENETRESET;
+ else
+ return -EAGAIN;
+ }
+ }
+ } else {
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ }
+ timestamp = prtd->marker_timestamp;
+
+ /* DSP returns timestamp in usec */
+ pr_debug("%s: timestamp = %lld usec\n", __func__, timestamp);
+ timestamp *= prtd->sample_rate;
+ tstamp.pcm_io_frames = (snd_pcm_uframes_t)div64_u64(timestamp, 1000000);
+ memcpy(arg, &tstamp, sizeof(struct snd_compr_tstamp));
+
+ return 0;
+}
+
+static int msm_compr_ack(struct snd_compr_stream *cstream,
+ size_t count)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_compr_audio *prtd = runtime->private_data;
+ void *src, *dstn;
+ size_t copy;
+ unsigned long flags;
+
+ WARN(1, "This path is untested");
+ return -EINVAL;
+
+ pr_debug("%s: count = %zd\n", __func__, count);
+ if (!prtd->buffer) {
+ pr_err("%s: Buffer is not allocated yet ??\n", __func__);
+ return -EINVAL;
+ }
+ src = runtime->buffer + prtd->app_pointer;
+ dstn = prtd->buffer + prtd->app_pointer;
+ if (count < prtd->buffer_size - prtd->app_pointer) {
+ memcpy(dstn, src, count);
+ prtd->app_pointer += count;
+ } else {
+ copy = prtd->buffer_size - prtd->app_pointer;
+ memcpy(dstn, src, copy);
+ memcpy(prtd->buffer, runtime->buffer, count - copy);
+ prtd->app_pointer = count - copy;
+ }
+
+ /*
+ * If the stream is started and all the bytes received were
+ * copied to DSP, the newly received bytes should be
+ * sent right away
+ */
+ spin_lock_irqsave(&prtd->lock, flags);
+
+ if (atomic_read(&prtd->start) &&
+ prtd->bytes_received == prtd->copied_total) {
+ prtd->bytes_received += count;
+ msm_compr_send_buffer(prtd);
+ } else
+ prtd->bytes_received += count;
+
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ return 0;
+}
+
+static int msm_compr_playback_copy(struct snd_compr_stream *cstream,
+ char __user *buf, size_t count)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_compr_audio *prtd = runtime->private_data;
+ void *dstn;
+ size_t copy;
+ uint64_t bytes_available = 0;
+ unsigned long flags;
+
+ pr_debug("%s: count = %zd\n", __func__, count);
+ if (!prtd->buffer) {
+ pr_err("%s: Buffer is not allocated yet ??", __func__);
+ return 0;
+ }
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ if (atomic_read(&prtd->error)) {
+ pr_err("%s Got RESET EVENTS notification", __func__);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ return -ENETRESET;
+ }
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ dstn = prtd->buffer + prtd->app_pointer;
+ if (count < prtd->buffer_size - prtd->app_pointer) {
+ if (copy_from_user(dstn, buf, count))
+ return -EFAULT;
+ prtd->app_pointer += count;
+ } else {
+ copy = prtd->buffer_size - prtd->app_pointer;
+ if (copy_from_user(dstn, buf, copy))
+ return -EFAULT;
+ if (copy_from_user(prtd->buffer, buf + copy, count - copy))
+ return -EFAULT;
+ prtd->app_pointer = count - copy;
+ }
+
+ /*
+ * If stream is started and there has been an xrun,
+ * since the available bytes fits fragment_size, copy the data right away
+ */
+ spin_lock_irqsave(&prtd->lock, flags);
+ prtd->bytes_received += count;
+ if (atomic_read(&prtd->start)) {
+ if (atomic_read(&prtd->xrun)) {
+ pr_debug("%s: in xrun, count = %zd\n", __func__, count);
+ bytes_available = prtd->bytes_received - prtd->copied_total;
+ if (bytes_available >= runtime->fragment_size) {
+ pr_debug("%s: handle xrun, bytes_to_write = %llu\n",
+ __func__,
+ bytes_available);
+ atomic_set(&prtd->xrun, 0);
+ msm_compr_send_buffer(prtd);
+ } /* else not sufficient data */
+ } /* writes will continue on the next write_done */
+ }
+
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ return count;
+}
+
+static int msm_compr_capture_copy(struct snd_compr_stream *cstream,
+ char __user *buf, size_t count)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_compr_audio *prtd = runtime->private_data;
+ void *source;
+ unsigned long flags;
+
+ pr_debug("%s: count = %zd\n", __func__, count);
+ if (!prtd->buffer) {
+ pr_err("%s: Buffer is not allocated yet ??", __func__);
+ return 0;
+ }
+
+ spin_lock_irqsave(&prtd->lock, flags);
+ if (atomic_read(&prtd->error)) {
+ pr_err("%s Got RESET EVENTS notification", __func__);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ return -ENETRESET;
+ }
+
+ source = prtd->buffer + prtd->app_pointer;
+ /* check if we have requested amount of data to copy to user*/
+ if (count <= prtd->received_total - prtd->bytes_copied) {
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ if (copy_to_user(buf, source, count)) {
+ pr_err("copy_to_user failed");
+ return -EFAULT;
+ }
+ spin_lock_irqsave(&prtd->lock, flags);
+ prtd->app_pointer += count;
+ if (prtd->app_pointer >= prtd->buffer_size)
+ prtd->app_pointer -= prtd->buffer_size;
+ prtd->bytes_copied += count;
+ }
+ msm_compr_read_buffer(prtd);
+
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ return count;
+}
+
+static int msm_compr_copy(struct snd_compr_stream *cstream,
+ char __user *buf, size_t count)
+{
+ int ret = 0;
+
+ pr_debug(" In %s\n", __func__);
+ if (cstream->direction == SND_COMPRESS_PLAYBACK)
+ ret = msm_compr_playback_copy(cstream, buf, count);
+ else if (cstream->direction == SND_COMPRESS_CAPTURE)
+ ret = msm_compr_capture_copy(cstream, buf, count);
+ return ret;
+}
+
+static int msm_compr_get_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_caps *arg)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_compr_audio *prtd = runtime->private_data;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ if ((arg != NULL) && (prtd != NULL)) {
+ memcpy(arg, &prtd->compr_cap, sizeof(struct snd_compr_caps));
+ } else {
+ ret = -EINVAL;
+ pr_err("%s: arg (0x%pK), prtd (0x%pK)\n", __func__, arg, prtd);
+ }
+
+ return ret;
+}
+
+static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_codec_caps *codec)
+{
+ pr_debug("%s\n", __func__);
+
+ switch (codec->codec) {
+ case SND_AUDIOCODEC_MP3:
+ codec->num_descriptors = 2;
+ codec->descriptor[0].max_ch = 2;
+ memcpy(codec->descriptor[0].sample_rates,
+ supported_sample_rates,
+ sizeof(supported_sample_rates));
+ codec->descriptor[0].num_sample_rates =
+ sizeof(supported_sample_rates)/sizeof(unsigned int);
+ codec->descriptor[0].bit_rate[0] = 320; /* 320kbps */
+ codec->descriptor[0].bit_rate[1] = 128;
+ codec->descriptor[0].num_bitrates = 2;
+ codec->descriptor[0].profiles = 0;
+ codec->descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO;
+ codec->descriptor[0].formats = 0;
+ break;
+ case SND_AUDIOCODEC_AAC:
+ codec->num_descriptors = 2;
+ codec->descriptor[1].max_ch = 2;
+ memcpy(codec->descriptor[1].sample_rates,
+ supported_sample_rates,
+ sizeof(supported_sample_rates));
+ codec->descriptor[1].num_sample_rates =
+ sizeof(supported_sample_rates)/sizeof(unsigned int);
+ codec->descriptor[1].bit_rate[0] = 320; /* 320kbps */
+ codec->descriptor[1].bit_rate[1] = 128;
+ codec->descriptor[1].num_bitrates = 2;
+ codec->descriptor[1].profiles = 0;
+ codec->descriptor[1].modes = 0;
+ codec->descriptor[1].formats =
+ (SND_AUDIOSTREAMFORMAT_MP4ADTS |
+ SND_AUDIOSTREAMFORMAT_RAW);
+ break;
+ case SND_AUDIOCODEC_AC3:
+ case SND_AUDIOCODEC_EAC3:
+ case SND_AUDIOCODEC_FLAC:
+ case SND_AUDIOCODEC_VORBIS:
+ case SND_AUDIOCODEC_ALAC:
+ case SND_AUDIOCODEC_APE:
+ case SND_AUDIOCODEC_DTS:
+ case SND_AUDIOCODEC_DSD:
+ case SND_AUDIOCODEC_TRUEHD:
+ case SND_AUDIOCODEC_IEC61937:
+ case SND_AUDIOCODEC_APTX:
+ case SND_AUDIOCODEC_APTXHD:
+ break;
+ default:
+ pr_err("%s: Unsupported audio codec %d\n",
+ __func__, codec->codec);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_compr_set_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ struct msm_compr_audio *prtd;
+ struct audio_client *ac;
+ pr_debug("%s\n", __func__);
+
+ if (!metadata || !cstream)
+ return -EINVAL;
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd || !prtd->audio_client) {
+ pr_err("%s: prtd or audio client is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (((metadata->key == SNDRV_COMPRESS_ENCODER_PADDING) ||
+ (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY)) &&
+ (prtd->compr_passthr != LEGACY_PCM)) {
+ pr_debug("%s: No trailing silence for compress_type[%d]\n",
+ __func__, prtd->compr_passthr);
+ return 0;
+ }
+
+ ac = prtd->audio_client;
+ if (metadata->key == SNDRV_COMPRESS_ENCODER_PADDING) {
+ pr_debug("%s, got encoder padding %u", __func__, metadata->value[0]);
+ prtd->gapless_state.trailing_samples_drop = metadata->value[0];
+ } else if (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY) {
+ pr_debug("%s, got encoder delay %u", __func__, metadata->value[0]);
+ prtd->gapless_state.initial_samples_drop = metadata->value[0];
+ } else if (metadata->key == SNDRV_COMPRESS_RENDER_MODE) {
+ return msm_compr_set_render_mode(prtd, metadata->value[0]);
+ } else if (metadata->key == SNDRV_COMPRESS_CLK_REC_MODE) {
+ return msm_compr_set_clk_rec_mode(ac, metadata->value[0]);
+ } else if (metadata->key == SNDRV_COMPRESS_RENDER_WINDOW) {
+ return msm_compr_set_render_window(
+ ac,
+ metadata->value[0],
+ metadata->value[1],
+ metadata->value[2],
+ metadata->value[3]);
+ } else if (metadata->key == SNDRV_COMPRESS_START_DELAY) {
+ prtd->start_delay_lsw = metadata->value[0];
+ prtd->start_delay_msw = metadata->value[1];
+ } else if (metadata->key ==
+ SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK) {
+ return msm_compr_enable_adjust_session_clock(ac,
+ metadata->value[0]);
+ } else if (metadata->key == SNDRV_COMPRESS_ADJUST_SESSION_CLOCK) {
+ return msm_compr_adjust_session_clock(ac,
+ metadata->value[0],
+ metadata->value[1]);
+ }
+
+ return 0;
+}
+
+static int msm_compr_get_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ struct msm_compr_audio *prtd;
+ struct audio_client *ac;
+ int ret = -EINVAL;
+
+ pr_debug("%s\n", __func__);
+
+ if (!metadata || !cstream || !cstream->runtime)
+ return ret;
+
+ if (metadata->key != SNDRV_COMPRESS_PATH_DELAY) {
+ pr_err("%s, unsupported key %d\n", __func__, metadata->key);
+ return ret;
+ }
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd || !prtd->audio_client) {
+ pr_err("%s: prtd or audio client is NULL\n", __func__);
+ return ret;
+ }
+
+ ac = prtd->audio_client;
+ ret = q6asm_get_path_delay(prtd->audio_client);
+ if (ret) {
+ pr_err("%s: get_path_delay failed, ret=%d\n", __func__, ret);
+ return ret;
+ }
+
+ pr_debug("%s, path delay(in us) %u\n", __func__, ac->path_delay);
+
+ metadata->value[0] = ac->path_delay;
+
+ return ret;
+}
+
+
+static int msm_compr_set_next_track_param(struct snd_compr_stream *cstream,
+ union snd_codec_options *codec_options)
+{
+ struct msm_compr_audio *prtd;
+ struct audio_client *ac;
+ int ret = 0;
+
+ if (!codec_options || !cstream)
+ return -EINVAL;
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd || !prtd->audio_client) {
+ pr_err("%s: prtd or audio client is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ac = prtd->audio_client;
+
+ pr_debug("%s: got codec options for codec type %u",
+ __func__, prtd->codec);
+ switch (prtd->codec) {
+ case FORMAT_WMA_V9:
+ case FORMAT_WMA_V10PRO:
+ case FORMAT_FLAC:
+ case FORMAT_VORBIS:
+ case FORMAT_ALAC:
+ case FORMAT_APE:
+ memcpy(&(prtd->gapless_state.codec_options),
+ codec_options,
+ sizeof(union snd_codec_options));
+ ret = msm_compr_send_media_format_block(cstream,
+ ac->stream_id, true);
+ if (ret < 0) {
+ pr_err("%s: failed to send media format block\n",
+ __func__);
+ }
+ break;
+
+ default:
+ pr_debug("%s: Ignore sending CMD Format block\n",
+ __func__);
+ break;
+ }
+
+ return ret;
+}
+
+static int msm_compr_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ uint32_t *volume = NULL;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds fe_id %lu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ volume = pdata->volume[fe_id];
+
+ volume[0] = ucontrol->value.integer.value[0];
+ volume[1] = ucontrol->value.integer.value[1];
+ pr_debug("%s: fe_id %lu left_vol %d right_vol %d\n",
+ __func__, fe_id, volume[0], volume[1]);
+ if (cstream)
+ msm_compr_set_volume(cstream, volume[0], volume[1]);
+ return 0;
+}
+
+static int msm_compr_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+
+ struct msm_compr_pdata *pdata =
+ snd_soc_component_get_drvdata(comp);
+ uint32_t *volume = NULL;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ volume = pdata->volume[fe_id];
+ pr_debug("%s: fe_id %lu\n", __func__, fe_id);
+ ucontrol->value.integer.value[0] = volume[0];
+ ucontrol->value.integer.value[1] = volume[1];
+
+ return 0;
+}
+
+static int msm_compr_audio_effects_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct msm_compr_audio_effects *audio_effects = NULL;
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd = NULL;
+ long *values = &(ucontrol->value.integer.value[0]);
+ int effects_module;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds fe_id %lu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+ mutex_lock(&pdata->lock);
+ cstream = pdata->cstream[fe_id];
+ audio_effects = pdata->audio_effects[fe_id];
+ if (!cstream || !audio_effects) {
+ pr_err("%s: stream or effects inactive\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: cannot set audio effects\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (prtd->compr_passthr != LEGACY_PCM) {
+ pr_debug("%s: No effects for compr_type[%d]\n",
+ __func__, prtd->compr_passthr);
+ return 0;
+ } else {
+ pr_debug("%s: Effects supported for compr_type[%d]\n",
+ __func__, prtd->compr_passthr);
+ }
+ effects_module = *values++;
+ switch (effects_module) {
+ case VIRTUALIZER_MODULE:
+ pr_debug("%s: VIRTUALIZER_MODULE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(effects_module,
+ prtd->audio_client->topology))
+ msm_audio_effects_virtualizer_handler(
+ prtd->audio_client,
+ &(audio_effects->virtualizer),
+ values);
+ break;
+ case REVERB_MODULE:
+ pr_debug("%s: REVERB_MODULE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(effects_module,
+ prtd->audio_client->topology))
+ msm_audio_effects_reverb_handler(prtd->audio_client,
+ &(audio_effects->reverb),
+ values);
+ break;
+ case BASS_BOOST_MODULE:
+ pr_debug("%s: BASS_BOOST_MODULE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(effects_module,
+ prtd->audio_client->topology))
+ msm_audio_effects_bass_boost_handler(prtd->audio_client,
+ &(audio_effects->bass_boost),
+ values);
+ break;
+ case PBE_MODULE:
+ pr_debug("%s: PBE_MODULE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(effects_module,
+ prtd->audio_client->topology))
+ msm_audio_effects_pbe_handler(prtd->audio_client,
+ &(audio_effects->pbe),
+ values);
+ break;
+ case EQ_MODULE:
+ pr_debug("%s: EQ_MODULE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(effects_module,
+ prtd->audio_client->topology))
+ msm_audio_effects_popless_eq_handler(prtd->audio_client,
+ &(audio_effects->equalizer),
+ values);
+ break;
+ case SOFT_VOLUME_MODULE:
+ pr_debug("%s: SOFT_VOLUME_MODULE\n", __func__);
+ break;
+ case SOFT_VOLUME2_MODULE:
+ pr_debug("%s: SOFT_VOLUME2_MODULE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(effects_module,
+ prtd->audio_client->topology))
+ msm_audio_effects_volume_handler_v2(prtd->audio_client,
+ &(audio_effects->volume),
+ values, SOFT_VOLUME_INSTANCE_2);
+ break;
+ default:
+ pr_err("%s Invalid effects config module\n", __func__);
+ ret = -EINVAL;
+ }
+done:
+ mutex_unlock(&pdata->lock);
+ return ret;
+}
+
+static int msm_compr_audio_effects_config_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct msm_compr_audio_effects *audio_effects = NULL;
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd = NULL;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds fe_id %lu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+ mutex_lock(&pdata->lock);
+ cstream = pdata->cstream[fe_id];
+ audio_effects = pdata->audio_effects[fe_id];
+ if (!cstream || !audio_effects) {
+ pr_err("%s: stream or effects inactive\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: cannot set audio effects\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ mutex_unlock(&pdata->lock);
+ return ret;
+}
+
+static int msm_compr_query_audio_effect_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct msm_compr_audio_effects *audio_effects = NULL;
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd = NULL;
+ int ret = 0;
+ long *values = &(ucontrol->value.integer.value[0]);
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds fe_id %lu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+ mutex_lock(&pdata->lock);
+ cstream = pdata->cstream[fe_id];
+ audio_effects = pdata->audio_effects[fe_id];
+ if (!cstream || !audio_effects) {
+ pr_err("%s: stream or effects inactive\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: cannot set audio effects\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (prtd->compr_passthr != LEGACY_PCM) {
+ pr_err("%s: No effects for compr_type[%d]\n",
+ __func__, prtd->compr_passthr);
+ ret = -EPERM;
+ goto done;
+ }
+ audio_effects->query.mod_id = (u32)*values++;
+ audio_effects->query.parm_id = (u32)*values++;
+ audio_effects->query.size = (u32)*values++;
+ audio_effects->query.offset = (u32)*values++;
+ audio_effects->query.device = (u32)*values++;
+done:
+ mutex_unlock(&pdata->lock);
+ return ret;
+}
+
+static int msm_compr_query_audio_effect_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct msm_compr_audio_effects *audio_effects = NULL;
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd = NULL;
+ int ret = 0;
+ long *values = &(ucontrol->value.integer.value[0]);
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds fe_id %lu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+ mutex_lock(&pdata->lock);
+ cstream = pdata->cstream[fe_id];
+ audio_effects = pdata->audio_effects[fe_id];
+ if (!cstream || !audio_effects) {
+ pr_debug("%s: stream or effects inactive\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: cannot set audio effects\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ values[0] = (long)audio_effects->query.mod_id;
+ values[1] = (long)audio_effects->query.parm_id;
+ values[2] = (long)audio_effects->query.size;
+ values[3] = (long)audio_effects->query.offset;
+ values[4] = (long)audio_effects->query.device;
+done:
+ mutex_unlock(&pdata->lock);
+ return ret;
+}
+
+static int msm_compr_send_dec_params(struct snd_compr_stream *cstream,
+ struct msm_compr_dec_params *dec_params,
+ int stream_id)
+{
+
+ int rc = 0;
+ struct msm_compr_audio *prtd = NULL;
+ struct snd_dec_ddp *ddp = &dec_params->ddp_params;
+
+ if (!cstream || !dec_params) {
+ pr_err("%s: stream or dec_params inactive\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: cannot set dec_params\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+ switch (prtd->codec) {
+ case FORMAT_MP3:
+ case FORMAT_MPEG4_AAC:
+ case FORMAT_TRUEHD:
+ case FORMAT_IEC61937:
+ case FORMAT_APTX:
+ case FORMAT_APTXHD:
+ pr_debug("%s: no runtime parameters for codec: %d\n", __func__,
+ prtd->codec);
+ break;
+ case FORMAT_AC3:
+ case FORMAT_EAC3:
+ if (prtd->compr_passthr != LEGACY_PCM) {
+ pr_debug("%s: No DDP param for compr_type[%d]\n",
+ __func__, prtd->compr_passthr);
+ break;
+ }
+ rc = msm_compr_send_ddp_cfg(prtd->audio_client, ddp, stream_id);
+ if (rc < 0)
+ pr_err("%s: DDP CMD CFG failed %d\n", __func__, rc);
+ break;
+ default:
+ break;
+ }
+end:
+ return rc;
+
+}
+static int msm_compr_dec_params_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct msm_compr_dec_params *dec_params = NULL;
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd = NULL;
+ long *values = &(ucontrol->value.integer.value[0]);
+ int rc = 0;
+
+ pr_debug("%s\n", __func__);
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds fe_id %lu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ dec_params = pdata->dec_params[fe_id];
+
+ if (!cstream || !dec_params) {
+ pr_err("%s: stream or dec_params inactive\n", __func__);
+ return -EINVAL;
+ }
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: cannot set dec_params\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&pdata->lock);
+ switch (prtd->codec) {
+ case FORMAT_MP3:
+ case FORMAT_MPEG4_AAC:
+ case FORMAT_FLAC:
+ case FORMAT_VORBIS:
+ case FORMAT_ALAC:
+ case FORMAT_APE:
+ case FORMAT_DTS:
+ case FORMAT_DSD:
+ case FORMAT_TRUEHD:
+ case FORMAT_IEC61937:
+ case FORMAT_APTX:
+ case FORMAT_APTXHD:
+ pr_debug("%s: no runtime parameters for codec: %d\n", __func__,
+ prtd->codec);
+ break;
+ case FORMAT_AC3:
+ case FORMAT_EAC3: {
+ struct snd_dec_ddp *ddp = &dec_params->ddp_params;
+ int cnt;
+ if (prtd->compr_passthr != LEGACY_PCM) {
+ pr_debug("%s: No DDP param for compr_type[%d]\n",
+ __func__, prtd->compr_passthr);
+ break;
+ }
+
+ ddp->params_length = (*values++);
+ if (ddp->params_length > DDP_DEC_MAX_NUM_PARAM) {
+ pr_err("%s: invalid num of params:: %d\n", __func__,
+ ddp->params_length);
+ rc = -EINVAL;
+ goto end;
+ }
+ for (cnt = 0; cnt < ddp->params_length; cnt++) {
+ ddp->params_id[cnt] = *values++;
+ ddp->params_value[cnt] = *values++;
+ }
+ prtd = cstream->runtime->private_data;
+ if (prtd && prtd->audio_client)
+ rc = msm_compr_send_dec_params(cstream, dec_params,
+ prtd->audio_client->stream_id);
+ break;
+ }
+ default:
+ break;
+ }
+end:
+ pr_debug("%s: ret %d\n", __func__, rc);
+ mutex_unlock(&pdata->lock);
+ return rc;
+}
+
+static int msm_compr_dec_params_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* dummy function */
+ return 0;
+}
+
+static int msm_compr_playback_app_type_cfg_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = ucontrol->value.integer.value[3];
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ int ret = 0;
+
+ cfg_data.app_type = ucontrol->value.integer.value[0];
+ cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
+ if (ucontrol->value.integer.value[2] != 0)
+ cfg_data.sample_rate = ucontrol->value.integer.value[2];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, &cfg_data);
+ if (ret < 0)
+ pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_compr_playback_app_type_cfg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = 0;
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ &be_id, &cfg_data);
+ if (ret < 0) {
+ pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[0] = cfg_data.app_type;
+ ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
+ ucontrol->value.integer.value[2] = cfg_data.sample_rate;
+ ucontrol->value.integer.value[3] = be_id;
+ pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+done:
+ return ret;
+}
+
+static int msm_compr_capture_app_type_cfg_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = ucontrol->value.integer.value[3];
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ int ret = 0;
+
+ cfg_data.app_type = ucontrol->value.integer.value[0];
+ cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
+ if (ucontrol->value.integer.value[2] != 0)
+ cfg_data.sample_rate = ucontrol->value.integer.value[2];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, &cfg_data);
+ if (ret < 0)
+ pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_compr_capture_app_type_cfg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = 0;
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ &be_id, &cfg_data);
+ if (ret < 0) {
+ pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[0] = cfg_data.app_type;
+ ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
+ ucontrol->value.integer.value[2] = cfg_data.sample_rate;
+ ucontrol->value.integer.value[3] = be_id;
+ pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+done:
+ return ret;
+}
+
+static int msm_compr_channel_map_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ u64 fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ int rc = 0, i;
+
+ pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds fe_id %llu\n",
+ __func__, fe_id);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (pdata->ch_map[fe_id]) {
+ pdata->ch_map[fe_id]->set_ch_map = true;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ pdata->ch_map[fe_id]->channel_map[i] =
+ (char)(ucontrol->value.integer.value[i]);
+ } else {
+ pr_debug("%s: no memory for ch_map, default will be set\n",
+ __func__);
+ }
+end:
+ pr_debug("%s: ret %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_compr_channel_map_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ u64 fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ int rc = 0, i;
+
+ pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s: Received out of bounds fe_id %llu\n",
+ __func__, fe_id);
+ rc = -EINVAL;
+ goto end;
+ }
+ if (pdata->ch_map[fe_id]) {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ ucontrol->value.integer.value[i] =
+ pdata->ch_map[fe_id]->channel_map[i];
+ }
+end:
+ pr_debug("%s: ret %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_compr_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd;
+ int ret = 0;
+ struct msm_adsp_event_data *event_data = NULL;
+ uint64_t actual_payload_len = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ if (cstream == NULL) {
+ pr_err("%s cstream is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: prtd is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client == NULL) {
+ pr_err("%s: audio_client is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data;
+ if ((event_data->event_type < ADSP_STREAM_PP_EVENT) ||
+ (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) {
+ pr_err("%s: invalid event_type=%d",
+ __func__, event_data->event_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ actual_payload_len = sizeof(struct msm_adsp_event_data) +
+ event_data->payload_len;
+ if (actual_payload_len >= U32_MAX) {
+ pr_err("%s payload length 0x%X exceeds limit",
+ __func__, event_data->payload_len);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (event_data->payload_len > sizeof(ucontrol->value.bytes.data)
+ - sizeof(struct msm_adsp_event_data)) {
+ pr_err("%s param length=%d exceeds limit",
+ __func__, event_data->payload_len);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = q6asm_send_stream_cmd(prtd->audio_client, event_data);
+ if (ret < 0)
+ pr_err("%s: failed to send stream event cmd, err = %d\n",
+ __func__, ret);
+done:
+ return ret;
+}
+
+static int msm_compr_playback_dnmix_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int len = 0;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd;
+ struct asm_stream_pan_ctrl_params dnmix_param;
+ int be_id = ucontrol->value.integer.value[len++];
+ int stream_id = 0;
+ /*
+ * Max index for this mixer control includes below
+ * be_id (1)
+ * num_output_channels (1)
+ * num_input_channels (1)
+ * output ch map (max) (8)
+ * input ch map (max) (8)
+ * mix matrix coefficients (max)(64)
+ */
+ int max_index = 0;
+ int max_mixer_ctrl_value_size = 128;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ if (cstream == NULL) {
+ pr_err("%s cstream is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: prtd is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client == NULL) {
+ pr_err("%s: audio_client is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ stream_id = prtd->audio_client->session;
+ if (len >= max_mixer_ctrl_value_size) {
+ ret = -EINVAL;
+ goto done;
+ }
+ dnmix_param.num_output_channels =
+ ucontrol->value.integer.value[len++];
+ if (dnmix_param.num_output_channels >
+ PCM_FORMAT_MAX_NUM_CHANNEL) {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (len >= max_mixer_ctrl_value_size) {
+ ret = -EINVAL;
+ goto done;
+ }
+ dnmix_param.num_input_channels =
+ ucontrol->value.integer.value[len++];
+ if (dnmix_param.num_input_channels >
+ PCM_FORMAT_MAX_NUM_CHANNEL) {
+ ret = -EINVAL;
+ goto done;
+ }
+ max_index = len + dnmix_param.num_output_channels +
+ dnmix_param.num_input_channels +
+ dnmix_param.num_output_channels *
+ dnmix_param.num_input_channels;
+ if (max_index >= max_mixer_ctrl_value_size) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (ucontrol->value.integer.value[len++]) {
+ for (i = 0; i < dnmix_param.num_output_channels; i++) {
+ dnmix_param.output_channel_map[i] =
+ ucontrol->value.integer.value[len++];
+ }
+ }
+ if (ucontrol->value.integer.value[len++]) {
+ for (i = 0; i < dnmix_param.num_input_channels; i++) {
+ dnmix_param.input_channel_map[i] =
+ ucontrol->value.integer.value[len++];
+ }
+ }
+ if (ucontrol->value.integer.value[len++]) {
+ for (i = 0; i < dnmix_param.num_output_channels *
+ dnmix_param.num_input_channels; i++) {
+ dnmix_param.gain[i] =
+ ucontrol->value.integer.value[len++];
+ }
+ }
+ msm_routing_set_downmix_control_data(be_id,
+ stream_id,
+ &dnmix_param);
+
+done:
+ return ret;
+}
+
+static int msm_compr_shm_ion_fd_map_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd;
+ int ret = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ if (cstream == NULL) {
+ pr_err("%s cstream is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: prtd is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client == NULL) {
+ pr_err("%s: audio_client is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(&prtd->shm_ion_fd, ucontrol->value.bytes.data,
+ sizeof(prtd->shm_ion_fd));
+ ret = q6asm_audio_map_shm_fd(prtd->audio_client,
+ &prtd->shm_ion_client,
+ &prtd->shm_ion_handle, prtd->shm_ion_fd);
+ if (ret < 0)
+ pr_err("%s: failed to map shm mem\n", __func__);
+done:
+ return ret;
+}
+
+static int msm_compr_lib_ion_fd_map_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ int ret = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(&pdata->ion_fd[fe_id], ucontrol->value.bytes.data,
+ sizeof(pdata->ion_fd[fe_id]));
+
+done:
+ return ret;
+}
+
+static int msm_compr_rtic_event_ack_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd;
+ int ret = 0;
+ int param_length = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ if (cstream == NULL) {
+ pr_err("%s cstream is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: prtd is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client == NULL) {
+ pr_err("%s: audio_client is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(&param_length, ucontrol->value.bytes.data,
+ sizeof(param_length));
+ if ((param_length + sizeof(param_length))
+ >= sizeof(ucontrol->value.bytes.data)) {
+ pr_err("%s param length=%d exceeds limit",
+ __func__, param_length);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = q6asm_send_rtic_event_ack(prtd->audio_client,
+ ucontrol->value.bytes.data + sizeof(param_length),
+ param_length);
+ if (ret < 0)
+ pr_err("%s: failed to send rtic event ack, err = %d\n",
+ __func__, ret);
+done:
+ return ret;
+}
+
+static int msm_compr_gapless_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ pdata->use_dsp_gapless_mode = ucontrol->value.integer.value[0];
+ pr_debug("%s: value: %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_compr_gapless_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct msm_compr_pdata *pdata =
+ snd_soc_component_get_drvdata(comp);
+ pr_debug("%s:gapless mode %d\n", __func__, pdata->use_dsp_gapless_mode);
+ ucontrol->value.integer.value[0] = pdata->use_dsp_gapless_mode;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new msm_compr_gapless_controls[] = {
+ SOC_SINGLE_EXT("Compress Gapless Playback",
+ 0, 0, 1, 0,
+ msm_compr_gapless_get,
+ msm_compr_gapless_put),
+};
+
+static int msm_compr_probe(struct snd_soc_platform *platform)
+{
+ struct msm_compr_pdata *pdata;
+ int i;
+ int rc;
+ const char *qdsp_version;
+
+ pr_debug("%s\n", __func__);
+ pdata = (struct msm_compr_pdata *)
+ kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ mutex_init(&pdata->lock);
+ snd_soc_platform_set_drvdata(platform, pdata);
+
+ for (i = 0; i < MSM_FRONTEND_DAI_MAX; i++) {
+ pdata->volume[i][0] = COMPRESSED_LR_VOL_MAX_STEPS;
+ pdata->volume[i][1] = COMPRESSED_LR_VOL_MAX_STEPS;
+ pdata->audio_effects[i] = NULL;
+ pdata->dec_params[i] = NULL;
+ pdata->cstream[i] = NULL;
+ pdata->ch_map[i] = NULL;
+ pdata->is_in_use[i] = false;
+ }
+
+ snd_soc_add_platform_controls(platform, msm_compr_gapless_controls,
+ ARRAY_SIZE(msm_compr_gapless_controls));
+
+ rc = of_property_read_string(platform->dev->of_node,
+ "qcom,adsp-version", &qdsp_version);
+ if (!rc) {
+ if (!strcmp(qdsp_version, "MDSP 1.2"))
+ pdata->use_legacy_api = true;
+ else
+ pdata->use_legacy_api = false;
+ } else
+ pdata->use_legacy_api = false;
+
+ pr_debug("%s: use legacy api %d\n", __func__, pdata->use_legacy_api);
+
+ if (of_property_read_bool(platform->dev->of_node,
+ "qcom,avs-version"))
+ pdata->avs_ver = true;
+ else
+ pdata->avs_ver = false;
+
+ pr_debug("%s: avs_ver = %d\n", __func__, pdata->avs_ver);
+
+ /*
+ * use_dsp_gapless_mode part of platform data(pdata) is updated from HAL
+ * through a mixer control before compress driver is opened. The mixer
+ * control is used to decide if dsp gapless mode needs to be enabled.
+ * Gapless is disabled by default.
+ */
+ pdata->use_dsp_gapless_mode = false;
+ return 0;
+}
+
+static int msm_compr_remove(struct snd_soc_platform *platform)
+{
+
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_platform_get_drvdata(platform);
+ if (!pdata) {
+ pr_err("%s pdata is null\n", __func__);
+ return -ENOMEM;
+ }
+
+ mutex_destroy(&pdata->lock);
+ kfree(pdata);
+ return 0;
+}
+static int msm_compr_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = COMPRESSED_LR_VOL_MAX_STEPS;
+ return 0;
+}
+
+static int msm_compr_audio_effects_config_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = MAX_PP_PARAMS_SZ;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
+static int msm_compr_query_audio_effect_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 128;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
+static int msm_compr_dec_params_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 128;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
+static int msm_compr_app_type_cfg_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 5;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
+static int msm_compr_channel_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 8;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
+static int msm_compr_device_downmix_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 128;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
+static int msm_compr_add_volume_control(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Compress Playback";
+ const char *deviceNo = "NN";
+ const char *suffix = "Volume";
+ char *mixer_str = NULL;
+ int ctl_len;
+ struct snd_kcontrol_new fe_volume_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_volume_info,
+ .tlv.p = msm_compr_vol_gain,
+ .get = msm_compr_volume_get,
+ .put = msm_compr_volume_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return 0;
+ }
+ pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 +
+ strlen(suffix) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ pr_err("failed to allocate mixer ctrl str of len %d", ctl_len);
+ return 0;
+ }
+ snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name,
+ rtd->pcm->device, suffix);
+ fe_volume_control[0].name = mixer_str;
+ fe_volume_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s", mixer_str);
+ snd_soc_add_platform_controls(rtd->platform, fe_volume_control,
+ ARRAY_SIZE(fe_volume_control));
+ kfree(mixer_str);
+ return 0;
+}
+
+static int msm_compr_add_audio_effects_control(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Audio Effects Config";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len;
+ struct snd_kcontrol_new fe_audio_effects_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_audio_effects_config_info,
+ .get = msm_compr_audio_effects_config_get,
+ .put = msm_compr_audio_effects_config_put,
+ .private_value = 0,
+ }
+ };
+
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return 0;
+ }
+
+ pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+
+ if (!mixer_str) {
+ pr_err("failed to allocate mixer ctrl str of len %d", ctl_len);
+ return 0;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+
+ fe_audio_effects_config_control[0].name = mixer_str;
+ fe_audio_effects_config_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s\n", mixer_str);
+ snd_soc_add_platform_controls(rtd->platform,
+ fe_audio_effects_config_control,
+ ARRAY_SIZE(fe_audio_effects_config_control));
+ kfree(mixer_str);
+ return 0;
+}
+
+static int msm_compr_add_query_audio_effect_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Query Audio Effect Param";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len;
+ struct snd_kcontrol_new fe_query_audio_effect_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_query_audio_effect_info,
+ .get = msm_compr_query_audio_effect_get,
+ .put = msm_compr_query_audio_effect_put,
+ .private_value = 0,
+ }
+ };
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return 0;
+ }
+ pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ pr_err("failed to allocate mixer ctrl str of len %d", ctl_len);
+ return 0;
+ }
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_query_audio_effect_control[0].name = mixer_str;
+ fe_query_audio_effect_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: registering new mixer ctl %s\n", __func__, mixer_str);
+ snd_soc_add_platform_controls(rtd->platform,
+ fe_query_audio_effect_control,
+ ARRAY_SIZE(fe_query_audio_effect_control));
+ kfree(mixer_str);
+ return 0;
+}
+
+static int msm_compr_add_audio_adsp_stream_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CMD;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_audio_adsp_stream_cmd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_compr_adsp_stream_cmd_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_audio_adsp_stream_cmd_config_control[0].name = mixer_str;
+ fe_audio_adsp_stream_cmd_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_audio_adsp_stream_cmd_config_control,
+ ARRAY_SIZE(fe_audio_adsp_stream_cmd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_compr_add_audio_adsp_stream_callback_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol *kctl;
+
+ struct snd_kcontrol_new fe_audio_adsp_callback_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_callback_info,
+ .get = msm_adsp_stream_callback_get,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s: rtd is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_audio_adsp_callback_config_control[0].name = mixer_str;
+ fe_audio_adsp_callback_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_audio_adsp_callback_config_control,
+ ARRAY_SIZE(fe_audio_adsp_callback_config_control));
+ if (ret < 0) {
+ pr_err("%s: failed to add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl->private_data = NULL;
+
+free_mixer_str:
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_compr_add_dec_runtime_params_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Audio Stream";
+ const char *deviceNo = "NN";
+ const char *suffix = "Dec Params";
+ char *mixer_str = NULL;
+ int ctl_len;
+ struct snd_kcontrol_new fe_dec_params_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_dec_params_info,
+ .get = msm_compr_dec_params_get,
+ .put = msm_compr_dec_params_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return 0;
+ }
+
+ pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 +
+ strlen(suffix) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+
+ if (!mixer_str) {
+ pr_err("failed to allocate mixer ctrl str of len %d", ctl_len);
+ return 0;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name,
+ rtd->pcm->device, suffix);
+
+ fe_dec_params_control[0].name = mixer_str;
+ fe_dec_params_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s", mixer_str);
+ snd_soc_add_platform_controls(rtd->platform,
+ fe_dec_params_control,
+ ARRAY_SIZE(fe_dec_params_control));
+ kfree(mixer_str);
+ return 0;
+}
+
+static int msm_compr_add_device_down_mix_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *playback_mixer_ctl_name = "Audio Device";
+ const char *deviceNo = "NN";
+ const char *suffix = "Downmix Control";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new device_downmix_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_device_downmix_info,
+ .put = msm_compr_playback_dnmix_ctl_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ device_downmix_control[0].name = mixer_str;
+ device_downmix_control[0].private_value = rtd->dai_link->be_id;
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ device_downmix_control,
+ ARRAY_SIZE(device_downmix_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *playback_mixer_ctl_name = "Audio Stream";
+ const char *capture_mixer_ctl_name = "Audio Stream Capture";
+ const char *deviceNo = "NN";
+ const char *suffix = "App Type Cfg";
+ char *mixer_str = NULL;
+ int ctl_len;
+ struct snd_kcontrol_new fe_app_type_cfg_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_app_type_cfg_info,
+ .put = msm_compr_playback_app_type_cfg_put,
+ .get = msm_compr_playback_app_type_cfg_get,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return 0;
+ }
+
+ pr_debug("%s: added new compr FE ctl with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+ if (rtd->compr->direction == SND_COMPRESS_PLAYBACK)
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 + strlen(deviceNo)
+ + 1 + strlen(suffix) + 1;
+ else
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 + strlen(deviceNo)
+ + 1 + strlen(suffix) + 1;
+
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+
+ if (!mixer_str) {
+ pr_err("failed to allocate mixer ctrl str of len %d", ctl_len);
+ return 0;
+ }
+
+ if (rtd->compr->direction == SND_COMPRESS_PLAYBACK)
+ snprintf(mixer_str, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ else
+ snprintf(mixer_str, ctl_len, "%s %d %s",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix);
+
+ fe_app_type_cfg_control[0].name = mixer_str;
+ fe_app_type_cfg_control[0].private_value = rtd->dai_link->be_id;
+
+ if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) {
+ fe_app_type_cfg_control[0].put =
+ msm_compr_playback_app_type_cfg_put;
+ fe_app_type_cfg_control[0].get =
+ msm_compr_playback_app_type_cfg_get;
+ } else {
+ fe_app_type_cfg_control[0].put =
+ msm_compr_capture_app_type_cfg_put;
+ fe_app_type_cfg_control[0].get =
+ msm_compr_capture_app_type_cfg_get;
+ }
+ pr_debug("Registering new mixer ctl %s", mixer_str);
+ snd_soc_add_platform_controls(rtd->platform,
+ fe_app_type_cfg_control,
+ ARRAY_SIZE(fe_app_type_cfg_control));
+ kfree(mixer_str);
+ return 0;
+}
+
+static int msm_compr_add_channel_map_control(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Playback Channel Map";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ struct msm_compr_pdata *pdata = NULL;
+ int ctl_len;
+ struct snd_kcontrol_new fe_channel_map_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_channel_map_info,
+ .get = msm_compr_channel_map_get,
+ .put = msm_compr_channel_map_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s: NULL rtd\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+
+ ctl_len = strlen(mixer_ctl_name) + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+
+ if (!mixer_str) {
+ pr_err("%s: failed to allocate mixer ctrl str of len %d\n",
+ __func__, ctl_len);
+ return -ENOMEM;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s%d", mixer_ctl_name, rtd->pcm->device);
+
+ fe_channel_map_control[0].name = mixer_str;
+ fe_channel_map_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ snd_soc_add_platform_controls(rtd->platform,
+ fe_channel_map_control,
+ ARRAY_SIZE(fe_channel_map_control));
+
+ pdata = snd_soc_platform_get_drvdata(rtd->platform);
+ pdata->ch_map[rtd->dai_link->be_id] =
+ kzalloc(sizeof(struct msm_compr_ch_map), GFP_KERNEL);
+ if (!pdata->ch_map[rtd->dai_link->be_id]) {
+ pr_err("%s: Could not allocate memory for channel map\n",
+ __func__);
+ kfree(mixer_str);
+ return -ENOMEM;
+ }
+ kfree(mixer_str);
+ return 0;
+}
+
+static int msm_compr_add_shm_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Playback ION FD";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_ion_fd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_compr_shm_ion_fd_map_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_ion_fd_config_control[0].name = mixer_str;
+ fe_ion_fd_config_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_ion_fd_config_control,
+ ARRAY_SIZE(fe_ion_fd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_compr_add_lib_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Playback ION LIB FD";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_ion_fd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_compr_lib_ion_fd_map_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_ion_fd_config_control[0].name = mixer_str;
+ fe_ion_fd_config_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_ion_fd_config_control,
+ ARRAY_SIZE(fe_ion_fd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_compr_add_event_ack_cmd_control(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Playback Event Ack";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_event_ack_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_compr_rtic_event_ack_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_event_ack_config_control[0].name = mixer_str;
+ fe_event_ack_config_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_event_ack_config_control,
+ ARRAY_SIZE(fe_event_ack_config_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_compr_new(struct snd_soc_pcm_runtime *rtd)
+{
+ int rc;
+
+ rc = msm_compr_add_volume_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr Volume Control\n", __func__);
+
+ rc = msm_compr_add_audio_effects_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr Audio Effects Control\n",
+ __func__);
+
+ rc = msm_compr_add_audio_adsp_stream_cmd_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr ADSP Stream Cmd Control\n",
+ __func__);
+
+ rc = msm_compr_add_audio_adsp_stream_callback_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr ADSP Stream Callback Control\n",
+ __func__);
+
+ rc = msm_compr_add_shm_ion_fd_cmd_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr ion fd Control\n",
+ __func__);
+
+ rc = msm_compr_add_lib_ion_fd_cmd_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr ion lib fd Control\n",
+ __func__);
+
+ rc = msm_compr_add_event_ack_cmd_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr event ack Control\n",
+ __func__);
+
+ rc = msm_compr_add_device_down_mix_controls(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr downmix Control\n",
+ __func__);
+
+ rc = msm_compr_add_query_audio_effect_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr Query Audio Effect Control\n",
+ __func__);
+
+ rc = msm_compr_add_dec_runtime_params_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr Dec runtime params Control\n",
+ __func__);
+ rc = msm_compr_add_app_type_cfg_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr App Type Cfg Control\n",
+ __func__);
+ rc = msm_compr_add_channel_map_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr Channel Map Control\n",
+ __func__);
+ return 0;
+}
+
+static struct snd_compr_ops msm_compr_ops = {
+ .open = msm_compr_open,
+ .free = msm_compr_free,
+ .trigger = msm_compr_trigger,
+ .pointer = msm_compr_pointer,
+ .set_params = msm_compr_set_params,
+ .set_metadata = msm_compr_set_metadata,
+ .get_metadata = msm_compr_get_metadata,
+ .set_next_track_param = msm_compr_set_next_track_param,
+ .ack = msm_compr_ack,
+ .copy = msm_compr_copy,
+ .get_caps = msm_compr_get_caps,
+ .get_codec_caps = msm_compr_get_codec_caps,
+};
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .probe = msm_compr_probe,
+ .compr_ops = &msm_compr_ops,
+ .pcm_new = msm_compr_new,
+ .remove = msm_compr_remove,
+};
+
+static int msm_compr_dev_probe(struct platform_device *pdev)
+{
+
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_compr_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_compr_dt_match[] = {
+ {.compatible = "qcom,msm-compress-dsp"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_compr_dt_match);
+
+static struct platform_driver msm_compr_driver = {
+ .driver = {
+ .name = "msm-compress-dsp",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_compr_dt_match,
+ },
+ .probe = msm_compr_dev_probe,
+ .remove = msm_compr_dev_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_compr_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_compr_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("Compress Offload platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
new file mode 100644
index 000000000000..6b1c150f1ee4
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -0,0 +1,555 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <sound/pcm_params.h>
+
+#define HDMI_RX_CA_MAX 0x32
+
+enum {
+ STATUS_PORT_STARTED, /* track if AFE port has started */
+ STATUS_MAX
+};
+
+struct msm_ext_disp_ca {
+ bool set_ca;
+ u32 ca;
+};
+
+struct msm_dai_q6_hdmi_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ struct msm_ext_disp_ca ca;
+ union afe_port_config port_config;
+};
+
+static int msm_dai_q6_ext_disp_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+
+ if (!dai_data) {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dai_data->port_config.hdmi_multi_ch.datatype = value;
+ pr_debug("%s: value = %d\n", __func__, value);
+
+ return 0;
+}
+
+static int msm_dai_q6_ext_disp_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+
+ if (!dai_data) {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] =
+ dai_data->port_config.hdmi_multi_ch.datatype;
+ pr_debug("%s: value = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_dai_q6_ext_disp_ca_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+
+ if (!dai_data) {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dai_data->ca.ca = ucontrol->value.integer.value[0];
+ dai_data->ca.set_ca = true;
+ pr_debug("%s: ca = %d\n", __func__, dai_data->ca.ca);
+ return 0;
+}
+
+static int msm_dai_q6_ext_disp_ca_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+
+ if (!dai_data) {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = dai_data->ca.ca;
+ pr_debug("%s: ca = %d\n", __func__, dai_data->ca.ca);
+ return 0;
+}
+
+/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command
+ * 0: linear PCM
+ * 1: non-linear PCM
+ */
+static const char * const hdmi_format[] = {
+ "LPCM",
+ "Compr"
+};
+
+static const struct soc_enum hdmi_config_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, hdmi_format),
+};
+
+static int msm_dai_q6_ext_disp_drift_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(struct afe_param_id_dev_timing_stats);
+
+ return 0;
+}
+
+static int msm_dai_q6_ext_disp_drift_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = -EINVAL;
+ struct afe_param_id_dev_timing_stats timing_stats;
+ struct snd_soc_dai *dai = kcontrol->private_data;
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ pr_err("%s: afe port not started. status_mask = %ld\n",
+ __func__, *dai_data->status_mask);
+ goto done;
+ }
+
+ memset(&timing_stats, 0, sizeof(struct afe_param_id_dev_timing_stats));
+ ret = afe_get_av_dev_drift(&timing_stats, dai->id);
+ if (ret) {
+ pr_err("%s: Error getting AFE Drift for port %d, err=%d\n",
+ __func__, dai->id, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(ucontrol->value.bytes.data, (void *)&timing_stats,
+ sizeof(struct afe_param_id_dev_timing_stats));
+done:
+ return ret;
+}
+
+static const struct snd_kcontrol_new hdmi_config_controls[] = {
+ SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
+ msm_dai_q6_ext_disp_format_get,
+ msm_dai_q6_ext_disp_format_put),
+ SOC_SINGLE_MULTI_EXT("HDMI RX CA", SND_SOC_NOPM, 0,
+ HDMI_RX_CA_MAX, 0, 1,
+ msm_dai_q6_ext_disp_ca_get,
+ msm_dai_q6_ext_disp_ca_put),
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "HDMI DRIFT",
+ .info = msm_dai_q6_ext_disp_drift_info,
+ .get = msm_dai_q6_ext_disp_drift_get,
+ },
+};
+
+static const struct snd_kcontrol_new display_port_config_controls[] = {
+ SOC_ENUM_EXT("Display Port RX Format", hdmi_config_enum[0],
+ msm_dai_q6_ext_disp_format_get,
+ msm_dai_q6_ext_disp_format_put),
+ SOC_SINGLE_MULTI_EXT("Display Port RX CA", SND_SOC_NOPM, 0,
+ HDMI_RX_CA_MAX, 0, 1,
+ msm_dai_q6_ext_disp_ca_get,
+ msm_dai_q6_ext_disp_ca_put),
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "DISPLAY_PORT DRIFT",
+ .info = msm_dai_q6_ext_disp_drift_info,
+ .get = msm_dai_q6_ext_disp_drift_get,
+ },
+};
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.hdmi_multi_ch.reserved = 0;
+ dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version = 1;
+ dai_data->port_config.hdmi_multi_ch.sample_rate = dai_data->rate;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dai_data->port_config.hdmi_multi_ch.bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dai_data->port_config.hdmi_multi_ch.bit_width = 24;
+ break;
+ }
+
+ /*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/
+ switch (dai_data->channels) {
+ case 2:
+ dai_data->port_config.hdmi_multi_ch.channel_allocation = 0;
+ break;
+ case 3:
+ dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x02;
+ break;
+ case 4:
+ dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x06;
+ break;
+ case 5:
+ dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x0A;
+ break;
+ case 6:
+ dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x0B;
+ break;
+ case 7:
+ dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x12;
+ break;
+ case 8:
+ dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x13;
+ break;
+ default:
+ dev_err(dai->dev, "invalid Channels = %u\n",
+ dai_data->channels);
+ return -EINVAL;
+ }
+ dev_dbg(dai->dev, "%s() minor version: %u samplerate: %u bitwidth: %u\n"
+ "num_ch = %u channel_allocation = %u datatype = %d\n", __func__,
+ dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version,
+ dai_data->port_config.hdmi_multi_ch.sample_rate,
+ dai_data->port_config.hdmi_multi_ch.bit_width,
+ dai_data->channels,
+ dai_data->port_config.hdmi_multi_ch.channel_allocation,
+ dai_data->port_config.hdmi_multi_ch.datatype);
+
+ return 0;
+}
+
+
+static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ pr_info("%s: afe port not started. dai_data->status_mask = %ld\n",
+ __func__, *dai_data->status_mask);
+ return;
+ }
+
+ rc = afe_close(dai->id); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+ *dai_data->status_mask);
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ memset(&dai_data->ca, 0, sizeof(dai_data->ca));
+}
+
+
+static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (dai_data->ca.set_ca)
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ dai_data->ca.ca;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_port_start(dai->id, &dai_data->port_config,
+ dai_data->rate);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE port %x\n",
+ dai->id);
+ else
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+
+ return rc;
+}
+
+static inline void msm_dai_q6_hdmi_set_dai_id(struct snd_soc_dai *dai)
+{
+ if (!dai->driver->id) {
+ dev_warn(dai->dev, "DAI driver id is not set\n");
+ return;
+ }
+ dai->id = dai->driver->id;
+ return;
+}
+
+static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data;
+ const struct snd_kcontrol_new *kcontrol;
+ int rc = 0;
+ struct snd_soc_dapm_route intercon;
+ struct snd_soc_dapm_context *dapm;
+
+ if (!dai || !dai->driver) {
+ pr_err("%s: dai or dai->driver is NULL\n", __func__);
+ return -EINVAL;
+ }
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data),
+ GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ msm_dai_q6_hdmi_set_dai_id(dai);
+
+ if (dai->driver->id == HDMI_RX) {
+ kcontrol = &hdmi_config_controls[0];
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+
+ kcontrol = &hdmi_config_controls[1];
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+
+ kcontrol = &hdmi_config_controls[2];
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai));
+ } else if (dai->driver->id == DISPLAY_PORT_RX) {
+ kcontrol = &display_port_config_controls[0];
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+
+ kcontrol = &display_port_config_controls[1];
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+
+ kcontrol = &display_port_config_controls[2];
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai));
+ } else {
+ dev_err(dai->dev, "%s: Invalid id:%d\n",
+ __func__, dai->driver->id);
+ kfree(dai_data);
+ dev_set_drvdata(dai->dev, NULL);
+ return -EINVAL;
+ }
+
+ dapm = snd_soc_component_get_dapm(dai->component);
+ memset(&intercon, 0 , sizeof(intercon));
+ if (!rc) {
+ if (dai->driver->playback.stream_name &&
+ dai->driver->playback.aif_name) {
+ dev_dbg(dai->dev, "%s add route for widget %s",
+ __func__, dai->driver->playback.stream_name);
+ intercon.source = dai->driver->playback.aif_name;
+ intercon.sink = dai->driver->playback.stream_name;
+ dev_dbg(dai->dev, "%s src %s sink %s\n",
+ __func__, intercon.source, intercon.sink);
+ snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ }
+ if (dai->driver->capture.stream_name &&
+ dai->driver->capture.aif_name) {
+ dev_dbg(dai->dev, "%s add route for widget %s",
+ __func__, dai->driver->capture.stream_name);
+ intercon.sink = dai->driver->capture.aif_name;
+ intercon.source = dai->driver->capture.stream_name;
+ dev_dbg(dai->dev, "%s src %s sink %s\n",
+ __func__, intercon.source, intercon.sink);
+ snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ }
+ }
+ return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(dai->id); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+ kfree(dai_data);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = {
+ .prepare = msm_dai_q6_hdmi_prepare,
+ .hw_params = msm_dai_q6_hdmi_hw_params,
+ .shutdown = msm_dai_q6_hdmi_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
+ .playback = {
+ .stream_name = "HDMI Playback",
+ .aif_name = "HDMI",
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_max = 192000,
+ .rate_min = 48000,
+ },
+ .ops = &msm_dai_q6_hdmi_ops,
+ .id = HDMI_RX,
+ .probe = msm_dai_q6_hdmi_dai_probe,
+ .remove = msm_dai_q6_hdmi_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_display_port_rx_dai[] = {
+ {
+ .playback = {
+ .stream_name = "Display Port Playback",
+ .aif_name = "DISPLAY_PORT",
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_max = 192000,
+ .rate_min = 48000,
+ },
+ .ops = &msm_dai_q6_hdmi_ops,
+ .id = DISPLAY_PORT_RX,
+ .probe = msm_dai_q6_hdmi_dai_probe,
+ .remove = msm_dai_q6_hdmi_dai_remove,
+ },
+};
+
+static const struct snd_soc_component_driver msm_dai_hdmi_q6_component = {
+ .name = "msm-dai-q6-hdmi",
+};
+
+/* To do: change to register DAIs as batch */
+static int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev)
+{
+ int rc, id;
+ const char *q6_dev_id = "qcom,msm-dai-q6-dev-id";
+
+ rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: missing %s in dt node\n", __func__, q6_dev_id);
+ return rc;
+ }
+
+ pdev->id = id;
+
+ pr_debug("%s: dev name %s, id:%d\n", __func__,
+ dev_name(&pdev->dev), pdev->id);
+
+ switch (pdev->id) {
+ case HDMI_RX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_hdmi_q6_component,
+ &msm_dai_q6_hdmi_hdmi_rx_dai, 1);
+ break;
+ case DISPLAY_PORT_RX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_hdmi_q6_component,
+ &msm_dai_q6_display_port_rx_dai[0],
+ ARRAY_SIZE(msm_dai_q6_display_port_rx_dai));
+ break;
+ default:
+ dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id);
+ rc = -ENODEV;
+ break;
+ }
+ return rc;
+}
+
+static int msm_dai_q6_hdmi_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_dai_q6_hdmi_dt_match[] = {
+ {.compatible = "qcom,msm-dai-q6-hdmi"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_hdmi_dt_match);
+
+static struct platform_driver msm_dai_q6_hdmi_driver = {
+ .probe = msm_dai_q6_hdmi_dev_probe,
+ .remove = msm_dai_q6_hdmi_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6-hdmi",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_q6_hdmi_dt_match,
+ },
+};
+
+static int __init msm_dai_q6_hdmi_init(void)
+{
+ return platform_driver_register(&msm_dai_q6_hdmi_driver);
+}
+module_init(msm_dai_q6_hdmi_init);
+
+static void __exit msm_dai_q6_hdmi_exit(void)
+{
+ platform_driver_unregister(&msm_dai_q6_hdmi_driver);
+}
+module_exit(msm_dai_q6_hdmi_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP HDMI DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
new file mode 100644
index 000000000000..ec590593e7c4
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -0,0 +1,9794 @@
+/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <linux/clk/msm-clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <sound/pcm_params.h>
+#include <sound/q6core.h>
+#include <soc/qcom/boot_stats.h>
+
+#define MSM_DAI_PRI_AUXPCM_DT_DEV_ID 1
+#define MSM_DAI_SEC_AUXPCM_DT_DEV_ID 2
+#define MSM_DAI_TERT_AUXPCM_DT_DEV_ID 3
+#define MSM_DAI_QUAT_AUXPCM_DT_DEV_ID 4
+
+
+#define spdif_clock_value(rate) (2*rate*32*2)
+#define CHANNEL_STATUS_SIZE 24
+#define CHANNEL_STATUS_MASK_INIT 0x0
+#define CHANNEL_STATUS_MASK 0x4
+#define AFE_API_VERSION_CLOCK_SET 1
+
+#define DAI_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+enum {
+ ENC_FMT_NONE,
+ ENC_FMT_SBC = ASM_MEDIA_FMT_SBC,
+ ENC_FMT_AAC_V2 = ASM_MEDIA_FMT_AAC_V2,
+ ENC_FMT_APTX = ASM_MEDIA_FMT_APTX,
+ ENC_FMT_APTX_HD = ASM_MEDIA_FMT_APTX_HD,
+};
+
+enum {
+ SPKR_1,
+ SPKR_2,
+};
+
+static const struct afe_clk_set lpass_clk_set_default = {
+ AFE_API_VERSION_CLOCK_SET,
+ Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT,
+ Q6AFE_LPASS_OSR_CLK_2_P048_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static const struct afe_clk_cfg lpass_clk_cfg_default = {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_OSR_CLK_2_P048_MHZ,
+ 0,
+ Q6AFE_LPASS_CLK_SRC_INTERNAL,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ Q6AFE_LPASS_MODE_CLK1_VALID,
+ 0,
+};
+enum {
+ STATUS_PORT_STARTED, /* track if AFE port has started */
+ /* track AFE Tx port status for bi-directional transfers */
+ STATUS_TX_PORT,
+ /* track AFE Rx port status for bi-directional transfers */
+ STATUS_RX_PORT,
+ STATUS_MAX
+};
+
+enum {
+ RATE_8KHZ,
+ RATE_16KHZ,
+ RATE_MAX_NUM_OF_AUX_PCM_RATES,
+};
+
+struct msm_dai_q6_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ DECLARE_BITMAP(hwfree_status, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ u32 bitwidth;
+ u32 cal_mode;
+ u32 afe_in_channels;
+ u16 afe_in_bitformat;
+ struct afe_enc_config enc_config;
+ union afe_port_config port_config;
+ u16 vi_feed_mono;
+};
+
+struct msm_dai_q6_spdif_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ u32 bitwidth;
+ struct afe_spdif_port_config spdif_port;
+};
+
+struct msm_dai_q6_mi2s_dai_config {
+ u16 pdata_mi2s_lines;
+ struct msm_dai_q6_dai_data mi2s_dai_data;
+};
+
+struct msm_dai_q6_mi2s_dai_data {
+ struct msm_dai_q6_mi2s_dai_config tx_dai;
+ struct msm_dai_q6_mi2s_dai_config rx_dai;
+};
+
+static DEFINE_MUTEX(group_mi2s_mutex);
+static atomic_t group_mi2s_ref[IDX_GROUP_MI2S_MAX];
+
+/* cache of mi2s group cfg per parent node */
+static struct afe_param_id_group_device_i2s_cfg_v1 group_mi2s_cfg = {
+ AFE_API_VERSION_GROUP_DEVICE_I2S_CONFIG,
+ AFE_GROUP_DEVICE_ID_TERTIARY_MI2S_TX,
+ AFE_PORT_I2S_QUAD01,
+ 48000,
+ {
+ AFE_PORT_ID_INVALID,
+ AFE_PORT_ID_TERTIARY_MI2S_TX_1,
+ AFE_PORT_ID_TERTIARY_MI2S_TX_2,
+ AFE_PORT_ID_INVALID,
+ AFE_PORT_ID_INVALID,
+ AFE_PORT_ID_INVALID,
+ AFE_PORT_ID_INVALID,
+ AFE_PORT_ID_INVALID
+ },
+ 32,
+ 0,
+};
+
+static struct afe_clk_set group_mi2s_bclk_set = {
+ AFE_API_VERSION_CLOCK_SET,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_3_P072_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static struct afe_clk_set group_mi2s_mclk_set = {
+ AFE_API_VERSION_CLOCK_SET,
+ Q6AFE_LPASS_CLK_ID_MCLK_2,
+ Q6AFE_LPASS_OSR_CLK_3_P072_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+struct msm_dai_q6_auxpcm_dai_data {
+ /* BITMAP to track Rx and Tx port usage count */
+ DECLARE_BITMAP(auxpcm_port_status, STATUS_MAX);
+ struct mutex rlock; /* auxpcm dev resource lock */
+ u16 rx_pid; /* AUXPCM RX AFE port ID */
+ u16 tx_pid; /* AUXPCM TX AFE port ID */
+ u16 afe_clk_ver;
+ struct afe_clk_cfg clk_cfg; /* hold LPASS clock configuration */
+ struct afe_clk_set clk_set; /* hold LPASS clock configuration */
+ struct msm_dai_q6_dai_data bdai_data; /* incoporate base DAI data */
+};
+
+struct msm_dai_q6_tdm_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ u32 bitwidth;
+ u32 num_group_ports;
+ struct afe_clk_set clk_set; /* hold LPASS clock config. */
+ union afe_port_group_config group_cfg; /* hold tdm group config */
+ struct afe_tdm_port_config port_cfg; /* hold tdm config */
+};
+
+struct msm_dai_q6_group_mi2s_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ u32 bit_width;
+ u32 num_group_ports;
+ u32 sync_mode;
+ struct afe_clk_set bclk_set;
+ struct afe_clk_set mclk_set;
+ union afe_port_group_mi2s_config group_cfg;
+ struct afe_i2s_port_config port_cfg;
+};
+
+/* MI2S format field for AFE_PORT_CMD_I2S_CONFIG command
+ * 0: linear PCM
+ * 1: non-linear PCM
+ * 2: PCM data in IEC 60968 container
+ * 3: compressed data in IEC 60958 container
+ */
+static const char *const mi2s_format[] = {
+ "LPCM",
+ "Compr",
+ "LPCM-60958",
+ "Compr-60958"
+};
+
+static const char *const mi2s_vi_feed_mono[] = {
+ "Left",
+ "Right",
+};
+
+static const struct soc_enum mi2s_config_enum[] = {
+ SOC_ENUM_SINGLE_EXT(4, mi2s_format),
+ SOC_ENUM_SINGLE_EXT(2, mi2s_vi_feed_mono),
+};
+
+static const char *const sb_format[] = {
+ "UNPACKED",
+ "PACKED_16B",
+ "DSD_DOP",
+};
+
+static const struct soc_enum sb_config_enum[] = {
+ SOC_ENUM_SINGLE_EXT(3, sb_format),
+};
+
+static const char *const tdm_data_format[] = {
+ "LPCM",
+ "Compr",
+ "Gen Compr"
+};
+
+static const char *const tdm_header_type[] = {
+ "Invalid",
+ "Default",
+ "Entertainment",
+};
+
+static const struct soc_enum tdm_config_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_data_format), tdm_data_format),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_header_type), tdm_header_type),
+};
+
+static DEFINE_MUTEX(tdm_mutex);
+
+static atomic_t tdm_group_ref[IDX_GROUP_TDM_MAX];
+
+/* cache of group cfg per parent node */
+static struct afe_param_id_group_device_tdm_cfg tdm_group_cfg = {
+ AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG,
+ AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX,
+ 0,
+ {AFE_PORT_ID_QUATERNARY_TDM_RX,
+ AFE_PORT_ID_QUATERNARY_TDM_RX_1,
+ AFE_PORT_ID_QUATERNARY_TDM_RX_2,
+ AFE_PORT_ID_QUATERNARY_TDM_RX_3,
+ AFE_PORT_ID_QUATERNARY_TDM_RX_4,
+ AFE_PORT_ID_QUATERNARY_TDM_RX_5,
+ AFE_PORT_ID_QUATERNARY_TDM_RX_6,
+ AFE_PORT_ID_QUATERNARY_TDM_RX_7},
+ 8,
+ 48000,
+ 32,
+ 8,
+ 32,
+ 0xFF,
+};
+
+static u32 num_tdm_group_ports;
+
+static struct afe_clk_set tdm_clk_set = {
+ AFE_API_VERSION_CLOCK_SET,
+ Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT,
+ Q6AFE_LPASS_IBIT_CLK_DISABLE,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+int msm_dai_q6_get_group_idx(u16 id)
+{
+ switch (id) {
+ case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ return IDX_GROUP_PRIMARY_TDM_RX;
+ case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ return IDX_GROUP_PRIMARY_TDM_TX;
+ case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ return IDX_GROUP_SECONDARY_TDM_RX;
+ case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ return IDX_GROUP_SECONDARY_TDM_TX;
+ case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ return IDX_GROUP_TERTIARY_TDM_RX;
+ case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ return IDX_GROUP_TERTIARY_TDM_TX;
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ return IDX_GROUP_QUATERNARY_TDM_RX;
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ return IDX_GROUP_QUATERNARY_TDM_TX;
+ default: return -EINVAL;
+ }
+}
+
+int msm_dai_q6_get_port_idx(u16 id)
+{
+ switch (id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ return IDX_PRIMARY_TDM_RX_0;
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ return IDX_PRIMARY_TDM_TX_0;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ return IDX_PRIMARY_TDM_RX_1;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ return IDX_PRIMARY_TDM_TX_1;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ return IDX_PRIMARY_TDM_RX_2;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ return IDX_PRIMARY_TDM_TX_2;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ return IDX_PRIMARY_TDM_RX_3;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ return IDX_PRIMARY_TDM_TX_3;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ return IDX_PRIMARY_TDM_RX_4;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ return IDX_PRIMARY_TDM_TX_4;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ return IDX_PRIMARY_TDM_RX_5;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ return IDX_PRIMARY_TDM_TX_5;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ return IDX_PRIMARY_TDM_RX_6;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ return IDX_PRIMARY_TDM_TX_6;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ return IDX_PRIMARY_TDM_RX_7;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ return IDX_PRIMARY_TDM_TX_7;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ return IDX_SECONDARY_TDM_RX_0;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ return IDX_SECONDARY_TDM_TX_0;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ return IDX_SECONDARY_TDM_RX_1;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ return IDX_SECONDARY_TDM_TX_1;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ return IDX_SECONDARY_TDM_RX_2;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ return IDX_SECONDARY_TDM_TX_2;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ return IDX_SECONDARY_TDM_RX_3;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ return IDX_SECONDARY_TDM_TX_3;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ return IDX_SECONDARY_TDM_RX_4;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ return IDX_SECONDARY_TDM_TX_4;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ return IDX_SECONDARY_TDM_RX_5;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ return IDX_SECONDARY_TDM_TX_5;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ return IDX_SECONDARY_TDM_RX_6;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ return IDX_SECONDARY_TDM_TX_6;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ return IDX_SECONDARY_TDM_RX_7;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ return IDX_SECONDARY_TDM_TX_7;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ return IDX_TERTIARY_TDM_RX_0;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ return IDX_TERTIARY_TDM_TX_0;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ return IDX_TERTIARY_TDM_RX_1;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ return IDX_TERTIARY_TDM_TX_1;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ return IDX_TERTIARY_TDM_RX_2;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ return IDX_TERTIARY_TDM_TX_2;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ return IDX_TERTIARY_TDM_RX_3;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ return IDX_TERTIARY_TDM_TX_3;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ return IDX_TERTIARY_TDM_RX_4;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ return IDX_TERTIARY_TDM_TX_4;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ return IDX_TERTIARY_TDM_RX_5;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ return IDX_TERTIARY_TDM_TX_5;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ return IDX_TERTIARY_TDM_RX_6;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ return IDX_TERTIARY_TDM_TX_6;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ return IDX_TERTIARY_TDM_RX_7;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ return IDX_TERTIARY_TDM_TX_7;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ return IDX_QUATERNARY_TDM_RX_0;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ return IDX_QUATERNARY_TDM_TX_0;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ return IDX_QUATERNARY_TDM_RX_1;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ return IDX_QUATERNARY_TDM_TX_1;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ return IDX_QUATERNARY_TDM_RX_2;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ return IDX_QUATERNARY_TDM_TX_2;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ return IDX_QUATERNARY_TDM_RX_3;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ return IDX_QUATERNARY_TDM_TX_3;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ return IDX_QUATERNARY_TDM_RX_4;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ return IDX_QUATERNARY_TDM_TX_4;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ return IDX_QUATERNARY_TDM_RX_5;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ return IDX_QUATERNARY_TDM_TX_5;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ return IDX_QUATERNARY_TDM_RX_6;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ return IDX_QUATERNARY_TDM_TX_6;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ return IDX_QUATERNARY_TDM_RX_7;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ return IDX_QUATERNARY_TDM_TX_7;
+ default: return -EINVAL;
+ }
+}
+
+static int msm_dai_q6_get_mi2s_group_idx(u16 id)
+{
+ switch (id) {
+ case AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ return IDX_GROUP_SECONDARY_MI2S_RX;
+ case AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_TX:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ return IDX_GROUP_SECONDARY_MI2S_TX;
+ case AFE_GROUP_DEVICE_ID_TERTIARY_MI2S_RX:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ return IDX_GROUP_TERTIARY_MI2S_RX;
+ case AFE_GROUP_DEVICE_ID_TERTIARY_MI2S_TX:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ return IDX_GROUP_TERTIARY_MI2S_TX;
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ return IDX_GROUP_QUATERNARY_MI2S_RX;
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_MI2S_TX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ return IDX_GROUP_QUATERNARY_MI2S_TX;
+ default: return -EINVAL;
+ }
+}
+
+static int msm_dai_q6_get_mi2s_port_idx(u16 id)
+{
+ switch (id) {
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ return IDX_SECONDARY_MI2S_RX_1;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ return IDX_SECONDARY_MI2S_RX_2;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ return IDX_SECONDARY_MI2S_RX_3;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ return IDX_SECONDARY_MI2S_RX_4;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ return IDX_SECONDARY_MI2S_TX_1;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ return IDX_SECONDARY_MI2S_TX_2;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ return IDX_SECONDARY_MI2S_TX_3;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ return IDX_SECONDARY_MI2S_TX_4;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ return IDX_TERTIARY_MI2S_RX_1;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ return IDX_TERTIARY_MI2S_RX_2;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ return IDX_TERTIARY_MI2S_RX_3;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ return IDX_TERTIARY_MI2S_RX_4;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ return IDX_TERTIARY_MI2S_TX_1;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ return IDX_TERTIARY_MI2S_TX_2;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ return IDX_TERTIARY_MI2S_TX_3;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ return IDX_TERTIARY_MI2S_TX_4;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ return IDX_QUATERNARY_MI2S_RX_1;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ return IDX_QUATERNARY_MI2S_RX_2;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ return IDX_QUATERNARY_MI2S_RX_3;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ return IDX_QUATERNARY_MI2S_RX_4;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ return IDX_QUATERNARY_MI2S_TX_1;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ return IDX_QUATERNARY_MI2S_TX_2;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ return IDX_QUATERNARY_MI2S_TX_3;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ return IDX_QUATERNARY_MI2S_TX_4;
+ default: return -EINVAL;
+ }
+}
+
+static u16 msm_dai_q6_max_num_slot(int frame_rate)
+{
+ /* Max num of slots is bits per frame divided
+ * by bits per sample which is 16
+ */
+ switch (frame_rate) {
+ case AFE_PORT_PCM_BITS_PER_FRAME_8:
+ return 0;
+ case AFE_PORT_PCM_BITS_PER_FRAME_16:
+ return 1;
+ case AFE_PORT_PCM_BITS_PER_FRAME_32:
+ return 2;
+ case AFE_PORT_PCM_BITS_PER_FRAME_64:
+ return 4;
+ case AFE_PORT_PCM_BITS_PER_FRAME_128:
+ return 8;
+ case AFE_PORT_PCM_BITS_PER_FRAME_256:
+ return 16;
+ default:
+ pr_err("%s Invalid bits per frame %d\n",
+ __func__, frame_rate);
+ return 0;
+ }
+}
+
+static int msm_dai_q6_dai_add_route(struct snd_soc_dai *dai)
+{
+ struct snd_soc_dapm_route intercon;
+ struct snd_soc_dapm_context *dapm;
+
+ if (!dai) {
+ pr_err("%s: Invalid params dai\n", __func__);
+ return -EINVAL;
+ }
+ if (!dai->driver) {
+ pr_err("%s: Invalid params dai driver\n", __func__);
+ return -EINVAL;
+ }
+ dapm = snd_soc_component_get_dapm(dai->component);
+ memset(&intercon, 0 , sizeof(intercon));
+ if (dai->driver->playback.stream_name &&
+ dai->driver->playback.aif_name) {
+ dev_dbg(dai->dev, "%s: add route for widget %s",
+ __func__, dai->driver->playback.stream_name);
+ intercon.source = dai->driver->playback.aif_name;
+ intercon.sink = dai->driver->playback.stream_name;
+ dev_dbg(dai->dev, "%s: src %s sink %s\n",
+ __func__, intercon.source, intercon.sink);
+ snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ }
+ if (dai->driver->capture.stream_name &&
+ dai->driver->capture.aif_name) {
+ dev_dbg(dai->dev, "%s: add route for widget %s",
+ __func__, dai->driver->capture.stream_name);
+ intercon.sink = dai->driver->capture.aif_name;
+ intercon.source = dai->driver->capture.stream_name;
+ dev_dbg(dai->dev, "%s: src %s sink %s\n",
+ __func__, intercon.source, intercon.sink);
+ snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ }
+ return 0;
+}
+
+static int msm_dai_q6_auxpcm_hw_params(
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_auxpcm_dai_data *aux_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_dai_data *dai_data = &aux_dai_data->bdai_data;
+ struct msm_dai_auxpcm_pdata *auxpcm_pdata =
+ (struct msm_dai_auxpcm_pdata *) dai->dev->platform_data;
+ int rc = 0, slot_mapping_copy_len = 0;
+
+ if (params_channels(params) != 1 || (params_rate(params) != 8000 &&
+ params_rate(params) != 16000)) {
+ dev_err(dai->dev, "%s: invalid param chan %d rate %d\n",
+ __func__, params_channels(params), params_rate(params));
+ return -EINVAL;
+ }
+
+ mutex_lock(&aux_dai_data->rlock);
+
+ if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+ test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+ /* AUXPCM DAI in use */
+ if (dai_data->rate != params_rate(params)) {
+ dev_err(dai->dev, "%s: rate mismatch of running DAI\n",
+ __func__);
+ rc = -EINVAL;
+ }
+ mutex_unlock(&aux_dai_data->rlock);
+ return rc;
+ }
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ if (dai_data->rate == 8000) {
+ dai_data->port_config.pcm.pcm_cfg_minor_version =
+ AFE_API_VERSION_PCM_CONFIG;
+ dai_data->port_config.pcm.aux_mode = auxpcm_pdata->mode_8k.mode;
+ dai_data->port_config.pcm.sync_src = auxpcm_pdata->mode_8k.sync;
+ dai_data->port_config.pcm.frame_setting =
+ auxpcm_pdata->mode_8k.frame;
+ dai_data->port_config.pcm.quantype =
+ auxpcm_pdata->mode_8k.quant;
+ dai_data->port_config.pcm.ctrl_data_out_enable =
+ auxpcm_pdata->mode_8k.data;
+ dai_data->port_config.pcm.sample_rate = dai_data->rate;
+ dai_data->port_config.pcm.num_channels = dai_data->channels;
+ dai_data->port_config.pcm.bit_width = 16;
+ if (ARRAY_SIZE(dai_data->port_config.pcm.slot_number_mapping) <=
+ auxpcm_pdata->mode_8k.num_slots)
+ slot_mapping_copy_len =
+ ARRAY_SIZE(
+ dai_data->port_config.pcm.slot_number_mapping)
+ * sizeof(uint16_t);
+ else
+ slot_mapping_copy_len = auxpcm_pdata->mode_8k.num_slots
+ * sizeof(uint16_t);
+
+ if (auxpcm_pdata->mode_8k.slot_mapping) {
+ memcpy(dai_data->port_config.pcm.slot_number_mapping,
+ auxpcm_pdata->mode_8k.slot_mapping,
+ slot_mapping_copy_len);
+ } else {
+ dev_err(dai->dev, "%s 8khz slot mapping is NULL\n",
+ __func__);
+ mutex_unlock(&aux_dai_data->rlock);
+ return -EINVAL;
+ }
+ } else {
+ dai_data->port_config.pcm.pcm_cfg_minor_version =
+ AFE_API_VERSION_PCM_CONFIG;
+ dai_data->port_config.pcm.aux_mode =
+ auxpcm_pdata->mode_16k.mode;
+ dai_data->port_config.pcm.sync_src =
+ auxpcm_pdata->mode_16k.sync;
+ dai_data->port_config.pcm.frame_setting =
+ auxpcm_pdata->mode_16k.frame;
+ dai_data->port_config.pcm.quantype =
+ auxpcm_pdata->mode_16k.quant;
+ dai_data->port_config.pcm.ctrl_data_out_enable =
+ auxpcm_pdata->mode_16k.data;
+ dai_data->port_config.pcm.sample_rate = dai_data->rate;
+ dai_data->port_config.pcm.num_channels = dai_data->channels;
+ dai_data->port_config.pcm.bit_width = 16;
+ if (ARRAY_SIZE(dai_data->port_config.pcm.slot_number_mapping) <=
+ auxpcm_pdata->mode_16k.num_slots)
+ slot_mapping_copy_len =
+ ARRAY_SIZE(
+ dai_data->port_config.pcm.slot_number_mapping)
+ * sizeof(uint16_t);
+ else
+ slot_mapping_copy_len = auxpcm_pdata->mode_16k.num_slots
+ * sizeof(uint16_t);
+
+ if (auxpcm_pdata->mode_16k.slot_mapping) {
+ memcpy(dai_data->port_config.pcm.slot_number_mapping,
+ auxpcm_pdata->mode_16k.slot_mapping,
+ slot_mapping_copy_len);
+ } else {
+ dev_err(dai->dev, "%s 16khz slot mapping is NULL\n",
+ __func__);
+ mutex_unlock(&aux_dai_data->rlock);
+ return -EINVAL;
+ }
+ }
+
+ dev_dbg(dai->dev, "%s: aux_mode 0x%x sync_src 0x%x frame_setting 0x%x\n",
+ __func__, dai_data->port_config.pcm.aux_mode,
+ dai_data->port_config.pcm.sync_src,
+ dai_data->port_config.pcm.frame_setting);
+ dev_dbg(dai->dev, "%s: qtype 0x%x dout 0x%x num_map[0] 0x%x\n"
+ "num_map[1] 0x%x num_map[2] 0x%x num_map[3] 0x%x\n",
+ __func__, dai_data->port_config.pcm.quantype,
+ dai_data->port_config.pcm.ctrl_data_out_enable,
+ dai_data->port_config.pcm.slot_number_mapping[0],
+ dai_data->port_config.pcm.slot_number_mapping[1],
+ dai_data->port_config.pcm.slot_number_mapping[2],
+ dai_data->port_config.pcm.slot_number_mapping[3]);
+
+ mutex_unlock(&aux_dai_data->rlock);
+ return rc;
+}
+
+static int msm_dai_q6_auxpcm_set_clk(
+ struct msm_dai_q6_auxpcm_dai_data *aux_dai_data,
+ u16 port_id, bool enable)
+{
+ int rc;
+
+ pr_debug("%s: afe_clk_ver: %d, port_id: %d, enable: %d\n", __func__,
+ aux_dai_data->afe_clk_ver, port_id, enable);
+ if (aux_dai_data->afe_clk_ver == AFE_CLK_VERSION_V2) {
+ aux_dai_data->clk_set.enable = enable;
+ rc = afe_set_lpass_clock_v2(port_id,
+ &aux_dai_data->clk_set);
+ } else {
+ if (!enable)
+ aux_dai_data->clk_cfg.clk_val1 = 0;
+ rc = afe_set_lpass_clock(port_id,
+ &aux_dai_data->clk_cfg);
+ }
+ return rc;
+}
+
+static void msm_dai_q6_auxpcm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int rc = 0;
+ struct msm_dai_q6_auxpcm_dai_data *aux_dai_data =
+ dev_get_drvdata(dai->dev);
+
+ mutex_lock(&aux_dai_data->rlock);
+
+ if (!(test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+ test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status))) {
+ dev_dbg(dai->dev, "%s(): dai->id %d PCM ports already closed\n",
+ __func__, dai->id);
+ goto exit;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status))
+ clear_bit(STATUS_TX_PORT,
+ aux_dai_data->auxpcm_port_status);
+ else {
+ dev_dbg(dai->dev, "%s: PCM_TX port already closed\n",
+ __func__);
+ goto exit;
+ }
+ } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status))
+ clear_bit(STATUS_RX_PORT,
+ aux_dai_data->auxpcm_port_status);
+ else {
+ dev_dbg(dai->dev, "%s: PCM_RX port already closed\n",
+ __func__);
+ goto exit;
+ }
+ }
+ if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+ test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+ dev_dbg(dai->dev, "%s: cannot shutdown PCM ports\n",
+ __func__);
+ goto exit;
+ }
+
+ dev_dbg(dai->dev, "%s: dai->id = %d closing PCM AFE ports\n",
+ __func__, dai->id);
+
+ rc = afe_close(aux_dai_data->rx_pid); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close PCM_RX AFE port\n");
+
+ rc = afe_close(aux_dai_data->tx_pid);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AUX PCM TX port\n");
+
+ msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->rx_pid, false);
+ msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->tx_pid, false);
+exit:
+ mutex_unlock(&aux_dai_data->rlock);
+ return;
+}
+
+static int msm_dai_q6_auxpcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_auxpcm_dai_data *aux_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_dai_data *dai_data = &aux_dai_data->bdai_data;
+ struct msm_dai_auxpcm_pdata *auxpcm_pdata = NULL;
+ int rc = 0;
+ u32 pcm_clk_rate;
+
+ auxpcm_pdata = dai->dev->platform_data;
+ mutex_lock(&aux_dai_data->rlock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (test_bit(STATUS_TX_PORT,
+ aux_dai_data->auxpcm_port_status)) {
+ dev_dbg(dai->dev, "%s: PCM_TX port already ON\n",
+ __func__);
+ goto exit;
+ } else
+ set_bit(STATUS_TX_PORT,
+ aux_dai_data->auxpcm_port_status);
+ } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (test_bit(STATUS_RX_PORT,
+ aux_dai_data->auxpcm_port_status)) {
+ dev_dbg(dai->dev, "%s: PCM_RX port already ON\n",
+ __func__);
+ goto exit;
+ } else
+ set_bit(STATUS_RX_PORT,
+ aux_dai_data->auxpcm_port_status);
+ }
+ if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) &&
+ test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+ dev_dbg(dai->dev, "%s: PCM ports already set\n", __func__);
+ goto exit;
+ }
+
+ dev_dbg(dai->dev, "%s: dai->id:%d opening afe ports\n",
+ __func__, dai->id);
+
+ rc = afe_q6_interface_prepare();
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "fail to open AFE APR\n");
+ goto fail;
+ }
+
+ /*
+ * For AUX PCM Interface the below sequence of clk
+ * settings and afe_open is a strict requirement.
+ *
+ * Also using afe_open instead of afe_port_start_nowait
+ * to make sure the port is open before deasserting the
+ * clock line. This is required because pcm register is
+ * not written before clock deassert. Hence the hw does
+ * not get updated with new setting if the below clock
+ * assert/deasset and afe_open sequence is not followed.
+ */
+
+ if (dai_data->rate == 8000) {
+ pcm_clk_rate = auxpcm_pdata->mode_8k.pcm_clk_rate;
+ } else if (dai_data->rate == 16000) {
+ pcm_clk_rate = (auxpcm_pdata->mode_16k.pcm_clk_rate);
+ } else {
+ dev_err(dai->dev, "%s: Invalid AUX PCM rate %d\n", __func__,
+ dai_data->rate);
+ rc = -EINVAL;
+ goto fail;
+ }
+ if (aux_dai_data->afe_clk_ver == AFE_CLK_VERSION_V2) {
+ memcpy(&aux_dai_data->clk_set, &lpass_clk_set_default,
+ sizeof(struct afe_clk_set));
+ aux_dai_data->clk_set.clk_freq_in_hz = pcm_clk_rate;
+
+ switch (dai->id) {
+ case MSM_DAI_PRI_AUXPCM_DT_DEV_ID:
+ if (pcm_clk_rate)
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT;
+ else
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_PRI_PCM_EBIT;
+ break;
+ case MSM_DAI_SEC_AUXPCM_DT_DEV_ID:
+ if (pcm_clk_rate)
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT;
+ else
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_SEC_PCM_EBIT;
+ break;
+ case MSM_DAI_TERT_AUXPCM_DT_DEV_ID:
+ if (pcm_clk_rate)
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT;
+ else
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_TER_PCM_EBIT;
+ break;
+ case MSM_DAI_QUAT_AUXPCM_DT_DEV_ID:
+ if (pcm_clk_rate)
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT;
+ else
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT;
+ break;
+ default:
+ dev_err(dai->dev, "%s: AUXPCM id: %d not supported\n",
+ __func__, dai->id);
+ break;
+ }
+ } else {
+ memcpy(&aux_dai_data->clk_cfg, &lpass_clk_cfg_default,
+ sizeof(struct afe_clk_cfg));
+ aux_dai_data->clk_cfg.clk_val1 = pcm_clk_rate;
+ }
+
+ rc = msm_dai_q6_auxpcm_set_clk(aux_dai_data,
+ aux_dai_data->rx_pid, true);
+ if (rc < 0) {
+ dev_err(dai->dev,
+ "%s:afe_set_lpass_clock on RX pcm_src_clk failed\n",
+ __func__);
+ goto fail;
+ }
+
+ rc = msm_dai_q6_auxpcm_set_clk(aux_dai_data,
+ aux_dai_data->tx_pid, true);
+ if (rc < 0) {
+ dev_err(dai->dev,
+ "%s:afe_set_lpass_clock on TX pcm_src_clk failed\n",
+ __func__);
+ goto fail;
+ }
+
+ afe_open(aux_dai_data->rx_pid, &dai_data->port_config, dai_data->rate);
+ afe_open(aux_dai_data->tx_pid, &dai_data->port_config, dai_data->rate);
+ goto exit;
+
+fail:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ clear_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status);
+ else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ clear_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status);
+
+exit:
+ mutex_unlock(&aux_dai_data->rlock);
+ return rc;
+}
+
+static int msm_dai_q6_auxpcm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ int rc = 0;
+
+ pr_debug("%s:port:%d cmd:%d\n",
+ __func__, dai->id, cmd);
+
+ switch (cmd) {
+
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /* afe_open will be called from prepare */
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ return 0;
+
+ default:
+ pr_err("%s: cmd %d\n", __func__, cmd);
+ rc = -EINVAL;
+ }
+
+ return rc;
+
+}
+
+static int msm_dai_q6_dai_auxpcm_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_auxpcm_dai_data *aux_dai_data;
+ int rc;
+
+ aux_dai_data = dev_get_drvdata(dai->dev);
+
+ dev_dbg(dai->dev, "%s: dai->id %d closing afe\n",
+ __func__, dai->id);
+
+ if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+ test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+ rc = afe_close(aux_dai_data->rx_pid); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AUXPCM RX AFE port\n");
+ rc = afe_close(aux_dai_data->tx_pid);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AUXPCM TX AFE port\n");
+ clear_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status);
+ clear_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status);
+ }
+ msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->rx_pid, false);
+ msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->tx_pid, false);
+ return 0;
+}
+
+static int msm_dai_q6_aux_pcm_probe(struct snd_soc_dai *dai)
+{
+ int rc = 0;
+
+ if (!dai) {
+ pr_err("%s: Invalid params dai\n", __func__);
+ return -EINVAL;
+ }
+ if (!dai->dev) {
+ pr_err("%s: Invalid params dai dev\n", __func__);
+ return -EINVAL;
+ }
+ if (!dai->driver->id) {
+ dev_warn(dai->dev, "DAI driver id is not set\n");
+ return -EINVAL;
+ }
+ dai->id = dai->driver->id;
+ rc = msm_dai_q6_dai_add_route(dai);
+ return rc;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_auxpcm_ops = {
+ .prepare = msm_dai_q6_auxpcm_prepare,
+ .trigger = msm_dai_q6_auxpcm_trigger,
+ .hw_params = msm_dai_q6_auxpcm_hw_params,
+ .shutdown = msm_dai_q6_auxpcm_shutdown,
+};
+
+static const struct snd_soc_component_driver
+ msm_dai_q6_aux_pcm_dai_component = {
+ .name = "msm-auxpcm-dev",
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_dai[] = {
+ {
+ .playback = {
+ .stream_name = "AUX PCM Playback",
+ .aif_name = "AUX_PCM_RX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .capture = {
+ .stream_name = "AUX PCM Capture",
+ .aif_name = "AUX_PCM_TX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .id = MSM_DAI_PRI_AUXPCM_DT_DEV_ID,
+ .ops = &msm_dai_q6_auxpcm_ops,
+ .probe = msm_dai_q6_aux_pcm_probe,
+ .remove = msm_dai_q6_dai_auxpcm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Sec AUX PCM Playback",
+ .aif_name = "SEC_AUX_PCM_RX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .capture = {
+ .stream_name = "Sec AUX PCM Capture",
+ .aif_name = "SEC_AUX_PCM_TX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .id = MSM_DAI_SEC_AUXPCM_DT_DEV_ID,
+ .ops = &msm_dai_q6_auxpcm_ops,
+ .probe = msm_dai_q6_aux_pcm_probe,
+ .remove = msm_dai_q6_dai_auxpcm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tert AUX PCM Playback",
+ .aif_name = "TERT_AUX_PCM_RX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .capture = {
+ .stream_name = "Tert AUX PCM Capture",
+ .aif_name = "TERT_AUX_PCM_TX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .id = MSM_DAI_TERT_AUXPCM_DT_DEV_ID,
+ .ops = &msm_dai_q6_auxpcm_ops,
+ .probe = msm_dai_q6_aux_pcm_probe,
+ .remove = msm_dai_q6_dai_auxpcm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quat AUX PCM Playback",
+ .aif_name = "QUAT_AUX_PCM_RX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .capture = {
+ .stream_name = "Quat AUX PCM Capture",
+ .aif_name = "QUAT_AUX_PCM_TX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .id = MSM_DAI_QUAT_AUXPCM_DT_DEV_ID,
+ .ops = &msm_dai_q6_auxpcm_ops,
+ .probe = msm_dai_q6_aux_pcm_probe,
+ .remove = msm_dai_q6_dai_auxpcm_remove,
+ },
+};
+
+static int msm_dai_q6_spdif_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+ dai_data->spdif_port.cfg.data_format = value;
+ pr_debug("%s: value = %d\n", __func__, value);
+ return 0;
+}
+
+static int msm_dai_q6_spdif_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data;
+ ucontrol->value.integer.value[0] =
+ dai_data->spdif_port.cfg.data_format;
+ return 0;
+}
+
+static const char * const spdif_format[] = {
+ "LPCM",
+ "Compr"
+};
+
+static const struct soc_enum spdif_config_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, spdif_format),
+};
+
+static int msm_dai_q6_spdif_chstatus_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data;
+ int ret = 0;
+
+ dai_data->spdif_port.ch_status.status_type =
+ AFE_API_VERSION_SPDIF_CH_STATUS_CONFIG;
+ memset(dai_data->spdif_port.ch_status.status_mask,
+ CHANNEL_STATUS_MASK_INIT, CHANNEL_STATUS_SIZE);
+ dai_data->spdif_port.ch_status.status_mask[0] =
+ CHANNEL_STATUS_MASK;
+
+ memcpy(dai_data->spdif_port.ch_status.status_bits,
+ ucontrol->value.iec958.status, CHANNEL_STATUS_SIZE);
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ pr_debug("%s: Port already started. Dynamic update\n",
+ __func__);
+ ret = afe_send_spdif_ch_status_cfg(
+ &dai_data->spdif_port.ch_status,
+ AFE_PORT_ID_SPDIF_RX);
+ }
+ return ret;
+}
+
+static int msm_dai_q6_spdif_chstatus_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data;
+ memcpy(ucontrol->value.iec958.status,
+ dai_data->spdif_port.ch_status.status_bits,
+ CHANNEL_STATUS_SIZE);
+ return 0;
+}
+
+static int msm_dai_q6_spdif_chstatus_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static const struct snd_kcontrol_new spdif_config_controls[] = {
+ {
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE),
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
+ .info = msm_dai_q6_spdif_chstatus_info,
+ .get = msm_dai_q6_spdif_chstatus_get,
+ .put = msm_dai_q6_spdif_chstatus_put,
+ },
+ SOC_ENUM_EXT("SPDIF RX Format", spdif_config_enum[0],
+ msm_dai_q6_spdif_format_get,
+ msm_dai_q6_spdif_format_put)
+};
+
+
+static int msm_dai_q6_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai->id = AFE_PORT_ID_SPDIF_RX;
+ dai_data->channels = params_channels(params);
+ dai_data->spdif_port.cfg.num_channels = dai_data->channels;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dai_data->spdif_port.cfg.bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ dai_data->spdif_port.cfg.bit_width = 24;
+ break;
+ default:
+ pr_err("%s: format %d\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ dai_data->rate = params_rate(params);
+ dai_data->bitwidth = dai_data->spdif_port.cfg.bit_width;
+ dai_data->spdif_port.cfg.sample_rate = dai_data->rate;
+ dai_data->spdif_port.cfg.spdif_cfg_minor_version =
+ AFE_API_VERSION_SPDIF_CONFIG;
+ dev_dbg(dai->dev, " channel %d sample rate %d bit width %d\n",
+ dai_data->channels, dai_data->rate,
+ dai_data->spdif_port.cfg.bit_width);
+ dai_data->spdif_port.cfg.reserved = 0;
+ return 0;
+}
+
+static void msm_dai_q6_spdif_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ pr_info("%s: afe port not started. dai_data->status_mask = %ld\n",
+ __func__, *dai_data->status_mask);
+ return;
+ }
+
+ rc = afe_close(dai->id);
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+ *dai_data->status_mask);
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+}
+
+
+static int msm_dai_q6_spdif_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: clk_config failed", __func__);
+ return rc;
+ }
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_spdif_port_start(dai->id, &dai_data->spdif_port,
+ dai_data->rate);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE port 0x%x\n",
+ dai->id);
+ else
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_spdif_dai_data *dai_data;
+ const struct snd_kcontrol_new *kcontrol;
+ int rc = 0;
+ struct snd_soc_dapm_route intercon;
+ struct snd_soc_dapm_context *dapm;
+
+ if (!dai) {
+ pr_err("%s: dai not found!!\n", __func__);
+ return -EINVAL;
+ }
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_spdif_dai_data),
+ GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ AFE_PORT_ID_SPDIF_RX);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ kcontrol = &spdif_config_controls[1];
+ dapm = snd_soc_component_get_dapm(dai->component);
+
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+
+ memset(&intercon, 0 , sizeof(intercon));
+ if (!rc && dai && dai->driver) {
+ if (dai->driver->playback.stream_name &&
+ dai->driver->playback.aif_name) {
+ dev_dbg(dai->dev, "%s: add route for widget %s",
+ __func__, dai->driver->playback.stream_name);
+ intercon.source = dai->driver->playback.aif_name;
+ intercon.sink = dai->driver->playback.stream_name;
+ dev_dbg(dai->dev, "%s: src %s sink %s\n",
+ __func__, intercon.source, intercon.sink);
+ snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ }
+ if (dai->driver->capture.stream_name &&
+ dai->driver->capture.aif_name) {
+ dev_dbg(dai->dev, "%s: add route for widget %s",
+ __func__, dai->driver->capture.stream_name);
+ intercon.sink = dai->driver->capture.aif_name;
+ intercon.source = dai->driver->capture.stream_name;
+ dev_dbg(dai->dev, "%s: src %s sink %s\n",
+ __func__, intercon.source, intercon.sink);
+ snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ }
+ }
+ return rc;
+}
+
+static int msm_dai_q6_spdif_dai_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_spdif_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(dai->id); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+ kfree(dai_data);
+
+ return 0;
+}
+
+
+static struct snd_soc_dai_ops msm_dai_q6_spdif_ops = {
+ .prepare = msm_dai_q6_spdif_prepare,
+ .hw_params = msm_dai_q6_spdif_hw_params,
+ .shutdown = msm_dai_q6_spdif_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_spdif_spdif_rx_dai = {
+ .playback = {
+ .stream_name = "SPDIF Playback",
+ .aif_name = "SPDIF_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_spdif_ops,
+ .probe = msm_dai_q6_spdif_dai_probe,
+ .remove = msm_dai_q6_spdif_dai_remove,
+};
+
+static const struct snd_soc_component_driver msm_dai_spdif_q6_component = {
+ .name = "msm-dai-q6-spdif",
+};
+
+static int msm_dai_q6_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ if (dai_data->enc_config.format != ENC_FMT_NONE) {
+ int bitwidth = 0;
+
+ switch (dai_data->afe_in_bitformat) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bitwidth = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bitwidth = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bitwidth = 16;
+ break;
+ }
+ pr_debug("%s: calling AFE_PORT_START_V2 with enc_format: %d\n",
+ __func__, dai_data->enc_config.format);
+ rc = afe_port_start_v2(dai->id, &dai_data->port_config,
+ dai_data->rate,
+ dai_data->afe_in_channels,
+ bitwidth,
+ &dai_data->enc_config);
+ if (rc < 0)
+ pr_err("%s: afe_port_start_v2 failed error: %d\n",
+ __func__, rc);
+ } else {
+ rc = afe_port_start(dai->id, &dai_data->port_config,
+ dai_data->rate);
+ }
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE port 0x%x\n",
+ dai->id);
+ else
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+ return rc;
+}
+
+static int msm_dai_q6_cdc_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ switch (dai_data->channels) {
+ case 2:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO;
+ break;
+ case 1:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+ break;
+ default:
+ return -EINVAL;
+ pr_err("%s: err channels %d\n",
+ __func__, dai_data->channels);
+ break;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_SPECIAL:
+ dai_data->port_config.i2s.bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ dai_data->port_config.i2s.bit_width = 24;
+ break;
+ default:
+ pr_err("%s: format %d\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.i2s.sample_rate = dai_data->rate;
+ dai_data->port_config.i2s.i2s_cfg_minor_version =
+ AFE_API_VERSION_I2S_CONFIG;
+ dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA;
+ dev_dbg(dai->dev, " channel %d sample rate %d entered\n",
+ dai_data->channels, dai_data->rate);
+
+ dai_data->port_config.i2s.channel_mode = 1;
+ return 0;
+}
+
+static u8 num_of_bits_set(u8 sd_line_mask)
+{
+ u8 num_bits_set = 0;
+
+ while (sd_line_mask) {
+ num_bits_set++;
+ sd_line_mask = sd_line_mask & (sd_line_mask - 1);
+ }
+ return num_bits_set;
+}
+
+static int msm_dai_q6_i2s_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct msm_i2s_data *i2s_pdata =
+ (struct msm_i2s_data *) dai->dev->platform_data;
+
+ dai_data->channels = params_channels(params);
+ if (num_of_bits_set(i2s_pdata->sd_lines) == 1) {
+ switch (dai_data->channels) {
+ case 2:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO;
+ break;
+ case 1:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+ break;
+ default:
+ pr_warn("%s: greater than stereo has not been validated %d",
+ __func__, dai_data->channels);
+ break;
+ }
+ }
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.i2s.sample_rate = dai_data->rate;
+ dai_data->port_config.i2s.i2s_cfg_minor_version =
+ AFE_API_VERSION_I2S_CONFIG;
+ dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA;
+ /* Q6 only supports 16 as now */
+ dai_data->port_config.i2s.bit_width = 16;
+ dai_data->port_config.i2s.channel_mode = 1;
+
+ return 0;
+}
+
+static int msm_dai_q6_slim_bus_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_SPECIAL:
+ dai_data->port_config.slim_sch.bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ dai_data->port_config.slim_sch.bit_width = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dai_data->port_config.slim_sch.bit_width = 32;
+ break;
+ default:
+ pr_err("%s: format %d\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ dai_data->port_config.slim_sch.sb_cfg_minor_version =
+ AFE_API_VERSION_SLIMBUS_CONFIG;
+ dai_data->port_config.slim_sch.sample_rate = dai_data->rate;
+ dai_data->port_config.slim_sch.num_channels = dai_data->channels;
+
+ switch (dai->id) {
+ case SLIMBUS_7_RX:
+ case SLIMBUS_7_TX:
+ case SLIMBUS_8_RX:
+ case SLIMBUS_8_TX:
+ dai_data->port_config.slim_sch.slimbus_dev_id =
+ AFE_SLIMBUS_DEVICE_2;
+ break;
+ default:
+ dai_data->port_config.slim_sch.slimbus_dev_id =
+ AFE_SLIMBUS_DEVICE_1;
+ break;
+ }
+
+ dev_dbg(dai->dev, "%s:slimbus_dev_id[%hu] bit_wd[%hu] format[%hu]\n"
+ "num_channel %hu shared_ch_mapping[0] %hu\n"
+ "slave_port_mapping[1] %hu slave_port_mapping[2] %hu\n"
+ "sample_rate %d\n", __func__,
+ dai_data->port_config.slim_sch.slimbus_dev_id,
+ dai_data->port_config.slim_sch.bit_width,
+ dai_data->port_config.slim_sch.data_format,
+ dai_data->port_config.slim_sch.num_channels,
+ dai_data->port_config.slim_sch.shared_ch_mapping[0],
+ dai_data->port_config.slim_sch.shared_ch_mapping[1],
+ dai_data->port_config.slim_sch.shared_ch_mapping[2],
+ dai_data->rate);
+
+ return 0;
+}
+
+static int msm_dai_q6_usb_audio_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_SPECIAL:
+ dai_data->port_config.usb_audio.bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ dai_data->port_config.usb_audio.bit_width = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dai_data->port_config.usb_audio.bit_width = 32;
+ break;
+
+ default:
+ dev_err(dai->dev, "%s: invalid format %d\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+ dai_data->port_config.usb_audio.cfg_minor_version =
+ AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG;
+ dai_data->port_config.usb_audio.num_channels = dai_data->channels;
+ dai_data->port_config.usb_audio.sample_rate = dai_data->rate;
+
+ dev_dbg(dai->dev, "%s: dev_id[0x%x] bit_wd[%hu] format[%hu]\n"
+ "num_channel %hu sample_rate %d\n", __func__,
+ dai_data->port_config.usb_audio.dev_token,
+ dai_data->port_config.usb_audio.bit_width,
+ dai_data->port_config.usb_audio.data_format,
+ dai_data->port_config.usb_audio.num_channels,
+ dai_data->port_config.usb_audio.sample_rate);
+
+ return 0;
+}
+
+static int msm_dai_q6_bt_fm_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ dev_dbg(dai->dev, "channels %d sample rate %d entered\n",
+ dai_data->channels, dai_data->rate);
+
+ memset(&dai_data->port_config, 0, sizeof(dai_data->port_config));
+
+ pr_debug("%s: setting bt_fm parameters\n", __func__);
+
+ dai_data->port_config.int_bt_fm.bt_fm_cfg_minor_version =
+ AFE_API_VERSION_INTERNAL_BT_FM_CONFIG;
+ dai_data->port_config.int_bt_fm.num_channels = dai_data->channels;
+ dai_data->port_config.int_bt_fm.sample_rate = dai_data->rate;
+ dai_data->port_config.int_bt_fm.bit_width = 16;
+
+ return 0;
+}
+
+static int msm_dai_q6_afe_rtproxy_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.rtproxy.num_channels = params_channels(params);
+ dai_data->port_config.rtproxy.sample_rate = params_rate(params);
+
+ pr_debug("channel %d entered,dai_id: %d,rate: %d\n",
+ dai_data->port_config.rtproxy.num_channels, dai->id, dai_data->rate);
+
+ dai_data->port_config.rtproxy.rt_proxy_cfg_minor_version =
+ AFE_API_VERSION_RT_PROXY_CONFIG;
+ dai_data->port_config.rtproxy.bit_width = 16; /* Q6 only supports 16 */
+ dai_data->port_config.rtproxy.interleaved = 1;
+ dai_data->port_config.rtproxy.frame_size = params_period_bytes(params);
+ dai_data->port_config.rtproxy.jitter_allowance =
+ dai_data->port_config.rtproxy.frame_size/2;
+ dai_data->port_config.rtproxy.low_water_mark = 0;
+ dai_data->port_config.rtproxy.high_water_mark = 0;
+
+ return 0;
+}
+
+static int msm_dai_q6_psuedo_port_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ /* Q6 only supports 16 as now */
+ dai_data->port_config.pseudo_port.pseud_port_cfg_minor_version =
+ AFE_API_VERSION_PSEUDO_PORT_CONFIG;
+ dai_data->port_config.pseudo_port.num_channels =
+ params_channels(params);
+ dai_data->port_config.pseudo_port.bit_width = 16;
+ dai_data->port_config.pseudo_port.data_format = 0;
+ dai_data->port_config.pseudo_port.timing_mode =
+ AFE_PSEUDOPORT_TIMING_MODE_TIMER;
+ dai_data->port_config.pseudo_port.sample_rate = params_rate(params);
+
+ dev_dbg(dai->dev, "%s: bit_wd[%hu] num_channels [%hu] format[%hu]\n"
+ "timing Mode %hu sample_rate %d\n", __func__,
+ dai_data->port_config.pseudo_port.bit_width,
+ dai_data->port_config.pseudo_port.num_channels,
+ dai_data->port_config.pseudo_port.data_format,
+ dai_data->port_config.pseudo_port.timing_mode,
+ dai_data->port_config.pseudo_port.sample_rate);
+
+ return 0;
+}
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int rc = 0;
+
+ switch (dai->id) {
+ case PRIMARY_I2S_TX:
+ case PRIMARY_I2S_RX:
+ case SECONDARY_I2S_RX:
+ rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream);
+ break;
+ case MI2S_RX:
+ rc = msm_dai_q6_i2s_hw_params(params, dai, substream->stream);
+ break;
+ case SLIMBUS_0_RX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_3_RX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_5_RX:
+ case SLIMBUS_6_RX:
+ case SLIMBUS_7_RX:
+ case SLIMBUS_8_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_TX:
+ case SLIMBUS_4_TX:
+ case SLIMBUS_5_TX:
+ case SLIMBUS_6_TX:
+ case SLIMBUS_7_TX:
+ case SLIMBUS_8_TX:
+ rc = msm_dai_q6_slim_bus_hw_params(params, dai,
+ substream->stream);
+ break;
+ case INT_BT_SCO_RX:
+ case INT_BT_SCO_TX:
+ case INT_BT_A2DP_RX:
+ case INT_FM_RX:
+ case INT_FM_TX:
+ rc = msm_dai_q6_bt_fm_hw_params(params, dai, substream->stream);
+ break;
+ case AFE_PORT_ID_USB_RX:
+ case AFE_PORT_ID_USB_TX:
+ rc = msm_dai_q6_usb_audio_hw_params(params, dai,
+ substream->stream);
+ break;
+ case RT_PROXY_DAI_001_TX:
+ case RT_PROXY_DAI_001_RX:
+ case RT_PROXY_DAI_002_TX:
+ case RT_PROXY_DAI_002_RX:
+ case RT_PROXY_PORT_002_TX:
+ case RT_PROXY_PORT_002_RX:
+ rc = msm_dai_q6_afe_rtproxy_hw_params(params, dai);
+ break;
+ case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ rc = msm_dai_q6_psuedo_port_hw_params(params,
+ dai, substream->stream);
+ break;
+ default:
+ dev_err(dai->dev, "invalid AFE port ID 0x%x\n", dai->id);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static void msm_dai_q6_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ pr_debug("%s: stop pseudo port:%d\n", __func__, dai->id);
+ rc = afe_close(dai->id); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+ pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+ *dai_data->status_mask);
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+}
+
+static int msm_dai_q6_cdc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ dai_data->port_config.i2s.ws_src = 1; /* CPU is master */
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ dai_data->port_config.i2s.ws_src = 0; /* CPU is slave */
+ break;
+ default:
+ pr_err("%s: fmt 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_dai_q6_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ int rc = 0;
+
+ dev_dbg(dai->dev, "%s: id = %d fmt[%d]\n", __func__,
+ dai->id, fmt);
+ switch (dai->id) {
+ case PRIMARY_I2S_TX:
+ case PRIMARY_I2S_RX:
+ case MI2S_RX:
+ case SECONDARY_I2S_RX:
+ rc = msm_dai_q6_cdc_set_fmt(dai, fmt);
+ break;
+ default:
+ dev_err(dai->dev, "invalid cpu_dai id 0x%x\n", dai->id);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+
+{
+ int rc = 0;
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ unsigned int i = 0;
+
+ dev_dbg(dai->dev, "%s: id = %d\n", __func__, dai->id);
+ switch (dai->id) {
+ case SLIMBUS_0_RX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_3_RX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_5_RX:
+ case SLIMBUS_6_RX:
+ case SLIMBUS_7_RX:
+ case SLIMBUS_8_RX:
+ /*
+ * channel number to be between 128 and 255.
+ * For RX port use channel numbers
+ * from 138 to 144 for pre-Taiko
+ * from 144 to 159 for Taiko
+ */
+ if (!rx_slot) {
+ pr_err("%s: rx slot not found\n", __func__);
+ return -EINVAL;
+ }
+ if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) {
+ pr_err("%s: invalid rx num %d\n", __func__, rx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rx_num; i++) {
+ dai_data->port_config.slim_sch.shared_ch_mapping[i] =
+ rx_slot[i];
+ pr_debug("%s: find number of channels[%d] ch[%d]\n",
+ __func__, i, rx_slot[i]);
+ }
+ dai_data->port_config.slim_sch.num_channels = rx_num;
+ pr_debug("%s: SLIMBUS_%d_RX cnt[%d] ch[%d %d]\n", __func__,
+ (dai->id - SLIMBUS_0_RX) / 2, rx_num,
+ dai_data->port_config.slim_sch.shared_ch_mapping[0],
+ dai_data->port_config.slim_sch.shared_ch_mapping[1]);
+
+ break;
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_TX:
+ case SLIMBUS_4_TX:
+ case SLIMBUS_5_TX:
+ case SLIMBUS_6_TX:
+ case SLIMBUS_7_TX:
+ case SLIMBUS_8_TX:
+ /*
+ * channel number to be between 128 and 255.
+ * For TX port use channel numbers
+ * from 128 to 137 for pre-Taiko
+ * from 128 to 143 for Taiko
+ */
+ if (!tx_slot) {
+ pr_err("%s: tx slot not found\n", __func__);
+ return -EINVAL;
+ }
+ if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) {
+ pr_err("%s: invalid tx num %d\n", __func__, tx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < tx_num; i++) {
+ dai_data->port_config.slim_sch.shared_ch_mapping[i] =
+ tx_slot[i];
+ pr_debug("%s: find number of channels[%d] ch[%d]\n",
+ __func__, i, tx_slot[i]);
+ }
+ dai_data->port_config.slim_sch.num_channels = tx_num;
+ pr_debug("%s:SLIMBUS_%d_TX cnt[%d] ch[%d %d]\n", __func__,
+ (dai->id - SLIMBUS_0_TX) / 2, tx_num,
+ dai_data->port_config.slim_sch.shared_ch_mapping[0],
+ dai_data->port_config.slim_sch.shared_ch_mapping[1]);
+ break;
+ default:
+ dev_err(dai->dev, "invalid cpu_dai id 0x%x\n", dai->id);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_ops = {
+ .prepare = msm_dai_q6_prepare,
+ .hw_params = msm_dai_q6_hw_params,
+ .shutdown = msm_dai_q6_shutdown,
+ .set_fmt = msm_dai_q6_set_fmt,
+ .set_channel_map = msm_dai_q6_set_channel_map,
+};
+
+/*
+ * For single CPU DAI registration, the dai id needs to be
+ * set explicitly in the dai probe as ASoC does not read
+ * the cpu->driver->id field rather it assigns the dai id
+ * from the device name that is in the form %s.%d. This dai
+ * id should be assigned to back-end AFE port id and used
+ * during dai prepare. For multiple dai registration, it
+ * is not required to call this function, however the dai->
+ * driver->id field must be defined and set to corresponding
+ * AFE Port id.
+ */
+static inline void msm_dai_q6_set_dai_id(struct snd_soc_dai *dai)
+{
+ if (!dai->driver->id) {
+ dev_warn(dai->dev, "DAI driver id is not set\n");
+ return;
+ }
+ dai->id = dai->driver->id;
+ return;
+}
+
+static int msm_dai_q6_cal_info_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+ u16 port_id = ((struct soc_enum *)
+ kcontrol->private_value)->reg;
+
+ dai_data->cal_mode = ucontrol->value.integer.value[0];
+ pr_debug("%s: setting cal_mode to %d\n",
+ __func__, dai_data->cal_mode);
+ afe_set_cal_mode(port_id, dai_data->cal_mode);
+
+ return 0;
+}
+
+static int msm_dai_q6_cal_info_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ ucontrol->value.integer.value[0] = dai_data->cal_mode;
+ return 0;
+}
+
+static int msm_dai_q6_sb_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+
+ if (dai_data) {
+ dai_data->port_config.slim_sch.data_format = value;
+ pr_debug("%s: format = %d\n", __func__, value);
+ }
+
+ return 0;
+}
+
+static int msm_dai_q6_sb_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ if (dai_data)
+ ucontrol->value.integer.value[0] =
+ dai_data->port_config.slim_sch.data_format;
+
+ return 0;
+}
+
+static int msm_dai_q6_usb_audio_cfg_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+ u32 val = ucontrol->value.integer.value[0];
+
+ if (dai_data) {
+ dai_data->port_config.usb_audio.dev_token = val;
+ pr_debug("%s: dev_token = 0x%x\n", __func__,
+ dai_data->port_config.usb_audio.dev_token);
+ } else {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ }
+
+ return 0;
+}
+
+static int msm_dai_q6_usb_audio_cfg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ if (dai_data) {
+ ucontrol->value.integer.value[0] =
+ dai_data->port_config.usb_audio.dev_token;
+ pr_debug("%s: dev_token = 0x%x\n", __func__,
+ dai_data->port_config.usb_audio.dev_token);
+ } else {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ }
+
+ return 0;
+}
+
+static int msm_dai_q6_usb_audio_endian_cfg_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+ u32 val = ucontrol->value.integer.value[0];
+
+ if (dai_data) {
+ dai_data->port_config.usb_audio.endian = val;
+ pr_debug("%s: endian = 0x%x\n", __func__,
+ dai_data->port_config.usb_audio.endian);
+ } else {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_dai_q6_usb_audio_endian_cfg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ if (dai_data) {
+ ucontrol->value.integer.value[0] =
+ dai_data->port_config.usb_audio.endian;
+ pr_debug("%s: endian = 0x%x\n", __func__,
+ dai_data->port_config.usb_audio.endian);
+ } else {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_dai_q6_afe_enc_cfg_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(struct afe_enc_config);
+
+ return 0;
+}
+
+static int msm_dai_q6_afe_enc_cfg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ if (dai_data) {
+ int format_size = sizeof(dai_data->enc_config.format);
+
+ pr_debug("%s:encoder config for %d format\n",
+ __func__, dai_data->enc_config.format);
+ memcpy(ucontrol->value.bytes.data,
+ &dai_data->enc_config.format,
+ format_size);
+ switch (dai_data->enc_config.format) {
+ case ENC_FMT_SBC:
+ memcpy(ucontrol->value.bytes.data + format_size,
+ &dai_data->enc_config.data,
+ sizeof(struct asm_sbc_enc_cfg_t));
+ break;
+ case ENC_FMT_AAC_V2:
+ memcpy(ucontrol->value.bytes.data + format_size,
+ &dai_data->enc_config.data,
+ sizeof(struct asm_aac_enc_cfg_v2_t));
+ break;
+ case ENC_FMT_APTX:
+ case ENC_FMT_APTX_HD:
+ memcpy(ucontrol->value.bytes.data + format_size,
+ &dai_data->enc_config.data,
+ sizeof(struct asm_aac_enc_cfg_v2_t));
+ break;
+ default:
+ pr_debug("%s: unknown format = %d\n",
+ __func__, dai_data->enc_config.format);
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int msm_dai_q6_afe_enc_cfg_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ if (dai_data) {
+ int format_size = sizeof(dai_data->enc_config.format);
+
+ memset(&dai_data->enc_config, 0x0,
+ sizeof(struct afe_enc_config));
+ memcpy(&dai_data->enc_config.format,
+ ucontrol->value.bytes.data,
+ format_size);
+ pr_debug("%s: Received encoder config for %d format\n",
+ __func__, dai_data->enc_config.format);
+ switch (dai_data->enc_config.format) {
+ case ENC_FMT_SBC:
+ memcpy(&dai_data->enc_config.data,
+ ucontrol->value.bytes.data + format_size,
+ sizeof(struct asm_sbc_enc_cfg_t));
+ break;
+ case ENC_FMT_AAC_V2:
+ memcpy(&dai_data->enc_config.data,
+ ucontrol->value.bytes.data + format_size,
+ sizeof(struct asm_aac_enc_cfg_v2_t));
+ break;
+ case ENC_FMT_APTX:
+ case ENC_FMT_APTX_HD:
+ memcpy(&dai_data->enc_config.data,
+ ucontrol->value.bytes.data + format_size,
+ sizeof(struct asm_custom_enc_cfg_aptx_t));
+ break;
+ default:
+ pr_debug("%s: Ignore enc config for unknown format = %d\n",
+ __func__, dai_data->enc_config.format);
+ ret = -EINVAL;
+ break;
+ }
+ } else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static const char *const afe_input_chs_text[] = {"Zero", "One", "Two"};
+
+static const struct soc_enum afe_input_chs_enum[] = {
+ SOC_ENUM_SINGLE_EXT(3, afe_input_chs_text),
+};
+
+static const char *const afe_input_bit_format_text[] = {"S16_LE", "S24_LE"};
+
+static const struct soc_enum afe_input_bit_format_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, afe_input_bit_format_text),
+};
+
+static int msm_dai_q6_afe_input_channel_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ if (dai_data) {
+ ucontrol->value.integer.value[0] = dai_data->afe_in_channels;
+ pr_debug("%s:afe input channel = %d\n",
+ __func__, dai_data->afe_in_channels);
+ }
+
+ return 0;
+}
+
+static int msm_dai_q6_afe_input_channel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ if (dai_data) {
+ dai_data->afe_in_channels = ucontrol->value.integer.value[0];
+ pr_debug("%s: updating afe input channel : %d\n",
+ __func__, dai_data->afe_in_channels);
+ }
+
+ return 0;
+}
+
+static int msm_dai_q6_afe_input_bit_format_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ if (!dai_data) {
+ pr_err("%s: Invalid dai data\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (dai_data->afe_in_bitformat) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: afe input bit format : %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_dai_q6_afe_input_bit_format_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ if (!dai_data) {
+ pr_err("%s: Invalid dai data\n", __func__);
+ return -EINVAL;
+ }
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ case 1:
+ dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: updating afe input bit format : %d\n",
+ __func__, dai_data->afe_in_bitformat);
+
+ return 0;
+}
+
+
+static const struct snd_kcontrol_new afe_enc_config_controls[] = {
+ {
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE),
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "SLIM_7_RX Encoder Config",
+ .info = msm_dai_q6_afe_enc_cfg_info,
+ .get = msm_dai_q6_afe_enc_cfg_get,
+ .put = msm_dai_q6_afe_enc_cfg_put,
+ },
+ SOC_ENUM_EXT("AFE Input Channels", afe_input_chs_enum[0],
+ msm_dai_q6_afe_input_channel_get,
+ msm_dai_q6_afe_input_channel_put),
+ SOC_ENUM_EXT("AFE Input Bit Format", afe_input_bit_format_enum[0],
+ msm_dai_q6_afe_input_bit_format_get,
+ msm_dai_q6_afe_input_bit_format_put),
+};
+
+static int msm_dai_q6_slim_rx_drift_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(struct afe_param_id_dev_timing_stats);
+
+ return 0;
+}
+
+static int msm_dai_q6_slim_rx_drift_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = -EINVAL;
+ struct afe_param_id_dev_timing_stats timing_stats;
+ struct snd_soc_dai *dai = kcontrol->private_data;
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ pr_err("%s: afe port not started. dai_data->status_mask = %ld\n",
+ __func__, *dai_data->status_mask);
+ goto done;
+ }
+
+ memset(&timing_stats, 0, sizeof(struct afe_param_id_dev_timing_stats));
+ ret = afe_get_av_dev_drift(&timing_stats, dai->id);
+ if (ret) {
+ pr_err("%s: Error getting AFE Drift for port %d, err=%d\n",
+ __func__, dai->id, ret);
+
+ goto done;
+ }
+
+ memcpy(ucontrol->value.bytes.data, (void *)&timing_stats,
+ sizeof(struct afe_param_id_dev_timing_stats));
+done:
+ return ret;
+}
+
+static const char * const afe_cal_mode_text[] = {
+ "CAL_MODE_DEFAULT", "CAL_MODE_NONE"
+};
+
+static const struct soc_enum slim_2_rx_enum =
+ SOC_ENUM_SINGLE(SLIMBUS_2_RX, 0, ARRAY_SIZE(afe_cal_mode_text),
+ afe_cal_mode_text);
+
+static const struct soc_enum rt_proxy_1_rx_enum =
+ SOC_ENUM_SINGLE(RT_PROXY_PORT_001_RX, 0, ARRAY_SIZE(afe_cal_mode_text),
+ afe_cal_mode_text);
+
+static const struct soc_enum rt_proxy_1_tx_enum =
+ SOC_ENUM_SINGLE(RT_PROXY_PORT_001_TX, 0, ARRAY_SIZE(afe_cal_mode_text),
+ afe_cal_mode_text);
+
+static const struct snd_kcontrol_new sb_config_controls[] = {
+ SOC_ENUM_EXT("SLIM_4_TX Format", sb_config_enum[0],
+ msm_dai_q6_sb_format_get,
+ msm_dai_q6_sb_format_put),
+ SOC_ENUM_EXT("SLIM_2_RX SetCalMode", slim_2_rx_enum,
+ msm_dai_q6_cal_info_get,
+ msm_dai_q6_cal_info_put),
+ SOC_ENUM_EXT("SLIM_2_RX Format", sb_config_enum[0],
+ msm_dai_q6_sb_format_get,
+ msm_dai_q6_sb_format_put)
+};
+
+static const struct snd_kcontrol_new rt_proxy_config_controls[] = {
+ SOC_ENUM_EXT("RT_PROXY_1_RX SetCalMode", rt_proxy_1_rx_enum,
+ msm_dai_q6_cal_info_get,
+ msm_dai_q6_cal_info_put),
+ SOC_ENUM_EXT("RT_PROXY_1_TX SetCalMode", rt_proxy_1_tx_enum,
+ msm_dai_q6_cal_info_get,
+ msm_dai_q6_cal_info_put),
+};
+
+static const struct snd_kcontrol_new usb_audio_cfg_controls[] = {
+ SOC_SINGLE_EXT("USB_AUDIO_RX dev_token", 0, 0, UINT_MAX, 0,
+ msm_dai_q6_usb_audio_cfg_get,
+ msm_dai_q6_usb_audio_cfg_put),
+ SOC_SINGLE_EXT("USB_AUDIO_RX endian", 0, 0, 1, 0,
+ msm_dai_q6_usb_audio_endian_cfg_get,
+ msm_dai_q6_usb_audio_endian_cfg_put),
+ SOC_SINGLE_EXT("USB_AUDIO_TX dev_token", 0, 0, UINT_MAX, 0,
+ msm_dai_q6_usb_audio_cfg_get,
+ msm_dai_q6_usb_audio_cfg_put),
+ SOC_SINGLE_EXT("USB_AUDIO_TX endian", 0, 0, 1, 0,
+ msm_dai_q6_usb_audio_endian_cfg_get,
+ msm_dai_q6_usb_audio_endian_cfg_put),
+};
+
+static const struct snd_kcontrol_new avd_drift_config_controls[] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "SLIMBUS_0_RX DRIFT",
+ .info = msm_dai_q6_slim_rx_drift_info,
+ .get = msm_dai_q6_slim_rx_drift_get,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "SLIMBUS_6_RX DRIFT",
+ .info = msm_dai_q6_slim_rx_drift_info,
+ .get = msm_dai_q6_slim_rx_drift_get,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "SLIMBUS_7_RX DRIFT",
+ .info = msm_dai_q6_slim_rx_drift_info,
+ .get = msm_dai_q6_slim_rx_drift_get,
+ },
+};
+static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc = 0;
+
+ if (!dai) {
+ pr_err("%s: Invalid params dai\n", __func__);
+ return -EINVAL;
+ }
+ if (!dai->dev) {
+ pr_err("%s: Invalid params dai dev\n", __func__);
+ return -EINVAL;
+ }
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data), GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ msm_dai_q6_set_dai_id(dai);
+
+ switch (dai->id) {
+ case SLIMBUS_4_TX:
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&sb_config_controls[0],
+ dai_data));
+ break;
+ case SLIMBUS_2_RX:
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&sb_config_controls[1],
+ dai_data));
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&sb_config_controls[2],
+ dai_data));
+ break;
+ case SLIMBUS_7_RX:
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&afe_enc_config_controls[0],
+ dai_data));
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&afe_enc_config_controls[1],
+ dai_data));
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&afe_enc_config_controls[2],
+ dai_data));
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&avd_drift_config_controls[2],
+ dai));
+ break;
+ case RT_PROXY_DAI_001_RX:
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&rt_proxy_config_controls[0],
+ dai_data));
+ break;
+ case RT_PROXY_DAI_001_TX:
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&rt_proxy_config_controls[1],
+ dai_data));
+ break;
+ case AFE_PORT_ID_USB_RX:
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&usb_audio_cfg_controls[0],
+ dai_data));
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&usb_audio_cfg_controls[1],
+ dai_data));
+ break;
+ case AFE_PORT_ID_USB_TX:
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&usb_audio_cfg_controls[2],
+ dai_data));
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&usb_audio_cfg_controls[3],
+ dai_data));
+ break;
+ case SLIMBUS_0_RX:
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&avd_drift_config_controls[0],
+ dai));
+ break;
+ case SLIMBUS_6_RX:
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(&avd_drift_config_controls[1],
+ dai));
+ break;
+ }
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "%s: err add config ctl, DAI = %s\n",
+ __func__, dai->name);
+
+ rc = msm_dai_q6_dai_add_route(dai);
+ return rc;
+}
+
+static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ pr_debug("%s: stop pseudo port:%d\n", __func__, dai->id);
+ rc = afe_close(dai->id); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+ kfree(dai_data);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver msm_dai_q6_afe_rx_dai[] = {
+ {
+ .playback = {
+ .stream_name = "AFE Playback",
+ .aif_name = "PCM_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = RT_PROXY_DAI_001_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "AFE-PROXY RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = RT_PROXY_DAI_002_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_afe_tx_dai[] = {
+ {
+ .capture = {
+ .stream_name = "AFE Capture",
+ .aif_name = "PCM_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = RT_PROXY_DAI_002_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "AFE-PROXY TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = RT_PROXY_DAI_001_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_sco_rx_dai = {
+ .playback = {
+ .stream_name = "Internal BT-SCO Playback",
+ .aif_name = "INT_BT_SCO_RX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = INT_BT_SCO_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_a2dp_rx_dai = {
+ .playback = {
+ .stream_name = "Internal BT-A2DP Playback",
+ .aif_name = "INT_BT_A2DP_RX",
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = INT_BT_A2DP_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_sco_tx_dai = {
+ .capture = {
+ .stream_name = "Internal BT-SCO Capture",
+ .aif_name = "INT_BT_SCO_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = INT_BT_SCO_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_rx_dai = {
+ .playback = {
+ .stream_name = "Internal FM Playback",
+ .aif_name = "INT_FM_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = INT_FM_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_tx_dai = {
+ .capture = {
+ .stream_name = "Internal FM Capture",
+ .aif_name = "INT_FM_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = INT_FM_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_voc_playback_dai[] = {
+ {
+ .playback = {
+ .stream_name = "Voice Farend Playback",
+ .aif_name = "VOICE_PLAYBACK_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = VOICE_PLAYBACK_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Voice2 Farend Playback",
+ .aif_name = "VOICE2_PLAYBACK_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = VOICE2_PLAYBACK_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_incall_record_dai[] = {
+ {
+ .capture = {
+ .stream_name = "Voice Uplink Capture",
+ .aif_name = "INCALL_RECORD_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = VOICE_RECORD_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Voice Downlink Capture",
+ .aif_name = "INCALL_RECORD_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = VOICE_RECORD_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_proxy_tx_dai = {
+ .capture = {
+ .stream_name = "Proxy Capture",
+ .aif_name = "PROXY_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = RT_PROXY_PORT_002_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_proxy_rx_dai = {
+ .playback = {
+ .stream_name = "Proxy Playback",
+ .aif_name = "PROXY_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = RT_PROXY_PORT_002_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_usb_rx_dai = {
+ .playback = {
+ .stream_name = "USB Audio Playback",
+ .aif_name = "USB_AUDIO_RX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 |
+ SNDRV_PCM_RATE_384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = AFE_PORT_ID_USB_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_usb_tx_dai = {
+ .capture = {
+ .stream_name = "USB Audio Capture",
+ .aif_name = "USB_AUDIO_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 |
+ SNDRV_PCM_RATE_384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_max = 384000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = AFE_PORT_ID_USB_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static int msm_auxpcm_dev_probe(struct platform_device *pdev)
+{
+ struct msm_dai_q6_auxpcm_dai_data *dai_data;
+ struct msm_dai_auxpcm_pdata *auxpcm_pdata;
+ uint32_t val_array[RATE_MAX_NUM_OF_AUX_PCM_RATES];
+ uint32_t val = 0;
+ const char *intf_name;
+ int rc = 0, i = 0, len = 0;
+ const uint32_t *slot_mapping_array = NULL;
+ u32 array_length = 0;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_auxpcm_dai_data),
+ GFP_KERNEL);
+ if (!dai_data) {
+ dev_err(&pdev->dev,
+ "Failed to allocate memory for auxpcm DAI data\n");
+ return -ENOMEM;
+ }
+
+ auxpcm_pdata = kzalloc(sizeof(struct msm_dai_auxpcm_pdata),
+ GFP_KERNEL);
+
+ if (!auxpcm_pdata) {
+ dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
+ goto fail_pdata_nomem;
+ }
+
+ dev_dbg(&pdev->dev, "%s: dev %pK, dai_data %pK, auxpcm_pdata %pK\n",
+ __func__, &pdev->dev, dai_data, auxpcm_pdata);
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-mode",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-mode missing in DT node\n",
+ __func__);
+ goto fail_invalid_dt;
+ }
+ auxpcm_pdata->mode_8k.mode = (u16)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.mode = (u16)val_array[RATE_16KHZ];
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-sync",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-sync missing in DT node\n",
+ __func__);
+ goto fail_invalid_dt;
+ }
+ auxpcm_pdata->mode_8k.sync = (u16)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.sync = (u16)val_array[RATE_16KHZ];
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-frame",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+
+ if (rc) {
+ dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-frame missing in DT node\n",
+ __func__);
+ goto fail_invalid_dt;
+ }
+ auxpcm_pdata->mode_8k.frame = (u16)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.frame = (u16)val_array[RATE_16KHZ];
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-quant",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-quant missing in DT node\n",
+ __func__);
+ goto fail_invalid_dt;
+ }
+ auxpcm_pdata->mode_8k.quant = (u16)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.quant = (u16)val_array[RATE_16KHZ];
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-num-slots",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-num-slots missing in DT node\n",
+ __func__);
+ goto fail_invalid_dt;
+ }
+ auxpcm_pdata->mode_8k.num_slots = (u16)val_array[RATE_8KHZ];
+
+ if (auxpcm_pdata->mode_8k.num_slots >
+ msm_dai_q6_max_num_slot(auxpcm_pdata->mode_8k.frame)) {
+ dev_err(&pdev->dev, "%s Max slots %d greater than DT node %d\n",
+ __func__,
+ msm_dai_q6_max_num_slot(auxpcm_pdata->mode_8k.frame),
+ auxpcm_pdata->mode_8k.num_slots);
+ rc = -EINVAL;
+ goto fail_invalid_dt;
+ }
+ auxpcm_pdata->mode_16k.num_slots = (u16)val_array[RATE_16KHZ];
+
+ if (auxpcm_pdata->mode_16k.num_slots >
+ msm_dai_q6_max_num_slot(auxpcm_pdata->mode_16k.frame)) {
+ dev_err(&pdev->dev, "%s Max slots %d greater than DT node %d\n",
+ __func__,
+ msm_dai_q6_max_num_slot(auxpcm_pdata->mode_16k.frame),
+ auxpcm_pdata->mode_16k.num_slots);
+ rc = -EINVAL;
+ goto fail_invalid_dt;
+ }
+
+ slot_mapping_array = of_get_property(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-slot-mapping", &len);
+
+ if (slot_mapping_array == NULL) {
+ dev_err(&pdev->dev, "%s slot_mapping_array is not valid\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_invalid_dt;
+ }
+
+ array_length = auxpcm_pdata->mode_8k.num_slots +
+ auxpcm_pdata->mode_16k.num_slots;
+
+ if (len != sizeof(uint32_t) * array_length) {
+ dev_err(&pdev->dev, "%s Length is %d and expected is %zd\n",
+ __func__, len, sizeof(uint32_t) * array_length);
+ rc = -EINVAL;
+ goto fail_invalid_dt;
+ }
+
+ auxpcm_pdata->mode_8k.slot_mapping =
+ kzalloc(sizeof(uint16_t) *
+ auxpcm_pdata->mode_8k.num_slots,
+ GFP_KERNEL);
+ if (!auxpcm_pdata->mode_8k.slot_mapping) {
+ dev_err(&pdev->dev, "%s No mem for mode_8k slot mapping\n",
+ __func__);
+ rc = -ENOMEM;
+ goto fail_invalid_dt;
+ }
+
+ for (i = 0; i < auxpcm_pdata->mode_8k.num_slots; i++)
+ auxpcm_pdata->mode_8k.slot_mapping[i] =
+ (u16)be32_to_cpu(slot_mapping_array[i]);
+
+ auxpcm_pdata->mode_16k.slot_mapping =
+ kzalloc(sizeof(uint16_t) *
+ auxpcm_pdata->mode_16k.num_slots,
+ GFP_KERNEL);
+
+ if (!auxpcm_pdata->mode_16k.slot_mapping) {
+ dev_err(&pdev->dev, "%s No mem for mode_16k slot mapping\n",
+ __func__);
+ rc = -ENOMEM;
+ goto fail_invalid_16k_slot_mapping;
+ }
+
+ for (i = 0; i < auxpcm_pdata->mode_16k.num_slots; i++)
+ auxpcm_pdata->mode_16k.slot_mapping[i] =
+ (u16)be32_to_cpu(slot_mapping_array[i +
+ auxpcm_pdata->mode_8k.num_slots]);
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-data",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-data missing in DT node\n",
+ __func__);
+ goto fail_invalid_dt1;
+ }
+ auxpcm_pdata->mode_8k.data = (u16)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.data = (u16)val_array[RATE_16KHZ];
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-pcm-clk-rate",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: qcom,msm-cpudai-auxpcm-pcm-clk-rate missing in DT\n",
+ __func__);
+ goto fail_invalid_dt1;
+ }
+ auxpcm_pdata->mode_8k.pcm_clk_rate = (int)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.pcm_clk_rate = (int)val_array[RATE_16KHZ];
+
+ rc = of_property_read_string(pdev->dev.of_node,
+ "qcom,msm-auxpcm-interface", &intf_name);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: qcom,msm-auxpcm-interface missing in DT node\n",
+ __func__);
+ goto fail_nodev_intf;
+ }
+
+ if (!strcmp(intf_name, "primary")) {
+ dai_data->rx_pid = AFE_PORT_ID_PRIMARY_PCM_RX;
+ dai_data->tx_pid = AFE_PORT_ID_PRIMARY_PCM_TX;
+ pdev->id = MSM_DAI_PRI_AUXPCM_DT_DEV_ID;
+ i = 0;
+ } else if (!strcmp(intf_name, "secondary")) {
+ dai_data->rx_pid = AFE_PORT_ID_SECONDARY_PCM_RX;
+ dai_data->tx_pid = AFE_PORT_ID_SECONDARY_PCM_TX;
+ pdev->id = MSM_DAI_SEC_AUXPCM_DT_DEV_ID;
+ i = 1;
+ } else if (!strcmp(intf_name, "tertiary")) {
+ dai_data->rx_pid = AFE_PORT_ID_TERTIARY_PCM_RX;
+ dai_data->tx_pid = AFE_PORT_ID_TERTIARY_PCM_TX;
+ pdev->id = MSM_DAI_TERT_AUXPCM_DT_DEV_ID;
+ i = 2;
+ } else if (!strcmp(intf_name, "quaternary")) {
+ dai_data->rx_pid = AFE_PORT_ID_QUATERNARY_PCM_RX;
+ dai_data->tx_pid = AFE_PORT_ID_QUATERNARY_PCM_TX;
+ pdev->id = MSM_DAI_QUAT_AUXPCM_DT_DEV_ID;
+ i = 3;
+ } else {
+ dev_err(&pdev->dev, "%s: invalid DT intf name %s\n",
+ __func__, intf_name);
+ goto fail_invalid_intf;
+ }
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-afe-clk-ver", &val);
+ if (rc)
+ dai_data->afe_clk_ver = AFE_CLK_VERSION_V1;
+ else
+ dai_data->afe_clk_ver = val;
+
+ mutex_init(&dai_data->rlock);
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+
+ dev_set_drvdata(&pdev->dev, dai_data);
+ pdev->dev.platform_data = (void *) auxpcm_pdata;
+
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_aux_pcm_dai_component,
+ &msm_dai_q6_aux_pcm_dai[i], 1);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: auxpcm dai reg failed, rc=%d\n",
+ __func__, rc);
+ goto fail_reg_dai;
+ }
+
+ return rc;
+
+fail_reg_dai:
+fail_invalid_intf:
+fail_nodev_intf:
+fail_invalid_dt1:
+ kfree(auxpcm_pdata->mode_16k.slot_mapping);
+fail_invalid_16k_slot_mapping:
+ kfree(auxpcm_pdata->mode_8k.slot_mapping);
+fail_invalid_dt:
+ kfree(auxpcm_pdata);
+fail_pdata_nomem:
+ kfree(dai_data);
+ return rc;
+}
+
+static int msm_auxpcm_dev_remove(struct platform_device *pdev)
+{
+ struct msm_dai_q6_auxpcm_dai_data *dai_data;
+
+ dai_data = dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_component(&pdev->dev);
+
+ mutex_destroy(&dai_data->rlock);
+ kfree(dai_data);
+ kfree(pdev->dev.platform_data);
+
+ return 0;
+}
+
+static struct of_device_id msm_auxpcm_dev_dt_match[] = {
+ { .compatible = "qcom,msm-auxpcm-dev", },
+ {}
+};
+
+
+static struct platform_driver msm_auxpcm_dev_driver = {
+ .probe = msm_auxpcm_dev_probe,
+ .remove = msm_auxpcm_dev_remove,
+ .driver = {
+ .name = "msm-auxpcm-dev",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_auxpcm_dev_dt_match,
+ },
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
+ {
+ .playback = {
+ .stream_name = "Slimbus Playback",
+ .aif_name = "SLIMBUS_0_RX",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_0_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Slimbus1 Playback",
+ .aif_name = "SLIMBUS_1_RX",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_1_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Slimbus2 Playback",
+ .aif_name = "SLIMBUS_2_RX",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_2_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Slimbus3 Playback",
+ .aif_name = "SLIMBUS_3_RX",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_3_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Slimbus4 Playback",
+ .aif_name = "SLIMBUS_4_RX",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_4_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Slimbus6 Playback",
+ .aif_name = "SLIMBUS_6_RX",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_6_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Slimbus5 Playback",
+ .aif_name = "SLIMBUS_5_RX",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_5_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Slimbus7 Playback",
+ .aif_name = "SLIMBUS_7_RX",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_7_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Slimbus8 Playback",
+ .aif_name = "SLIMBUS_8_RX",
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_8_RX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_tx_dai[] = {
+ {
+ .capture = {
+ .stream_name = "Slimbus Capture",
+ .aif_name = "SLIMBUS_0_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_0_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Slimbus1 Capture",
+ .aif_name = "SLIMBUS_1_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_1_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Slimbus2 Capture",
+ .aif_name = "SLIMBUS_2_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_2_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Slimbus3 Capture",
+ .aif_name = "SLIMBUS_3_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 2,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_3_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Slimbus4 Capture",
+ .aif_name = "SLIMBUS_4_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_4_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Slimbus5 Capture",
+ .aif_name = "SLIMBUS_5_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_5_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Slimbus6 Capture",
+ .aif_name = "SLIMBUS_6_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_6_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Slimbus7 Capture",
+ .aif_name = "SLIMBUS_7_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_7_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Slimbus8 Capture",
+ .aif_name = "SLIMBUS_8_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .id = SLIMBUS_8_TX,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+ },
+};
+
+static int msm_dai_q6_mi2s_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+ dai_data->port_config.i2s.data_format = value;
+ pr_debug("%s: value = %d, channel = %d, line = %d\n",
+ __func__, value, dai_data->port_config.i2s.mono_stereo,
+ dai_data->port_config.i2s.channel_mode);
+ return 0;
+}
+
+static int msm_dai_q6_mi2s_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+ ucontrol->value.integer.value[0] =
+ dai_data->port_config.i2s.data_format;
+ return 0;
+}
+
+static int msm_dai_q6_mi2s_vi_feed_mono_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+
+ dai_data->vi_feed_mono = value;
+ pr_debug("%s: value = %d\n", __func__, value);
+ return 0;
+}
+
+static int msm_dai_q6_mi2s_vi_feed_mono_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ ucontrol->value.integer.value[0] = dai_data->vi_feed_mono;
+ return 0;
+}
+
+static const struct snd_kcontrol_new mi2s_config_controls[] = {
+ SOC_ENUM_EXT("PRI MI2S RX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("SEC MI2S RX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("TERT MI2S RX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("QUAT MI2S RX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("QUIN MI2S RX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("PRI MI2S TX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("SEC MI2S TX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("TERT MI2S TX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("QUAT MI2S TX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("QUIN MI2S TX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("SENARY MI2S TX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("INT5 MI2S TX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+};
+
+static const struct snd_kcontrol_new mi2s_vi_feed_controls[] = {
+ SOC_ENUM_EXT("INT5 MI2S VI MONO", mi2s_config_enum[1],
+ msm_dai_q6_mi2s_vi_feed_mono_get,
+ msm_dai_q6_mi2s_vi_feed_mono_put),
+};
+
+static int msm_dai_q6_dai_mi2s_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_mi2s_pdata *mi2s_pdata =
+ (struct msm_mi2s_pdata *) dai->dev->platform_data;
+ struct snd_kcontrol *kcontrol = NULL;
+ int rc = 0;
+ const struct snd_kcontrol_new *ctrl = NULL;
+ const struct snd_kcontrol_new *vi_feed_ctrl = NULL;
+
+ dai->id = mi2s_pdata->intf_id;
+
+ if (mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode) {
+ if (dai->id == MSM_PRIM_MI2S)
+ ctrl = &mi2s_config_controls[0];
+ if (dai->id == MSM_SEC_MI2S)
+ ctrl = &mi2s_config_controls[1];
+ if (dai->id == MSM_TERT_MI2S)
+ ctrl = &mi2s_config_controls[2];
+ if (dai->id == MSM_QUAT_MI2S)
+ ctrl = &mi2s_config_controls[3];
+ if (dai->id == MSM_QUIN_MI2S)
+ ctrl = &mi2s_config_controls[4];
+ }
+
+ if (ctrl) {
+ kcontrol = snd_ctl_new1(ctrl,
+ &mi2s_dai_data->rx_dai.mi2s_dai_data);
+ rc = snd_ctl_add(dai->component->card->snd_card, kcontrol);
+
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: err add RX fmt ctl DAI = %s\n",
+ __func__, dai->name);
+ goto rtn;
+ }
+ }
+
+ ctrl = NULL;
+ if (mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode) {
+ if (dai->id == MSM_PRIM_MI2S)
+ ctrl = &mi2s_config_controls[5];
+ if (dai->id == MSM_SEC_MI2S)
+ ctrl = &mi2s_config_controls[6];
+ if (dai->id == MSM_TERT_MI2S)
+ ctrl = &mi2s_config_controls[7];
+ if (dai->id == MSM_QUAT_MI2S)
+ ctrl = &mi2s_config_controls[8];
+ if (dai->id == MSM_QUIN_MI2S)
+ ctrl = &mi2s_config_controls[9];
+ if (dai->id == MSM_SENARY_MI2S)
+ ctrl = &mi2s_config_controls[10];
+ if (dai->id == MSM_INT5_MI2S)
+ ctrl = &mi2s_config_controls[11];
+ }
+
+ if (ctrl) {
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(ctrl,
+ &mi2s_dai_data->tx_dai.mi2s_dai_data));
+
+ if (IS_ERR_VALUE(rc)) {
+ if (kcontrol)
+ snd_ctl_remove(dai->component->card->snd_card,
+ kcontrol);
+ dev_err(dai->dev, "%s: err add TX fmt ctl DAI = %s\n",
+ __func__, dai->name);
+ }
+ }
+
+ if (dai->id == MSM_INT5_MI2S)
+ vi_feed_ctrl = &mi2s_vi_feed_controls[0];
+
+ if (vi_feed_ctrl) {
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(vi_feed_ctrl,
+ &mi2s_dai_data->tx_dai.mi2s_dai_data));
+
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: err add TX vi feed channel ctl DAI = %s\n",
+ __func__, dai->name);
+ }
+ }
+
+ rc = msm_dai_q6_dai_add_route(dai);
+rtn:
+ return rc;
+}
+
+
+static int msm_dai_q6_dai_mi2s_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ int rc;
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask)) {
+ rc = afe_close(MI2S_RX); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close MI2S_RX port\n");
+ clear_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask);
+ }
+ if (test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) {
+ rc = afe_close(MI2S_TX); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close MI2S_TX port\n");
+ clear_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask);
+ }
+ kfree(mi2s_dai_data);
+ return 0;
+}
+
+static int msm_dai_q6_mi2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+
+ return 0;
+}
+
+
+static int msm_mi2s_get_port_id(u32 mi2s_id, int stream, u16 *port_id)
+{
+ int ret = 0;
+
+ switch (stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ switch (mi2s_id) {
+ case MSM_PRIM_MI2S:
+ *port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+ break;
+ case MSM_SEC_MI2S:
+ *port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
+ break;
+ case MSM_TERT_MI2S:
+ *port_id = AFE_PORT_ID_TERTIARY_MI2S_RX;
+ break;
+ case MSM_QUAT_MI2S:
+ *port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX;
+ break;
+ case MSM_SEC_MI2S_SD1:
+ *port_id = AFE_PORT_ID_SECONDARY_MI2S_RX_SD1;
+ break;
+ case MSM_QUIN_MI2S:
+ *port_id = AFE_PORT_ID_QUINARY_MI2S_RX;
+ break;
+ case MSM_INT0_MI2S:
+ *port_id = AFE_PORT_ID_INT0_MI2S_RX;
+ break;
+ case MSM_INT1_MI2S:
+ *port_id = AFE_PORT_ID_INT1_MI2S_RX;
+ break;
+ case MSM_INT2_MI2S:
+ *port_id = AFE_PORT_ID_INT2_MI2S_RX;
+ break;
+ case MSM_INT3_MI2S:
+ *port_id = AFE_PORT_ID_INT3_MI2S_RX;
+ break;
+ case MSM_INT4_MI2S:
+ *port_id = AFE_PORT_ID_INT4_MI2S_RX;
+ break;
+ case MSM_INT5_MI2S:
+ *port_id = AFE_PORT_ID_INT5_MI2S_RX;
+ break;
+ case MSM_INT6_MI2S:
+ *port_id = AFE_PORT_ID_INT6_MI2S_RX;
+ break;
+ default:
+ pr_err("%s: playback err id 0x%x\n",
+ __func__, mi2s_id);
+ ret = -1;
+ break;
+ }
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ switch (mi2s_id) {
+ case MSM_PRIM_MI2S:
+ *port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+ break;
+ case MSM_SEC_MI2S:
+ *port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+ break;
+ case MSM_TERT_MI2S:
+ *port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+ break;
+ case MSM_QUAT_MI2S:
+ *port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+ break;
+ case MSM_QUIN_MI2S:
+ *port_id = AFE_PORT_ID_QUINARY_MI2S_TX;
+ break;
+ case MSM_SENARY_MI2S:
+ *port_id = AFE_PORT_ID_SENARY_MI2S_TX;
+ break;
+ case MSM_INT0_MI2S:
+ *port_id = AFE_PORT_ID_INT0_MI2S_TX;
+ break;
+ case MSM_INT1_MI2S:
+ *port_id = AFE_PORT_ID_INT1_MI2S_TX;
+ break;
+ case MSM_INT2_MI2S:
+ *port_id = AFE_PORT_ID_INT2_MI2S_TX;
+ break;
+ case MSM_INT3_MI2S:
+ *port_id = AFE_PORT_ID_INT3_MI2S_TX;
+ break;
+ case MSM_INT4_MI2S:
+ *port_id = AFE_PORT_ID_INT4_MI2S_TX;
+ break;
+ case MSM_INT5_MI2S:
+ *port_id = AFE_PORT_ID_INT5_MI2S_TX;
+ break;
+ case MSM_INT6_MI2S:
+ *port_id = AFE_PORT_ID_INT6_MI2S_TX;
+ break;
+ default:
+ pr_err("%s: capture err id 0x%x\n", __func__, mi2s_id);
+ ret = -1;
+ break;
+ }
+ break;
+ default:
+ pr_err("%s: default err %d\n", __func__, stream);
+ ret = -1;
+ break;
+ }
+ pr_debug("%s: port_id = 0x%x\n", __func__, *port_id);
+ return ret;
+}
+
+static int msm_dai_q6_mi2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_dai_data *dai_data =
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ &mi2s_dai_data->rx_dai.mi2s_dai_data :
+ &mi2s_dai_data->tx_dai.mi2s_dai_data);
+ u16 port_id = 0;
+ int rc = 0;
+
+ if (msm_mi2s_get_port_id(dai->id, substream->stream,
+ &port_id) != 0) {
+ dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n",
+ __func__, port_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "%s: dai id %d, afe port id = 0x%x\n"
+ "dai_data->channels = %u sample_rate = %u\n", __func__,
+ dai->id, port_id, dai_data->channels, dai_data->rate);
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ /* PORT START should be set if prepare called
+ * in active state.
+ */
+ rc = afe_port_start(port_id, &dai_data->port_config,
+ dai_data->rate);
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE port 0x%x\n",
+ dai->id);
+ else
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) {
+ set_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+ dev_dbg(dai->dev, "%s: set hwfree_status to started\n",
+ __func__);
+ }
+ return rc;
+}
+
+static int msm_dai_q6_mi2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_mi2s_dai_config *mi2s_dai_config =
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ &mi2s_dai_data->rx_dai : &mi2s_dai_data->tx_dai);
+ struct msm_dai_q6_dai_data *dai_data = &mi2s_dai_config->mi2s_dai_data;
+ struct afe_param_id_i2s_cfg *i2s = &dai_data->port_config.i2s;
+
+ dai_data->channels = params_channels(params);
+ switch (dai_data->channels) {
+ case 8:
+ case 7:
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_8CHS)
+ goto error_invalid_data;
+ dai_data->port_config.i2s.channel_mode = AFE_PORT_I2S_8CHS;
+ break;
+ case 6:
+ case 5:
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_6CHS)
+ goto error_invalid_data;
+ dai_data->port_config.i2s.channel_mode = AFE_PORT_I2S_6CHS;
+ break;
+ case 4:
+ case 3:
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_QUAD01)
+ goto error_invalid_data;
+ if (mi2s_dai_config->pdata_mi2s_lines == AFE_PORT_I2S_QUAD23)
+ dai_data->port_config.i2s.channel_mode =
+ mi2s_dai_config->pdata_mi2s_lines;
+ else
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_QUAD01;
+ break;
+ case 2:
+ case 1:
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_SD0)
+ goto error_invalid_data;
+ switch (mi2s_dai_config->pdata_mi2s_lines) {
+ case AFE_PORT_I2S_SD0:
+ case AFE_PORT_I2S_SD1:
+ case AFE_PORT_I2S_SD2:
+ case AFE_PORT_I2S_SD3:
+ dai_data->port_config.i2s.channel_mode =
+ mi2s_dai_config->pdata_mi2s_lines;
+ break;
+ case AFE_PORT_I2S_QUAD01:
+ case AFE_PORT_I2S_6CHS:
+ case AFE_PORT_I2S_8CHS:
+ if (dai_data->vi_feed_mono == SPKR_1)
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD0;
+ else
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD1;
+ break;
+ case AFE_PORT_I2S_QUAD23:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD2;
+ break;
+ }
+ if (dai_data->channels == 2)
+ dai_data->port_config.i2s.mono_stereo =
+ MSM_AFE_CH_STEREO;
+ else
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+ break;
+ default:
+ pr_err("%s: default err channels %d\n",
+ __func__, dai_data->channels);
+ goto error_invalid_data;
+ }
+ dai_data->rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_SPECIAL:
+ dai_data->port_config.i2s.bit_width = 16;
+ dai_data->bitwidth = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ dai_data->port_config.i2s.bit_width = 24;
+ dai_data->bitwidth = 24;
+ break;
+ default:
+ pr_err("%s: format %d\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ dai_data->port_config.i2s.i2s_cfg_minor_version =
+ AFE_API_VERSION_I2S_CONFIG;
+ dai_data->port_config.i2s.sample_rate = dai_data->rate;
+ if ((test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) &&
+ test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.hwfree_status)) ||
+ (test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask) &&
+ test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.hwfree_status))) {
+ if ((mi2s_dai_data->tx_dai.mi2s_dai_data.rate !=
+ mi2s_dai_data->rx_dai.mi2s_dai_data.rate) ||
+ (mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth !=
+ mi2s_dai_data->tx_dai.mi2s_dai_data.bitwidth)) {
+ dev_err(dai->dev, "%s: Error mismatch in HW params\n"
+ "Tx sample_rate = %u bit_width = %hu\n"
+ "Rx sample_rate = %u bit_width = %hu\n"
+ , __func__,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.rate,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.bitwidth,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.rate,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth);
+ return -EINVAL;
+ }
+ }
+ dev_dbg(dai->dev, "%s: dai id %d dai_data->channels = %d\n"
+ "sample_rate = %u i2s_cfg_minor_version = 0x%x\n"
+ "bit_width = %hu channel_mode = 0x%x mono_stereo = %#x\n"
+ "ws_src = 0x%x sample_rate = %u data_format = 0x%x\n"
+ "reserved = %u\n", __func__, dai->id, dai_data->channels,
+ dai_data->rate, i2s->i2s_cfg_minor_version, i2s->bit_width,
+ i2s->channel_mode, i2s->mono_stereo, i2s->ws_src,
+ i2s->sample_rate, i2s->data_format, i2s->reserved);
+
+ return 0;
+
+error_invalid_data:
+ pr_err("%s: dai_data->channels = %d channel_mode = %d\n", __func__,
+ dai_data->channels, dai_data->port_config.i2s.channel_mode);
+ return -EINVAL;
+}
+
+
+static int msm_dai_q6_mi2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+
+ if (test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) ||
+ test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) {
+ dev_err(dai->dev, "%s: err chg i2s mode while dai running",
+ __func__);
+ return -EPERM;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1;
+ mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0;
+ mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0;
+ break;
+ default:
+ pr_err("%s: fmt %d\n",
+ __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_dai_q6_mi2s_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_dai_data *dai_data =
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ &mi2s_dai_data->rx_dai.mi2s_dai_data :
+ &mi2s_dai_data->tx_dai.mi2s_dai_data);
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) {
+ clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+ dev_dbg(dai->dev, "%s: clear hwfree_status\n", __func__);
+ }
+ return 0;
+}
+
+static void msm_dai_q6_mi2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_dai_data *dai_data =
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ &mi2s_dai_data->rx_dai.mi2s_dai_data :
+ &mi2s_dai_data->tx_dai.mi2s_dai_data);
+ u16 port_id = 0;
+ int rc = 0;
+
+ if (msm_mi2s_get_port_id(dai->id, substream->stream,
+ &port_id) != 0) {
+ dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n",
+ __func__, port_id);
+ }
+
+ dev_dbg(dai->dev, "%s: closing afe port id = 0x%x\n",
+ __func__, port_id);
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(port_id);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+ if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status))
+ clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_mi2s_ops = {
+ .startup = msm_dai_q6_mi2s_startup,
+ .prepare = msm_dai_q6_mi2s_prepare,
+ .hw_params = msm_dai_q6_mi2s_hw_params,
+ .hw_free = msm_dai_q6_mi2s_hw_free,
+ .set_fmt = msm_dai_q6_mi2s_set_fmt,
+ .shutdown = msm_dai_q6_mi2s_shutdown,
+};
+
+/* Channel min and max are initialized base on platform data */
+static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = {
+ {
+ .playback = {
+ .stream_name = "Primary MI2S Playback",
+ .aif_name = "PRI_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "Primary MI2S Capture",
+ .aif_name = "PRI_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_PRIM_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary MI2S Playback",
+ .aif_name = "SEC_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "Secondary MI2S Capture",
+ .aif_name = "SEC_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_SEC_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary MI2S Playback",
+ .aif_name = "TERT_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "Tertiary MI2S Capture",
+ .aif_name = "TERT_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_TERT_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary MI2S Playback",
+ .aif_name = "QUAT_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "Quaternary MI2S Capture",
+ .aif_name = "QUAT_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_QUAT_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quinary MI2S Playback",
+ .aif_name = "QUIN_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "Quinary MI2S Capture",
+ .aif_name = "QUIN_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_QUIN_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary MI2S Playback SD1",
+ .aif_name = "SEC_MI2S_RX_SD1",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .id = MSM_SEC_MI2S_SD1,
+ },
+ {
+ .capture = {
+ .stream_name = "Senary_mi2s Capture",
+ .aif_name = "SENARY_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_SENARY_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "INT0 MI2S Playback",
+ .aif_name = "INT0_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "INT0 MI2S Capture",
+ .aif_name = "INT0_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_INT0_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "INT1 MI2S Playback",
+ .aif_name = "INT1_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "INT1 MI2S Capture",
+ .aif_name = "INT1_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_INT1_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "INT2 MI2S Playback",
+ .aif_name = "INT2_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "INT2 MI2S Capture",
+ .aif_name = "INT2_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_INT2_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "INT3 MI2S Playback",
+ .aif_name = "INT3_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "INT3 MI2S Capture",
+ .aif_name = "INT3_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_INT3_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "INT4 MI2S Playback",
+ .aif_name = "INT4_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "INT4 MI2S Capture",
+ .aif_name = "INT4_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_INT4_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "INT5 MI2S Playback",
+ .aif_name = "INT5_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "INT5 MI2S Capture",
+ .aif_name = "INT5_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_INT5_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "INT6 MI2S Playback",
+ .aif_name = "INT6_MI2S_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "INT6 MI2S Capture",
+ .aif_name = "INT6_MI2S_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .id = MSM_INT6_MI2S,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+ },
+};
+
+static int msm_dai_q6_group_mi2s_set_clk_param(u32 group_id,
+ struct afe_clk_set *mclk_set,
+ struct afe_clk_set *bclk_set,
+ u32 sync_mode)
+{
+ switch (group_id) {
+ case AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_RX:
+ case AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_TX:
+ if (sync_mode)
+ bclk_set->clk_id = Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT;
+ else
+ bclk_set->clk_id = Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT;
+ mclk_set->clk_id = Q6AFE_LPASS_CLK_ID_MCLK_1;
+ break;
+ case AFE_GROUP_DEVICE_ID_TERTIARY_MI2S_RX:
+ case AFE_GROUP_DEVICE_ID_TERTIARY_MI2S_TX:
+ if (sync_mode)
+ bclk_set->clk_id = Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT;
+ else
+ bclk_set->clk_id = Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT;
+ mclk_set->clk_id = Q6AFE_LPASS_CLK_ID_MCLK_2;
+ break;
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_MI2S_RX:
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_MI2S_TX:
+ if (sync_mode)
+ bclk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT;
+ else
+ bclk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT;
+ mclk_set->clk_id = Q6AFE_LPASS_CLK_ID_MCLK_3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int msm_dai_q6_group_mi2s_set_clk(
+ struct msm_dai_q6_group_mi2s_dai_data *dai_data,
+ u16 port_id, bool enable)
+{
+ int rc = 0;
+
+ dai_data->bclk_set.enable = enable;
+ rc = afe_set_lpass_clock_v2(port_id,
+ &dai_data->bclk_set);
+ if (rc < 0)
+ pr_err("%s: afe lpass bclk failed, err: %d\n",
+ __func__, rc);
+
+ /*non zero mclk freq value means to enable mclk for mi2s intf*/
+ if (dai_data->mclk_set.clk_freq_in_hz != 0) {
+ dai_data->mclk_set.enable = enable;
+ rc = afe_set_lpass_clock_v2(port_id,
+ &dai_data->mclk_set);
+ if (rc < 0)
+ pr_err("%s: afe lpass mclk failed, err: %d\n",
+ __func__, rc);
+ }
+ return rc;
+}
+
+static int msm_dai_q6_dai_group_mi2s_probe(struct snd_soc_dai *dai)
+{
+ int rc = 0;
+
+ msm_dai_q6_set_dai_id(dai);
+ rc = msm_dai_q6_dai_add_route(dai);
+ return rc;
+}
+
+static int msm_dai_q6_dai_group_mi2s_remove(struct snd_soc_dai *dai)
+{
+ int rc = 0;
+ struct msm_dai_q6_group_mi2s_dai_data *dai_data =
+ dev_get_drvdata(dai->dev);
+ u16 group_id = dai_data->group_cfg.i2s_cfg.group_id;
+ int group_idx = 0;
+ atomic_t *group_ref = NULL;
+
+ dev_dbg(dai->dev, "%s rc %d", __func__, rc);
+
+ group_idx = msm_dai_q6_get_mi2s_group_idx(dai->id);
+ if (group_idx < 0 || group_idx > IDX_GROUP_MI2S_MAX) {
+ dev_err(dai->dev, "%s: port id 0x%x not supported\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ group_ref = &group_mi2s_ref[group_idx];
+
+ /* if AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(dai->id); /* can block */
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to close Group MI2S port 0x%x\n",
+ __func__, dai->id);
+ }
+ atomic_dec(group_ref);
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+
+ if (atomic_read(group_ref) == 0) {
+ rc = afe_port_group_mi2s_enable(group_id, NULL, false);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to disable Group MI2S group 0x%x\n",
+ __func__, group_id);
+ }
+ rc = msm_dai_q6_group_mi2s_set_clk(dai_data,
+ dai->id, false);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n",
+ __func__, dai->id);
+ }
+ }
+ }
+ return 0;
+}
+
+static int msm_dai_q6_group_mi2s_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ int rc = 0;
+ struct msm_dai_q6_group_mi2s_dai_data *dai_data =
+ dev_get_drvdata(dai->dev);
+ int i = 0;
+
+ switch (dai->id) {
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ if (!rx_slot) {
+ dev_err(dai->dev, "%s: rx slot not found\n",
+ __func__);
+ return -EINVAL;
+ }
+ for (i = 0; i < rx_num; i++)
+ dai_data->port_cfg.slot_mapping.offset[i] = rx_slot[i];
+ for (i = rx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++)
+ dai_data->port_cfg.slot_mapping.offset[i] =
+ AFE_SLOT_MAPPING_OFFSET_INVALID;
+ dai_data->port_cfg.slot_mapping.num_channel = rx_num;
+ break;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ if (!tx_slot) {
+ dev_err(dai->dev, "%s: tx slot not found\n",
+ __func__);
+ return -EINVAL;
+ }
+ for (i = 0; i < tx_num; i++)
+ dai_data->port_cfg.slot_mapping.offset[i] = tx_slot[i];
+ for (i = tx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++)
+ dai_data->port_cfg.slot_mapping.offset[i] =
+ AFE_SLOT_MAPPING_OFFSET_INVALID;
+
+ dai_data->port_cfg.slot_mapping.num_channel = tx_num;
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_group_mi2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_group_mi2s_dai_data *dai_data =
+ dev_get_drvdata(dai->dev);
+ struct afe_param_id_group_device_i2s_cfg_v1 *group_mi2s_cfg =
+ &dai_data->group_cfg.i2s_cfg;
+ struct afe_param_id_i2s_cfg *dev_i2s_cfg =
+ &dai_data->port_cfg.i2s_cfg;
+ struct afe_param_id_slot_mapping_cfg *slot_mapping =
+ &dai_data->port_cfg.slot_mapping;
+
+ /* update group_mi2s group config param */
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_SPECIAL:
+ dai_data->bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ dai_data->bit_width = 24;
+ break;
+ default:
+ pr_err("%s: format %d\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ /* update group_mi2s config param */
+ group_mi2s_cfg->sample_rate = dai_data->rate;
+ group_mi2s_cfg->bit_width = dai_data->bit_width;
+ pr_debug("%s: Group MI2S GROUP 0x%x:\n"
+ "channel_mode = %d sample_rate = %d bit_width = %d\n",
+ __func__, group_mi2s_cfg->group_id,
+ group_mi2s_cfg->channel_mode,
+ group_mi2s_cfg->sample_rate,
+ group_mi2s_cfg->bit_width);
+ pr_debug("%s: Group MI2S GROUP 0x%x:\n"
+ "port_id[0]=0x%x port_id[1]=0x%x port_id[2]=0x%x port_id[3]=0x%x\n"
+ "port_id[4]=0x%x port_id[5]=0x%x port_id[6]=0x%x port_id[7]=0x%x\n",
+ __func__, group_mi2s_cfg->group_id,
+ group_mi2s_cfg->port_id[0],
+ group_mi2s_cfg->port_id[1],
+ group_mi2s_cfg->port_id[2],
+ group_mi2s_cfg->port_id[3],
+ group_mi2s_cfg->port_id[4],
+ group_mi2s_cfg->port_id[5],
+ group_mi2s_cfg->port_id[6],
+ group_mi2s_cfg->port_id[7]);
+ /*i2s port cfg*/
+ dev_i2s_cfg->i2s_cfg_minor_version =
+ AFE_API_VERSION_I2S_CONFIG;
+ dev_i2s_cfg->bit_width = group_mi2s_cfg->bit_width;
+ dev_i2s_cfg->mono_stereo = AFE_PORT_I2S_STEREO;
+ dev_i2s_cfg->sample_rate = group_mi2s_cfg->sample_rate;
+ dev_i2s_cfg->data_format = AFE_LINEAR_PCM_DATA;
+ dev_i2s_cfg->ws_src = dai_data->sync_mode;
+ pr_debug("%s: MI2S port:\n"
+ "dai id %d dai_data->channels = %d\n"
+ "sample_rate = %u i2s_cfg_minor_version = 0x%x\n"
+ "bit_width = %hu channel_mode = 0x%x mono_stereo = %#x\n"
+ "ws_src = 0x%x sample_rate = %u data_format = 0x%x\n"
+ "reserved = %u\n", __func__, dai->id, dai_data->channels,
+ dai_data->rate, dev_i2s_cfg->i2s_cfg_minor_version,
+ dev_i2s_cfg->bit_width, dev_i2s_cfg->channel_mode,
+ dev_i2s_cfg->mono_stereo, dev_i2s_cfg->ws_src,
+ dev_i2s_cfg->sample_rate, dev_i2s_cfg->data_format,
+ dev_i2s_cfg->reserved);
+
+ /*slot mapping cfg*/
+ slot_mapping->minor_version =
+ AFE_API_VERSION_SLOT_MAPPING_CONFIG;
+ slot_mapping->bitwidth = group_mi2s_cfg->bit_width;
+ slot_mapping->data_align_type =
+ AFE_SLOT_MAPPING_DATA_ALIGN_MSB;
+ pr_debug("%s: Group MI2S port 0x%x SLOT MAPPING:\n"
+ "num_channel=%d bitwidth=%d data_align=0x%x\n",
+ __func__, dai->id,
+ slot_mapping->num_channel,
+ slot_mapping->bitwidth,
+ slot_mapping->data_align_type);
+ pr_debug("%s: Group MI2S port 0x%x SLOT MAPPING:\n"
+ "offset[0]=0x%x offset[1]=0x%x offset[2]=0x%x offset[3]=0x%x\n"
+ "offset[4]=0x%x offset[5]=0x%x offset[6]=0x%x offset[7]=0x%x\n",
+ __func__, dai->id,
+ slot_mapping->offset[0],
+ slot_mapping->offset[1],
+ slot_mapping->offset[2],
+ slot_mapping->offset[3],
+ slot_mapping->offset[4],
+ slot_mapping->offset[5],
+ slot_mapping->offset[6],
+ slot_mapping->offset[7]);
+
+ return 0;
+}
+
+static int msm_dai_q6_group_mi2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int rc = 0;
+ struct msm_dai_q6_group_mi2s_dai_data *dai_data =
+ dev_get_drvdata(dai->dev);
+ u16 group_id = dai_data->group_cfg.i2s_cfg.group_id;
+ int group_idx = 0;
+ atomic_t *group_ref = NULL;
+
+ group_idx = msm_dai_q6_get_mi2s_group_idx(dai->id);
+ if (group_idx < 0 || group_idx > IDX_GROUP_MI2S_MAX) {
+ dev_err(dai->dev, "%s port id 0x%x not supported\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&group_mi2s_mutex);
+
+ group_ref = &group_mi2s_ref[group_idx];
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ /* PORT START should be set if prepare called
+ * in active state.
+ */
+ if (atomic_read(group_ref) == 0) {
+ /*
+ * TX and RX share the same clk.
+ * AFE clk is enabled per group to simplify the logic.
+ * DSP will monitor the clk count.
+ */
+ rc = msm_dai_q6_group_mi2s_set_clk(dai_data,
+ dai->id, true);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to enable AFE clk 0x%x\n",
+ __func__, dai->id);
+ goto rtn;
+ }
+
+ /*
+ * if only one port, don't do group enable as there
+ * is no group need for only one port
+ */
+ if (dai_data->num_group_ports > 1) {
+ rc = afe_port_group_mi2s_enable(group_id,
+ &dai_data->group_cfg, true);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev,
+ "%s: fail to enable AFE group 0x%x\n",
+ __func__, group_id);
+ goto rtn;
+ }
+ }
+ }
+
+ rc = afe_i2s_port_start(dai->id, &dai_data->port_cfg,
+ dai_data->rate, dai_data->num_group_ports);
+ if (IS_ERR_VALUE(rc)) {
+ if (atomic_read(group_ref) == 0) {
+ afe_port_group_mi2s_enable(group_id,
+ NULL, false);
+ msm_dai_q6_group_mi2s_set_clk(dai_data,
+ dai->id, false);
+ }
+ dev_err(dai->dev, "%s: fail to open AFE port 0x%x\n",
+ __func__, dai->id);
+ } else {
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ atomic_inc(group_ref);
+ }
+ }
+
+rtn:
+ mutex_unlock(&group_mi2s_mutex);
+ return rc;
+}
+
+static void msm_dai_q6_group_mi2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int rc = 0;
+ struct msm_dai_q6_group_mi2s_dai_data *dai_data =
+ dev_get_drvdata(dai->dev);
+ u16 group_id = dai_data->group_cfg.i2s_cfg.group_id;
+ int group_idx = 0;
+ atomic_t *group_ref = NULL;
+
+ group_idx = msm_dai_q6_get_mi2s_group_idx(dai->id);
+ if (group_idx < 0 || group_idx > IDX_GROUP_MI2S_MAX) {
+ dev_err(dai->dev, "%s port id 0x%x not supported\n",
+ __func__, dai->id);
+ return;
+ }
+
+ mutex_lock(&group_mi2s_mutex);
+ group_ref = &group_mi2s_ref[group_idx];
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(dai->id);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to close AFE port 0x%x\n",
+ __func__, dai->id);
+ }
+ atomic_dec(group_ref);
+ clear_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+
+ if (atomic_read(group_ref) == 0) {
+ rc = afe_port_group_mi2s_enable(group_id,
+ NULL, false);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to disable AFE group 0x%x\n",
+ __func__, group_id);
+ }
+ rc = msm_dai_q6_group_mi2s_set_clk(dai_data,
+ dai->id, false);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n",
+ __func__, dai->id);
+ }
+ }
+ }
+
+ mutex_unlock(&group_mi2s_mutex);
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_group_mi2s_ops = {
+ .prepare = msm_dai_q6_group_mi2s_prepare,
+ .hw_params = msm_dai_q6_group_mi2s_hw_params,
+ .set_channel_map = msm_dai_q6_group_mi2s_set_channel_map,
+ .shutdown = msm_dai_q6_group_mi2s_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_group_mi2s_dai[] = {
+ {
+ .playback = {
+ .stream_name = "Secondary MI2S1 Playback",
+ .aif_name = "SEC_MI2S_RX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_SECONDARY_MI2S_RX_1,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary MI2S2 Playback",
+ .aif_name = "SEC_MI2S_RX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_SECONDARY_MI2S_RX_2,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary MI2S3 Playback",
+ .aif_name = "SEC_MI2S_RX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_SECONDARY_MI2S_RX_3,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary MI2S4 Playback",
+ .aif_name = "SEC_MI2S_RX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_SECONDARY_MI2S_RX_4,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary MI2S1 Capture",
+ .aif_name = "SEC_MI2S_TX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_SECONDARY_MI2S_TX_1,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary MI2S2 Capture",
+ .aif_name = "SEC_MI2S_TX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_SECONDARY_MI2S_TX_2,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary MI2S3 Capture",
+ .aif_name = "SEC_MI2S_TX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_SECONDARY_MI2S_TX_3,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary MI2S4 Capture",
+ .aif_name = "SEC_MI2S_TX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_SECONDARY_MI2S_TX_4,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary MI2S1 Playback",
+ .aif_name = "TERT_MI2S_RX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_TERTIARY_MI2S_RX_1,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary MI2S2 Playback",
+ .aif_name = "TERT_MI2S_RX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_TERTIARY_MI2S_RX_2,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary MI2S3 Playback",
+ .aif_name = "TERT_MI2S_RX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_TERTIARY_MI2S_RX_3,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary MI2S4 Playback",
+ .aif_name = "TERT_MI2S_RX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_TERTIARY_MI2S_RX_4,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary MI2S1 Capture",
+ .aif_name = "TERT_MI2S_TX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_TERTIARY_MI2S_TX_1,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary MI2S2 Capture",
+ .aif_name = "TERT_MI2S_TX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_TERTIARY_MI2S_TX_2,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary MI2S3 Capture",
+ .aif_name = "TERT_MI2S_TX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_TERTIARY_MI2S_TX_3,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary MI2S4 Capture",
+ .aif_name = "TERT_MI2S_TX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_TERTIARY_MI2S_TX_4,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary MI2S1 Playback",
+ .aif_name = "QUAT_MI2S_RX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_QUATERNARY_MI2S_RX_1,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary MI2S2 Playback",
+ .aif_name = "QUAT_MI2S_RX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_QUATERNARY_MI2S_RX_2,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary MI2S3 Playback",
+ .aif_name = "QUAT_MI2S_RX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_QUATERNARY_MI2S_RX_3,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary MI2S4 Playback",
+ .aif_name = "QUAT_MI2S_RX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_QUATERNARY_MI2S_RX_4,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary MI2S1 Capture",
+ .aif_name = "QUAT_MI2S_TX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_QUATERNARY_MI2S_TX_1,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary MI2S2 Capture",
+ .aif_name = "QUAT_MI2S_TX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_QUATERNARY_MI2S_TX_2,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary MI2S3 Capture",
+ .aif_name = "QUAT_MI2S_TX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_QUATERNARY_MI2S_TX_3,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary MI2S4 Capture",
+ .aif_name = "QUAT_MI2S_TX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &msm_dai_q6_group_mi2s_ops,
+ .id = AFE_PORT_ID_QUATERNARY_MI2S_TX_4,
+ .probe = msm_dai_q6_dai_group_mi2s_probe,
+ .remove = msm_dai_q6_dai_group_mi2s_remove,
+ }
+};
+
+static int msm_dai_q6_mi2s_get_lineconfig(u16 sd_lines, u16 *config_ptr,
+ unsigned int *ch_cnt)
+{
+ u8 num_of_sd_lines;
+
+ num_of_sd_lines = num_of_bits_set(sd_lines);
+ switch (num_of_sd_lines) {
+ case 0:
+ pr_debug("%s: no line is assigned\n", __func__);
+ break;
+ case 1:
+ switch (sd_lines) {
+ case MSM_MI2S_SD0:
+ *config_ptr = AFE_PORT_I2S_SD0;
+ break;
+ case MSM_MI2S_SD1:
+ *config_ptr = AFE_PORT_I2S_SD1;
+ break;
+ case MSM_MI2S_SD2:
+ *config_ptr = AFE_PORT_I2S_SD2;
+ break;
+ case MSM_MI2S_SD3:
+ *config_ptr = AFE_PORT_I2S_SD3;
+ break;
+ default:
+ pr_err("%s: invalid SD lines %d\n",
+ __func__, sd_lines);
+ goto error_invalid_data;
+ }
+ break;
+ case 2:
+ switch (sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1:
+ *config_ptr = AFE_PORT_I2S_QUAD01;
+ break;
+ case MSM_MI2S_SD2 | MSM_MI2S_SD3:
+ *config_ptr = AFE_PORT_I2S_QUAD23;
+ break;
+ default:
+ pr_err("%s: invalid SD lines %d\n",
+ __func__, sd_lines);
+ goto error_invalid_data;
+ }
+ break;
+ case 3:
+ switch (sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2:
+ *config_ptr = AFE_PORT_I2S_6CHS;
+ break;
+ default:
+ pr_err("%s: invalid SD lines %d\n",
+ __func__, sd_lines);
+ goto error_invalid_data;
+ }
+ break;
+ case 4:
+ switch (sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 | MSM_MI2S_SD3:
+ *config_ptr = AFE_PORT_I2S_8CHS;
+ break;
+ default:
+ pr_err("%s: invalid SD lines %d\n",
+ __func__, sd_lines);
+ goto error_invalid_data;
+ }
+ break;
+ default:
+ pr_err("%s: invalid SD lines %d\n", __func__, num_of_sd_lines);
+ goto error_invalid_data;
+ }
+ *ch_cnt = num_of_sd_lines;
+ return 0;
+
+error_invalid_data:
+ pr_err("%s: invalid data\n", __func__);
+ return -EINVAL;
+}
+
+static int msm_dai_q6_mi2s_platform_data_validation(
+ struct platform_device *pdev, struct snd_soc_dai_driver *dai_driver)
+{
+ struct msm_dai_q6_mi2s_dai_data *dai_data = dev_get_drvdata(&pdev->dev);
+ struct msm_mi2s_pdata *mi2s_pdata =
+ (struct msm_mi2s_pdata *) pdev->dev.platform_data;
+ unsigned int ch_cnt;
+ int rc = 0;
+ u16 sd_line;
+
+ if (mi2s_pdata == NULL) {
+ pr_err("%s: mi2s_pdata NULL", __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->rx_sd_lines,
+ &sd_line, &ch_cnt);
+
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(&pdev->dev, "invalid MI2S RX sd line config\n");
+ goto rtn;
+ }
+
+ if (ch_cnt) {
+ dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
+ sd_line;
+ dai_data->rx_dai.pdata_mi2s_lines = sd_line;
+ dai_driver->playback.channels_min = 1;
+ dai_driver->playback.channels_max = ch_cnt << 1;
+ } else {
+ dai_driver->playback.channels_min = 0;
+ dai_driver->playback.channels_max = 0;
+ }
+ rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->tx_sd_lines,
+ &sd_line, &ch_cnt);
+
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(&pdev->dev, "invalid MI2S TX sd line config\n");
+ goto rtn;
+ }
+
+ if (ch_cnt) {
+ dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
+ sd_line;
+ dai_data->tx_dai.pdata_mi2s_lines = sd_line;
+ dai_driver->capture.channels_min = 1;
+ dai_driver->capture.channels_max = ch_cnt << 1;
+ } else {
+ dai_driver->capture.channels_min = 0;
+ dai_driver->capture.channels_max = 0;
+ }
+
+ dev_dbg(&pdev->dev, "%s: playback sdline 0x%x capture sdline 0x%x\n",
+ __func__, dai_data->rx_dai.pdata_mi2s_lines,
+ dai_data->tx_dai.pdata_mi2s_lines);
+ dev_dbg(&pdev->dev, "%s: playback ch_max %d capture ch_mx %d\n",
+ __func__, dai_driver->playback.channels_max,
+ dai_driver->capture.channels_max);
+rtn:
+ return rc;
+}
+
+static const struct snd_soc_component_driver msm_q6_mi2s_dai_component = {
+ .name = "msm-dai-q6-mi2s",
+};
+static int msm_dai_q6_mi2s_dev_probe(struct platform_device *pdev)
+{
+ struct msm_dai_q6_mi2s_dai_data *dai_data;
+ const char *q6_mi2s_dev_id = "qcom,msm-dai-q6-mi2s-dev-id";
+ u32 tx_line = 0;
+ u32 rx_line = 0;
+ u32 mi2s_intf = 0;
+ struct msm_mi2s_pdata *mi2s_pdata;
+ int rc;
+ char boot_marker[40];
+
+ rc = of_property_read_u32(pdev->dev.of_node, q6_mi2s_dev_id,
+ &mi2s_intf);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: missing 0x%x in dt node\n", __func__, mi2s_intf);
+ goto rtn;
+ }
+
+ snprintf(boot_marker, sizeof(boot_marker),
+ "M - DRIVER MSM I2S_%d Init", mi2s_intf);
+ place_marker(boot_marker);
+
+ dev_dbg(&pdev->dev, "dev name %s dev id 0x%x\n", dev_name(&pdev->dev),
+ mi2s_intf);
+
+ if ((mi2s_intf < MSM_MI2S_MIN || mi2s_intf > MSM_MI2S_MAX)
+ || (mi2s_intf >= ARRAY_SIZE(msm_dai_q6_mi2s_dai))) {
+ dev_err(&pdev->dev,
+ "%s: Invalid MI2S ID %u from Device Tree\n",
+ __func__, mi2s_intf);
+ rc = -ENXIO;
+ goto rtn;
+ }
+
+ pdev->id = mi2s_intf;
+
+ mi2s_pdata = kzalloc(sizeof(struct msm_mi2s_pdata), GFP_KERNEL);
+ if (!mi2s_pdata) {
+ dev_err(&pdev->dev, "fail to allocate mi2s_pdata data\n");
+ rc = -ENOMEM;
+ goto rtn;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-rx-lines",
+ &rx_line);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Rx line from DT file %s\n", __func__,
+ "qcom,msm-mi2s-rx-lines");
+ goto free_pdata;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-tx-lines",
+ &tx_line);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Tx line from DT file %s\n", __func__,
+ "qcom,msm-mi2s-tx-lines");
+ goto free_pdata;
+ }
+ dev_dbg(&pdev->dev, "dev name %s Rx line 0x%x , Tx ine 0x%x\n",
+ dev_name(&pdev->dev), rx_line, tx_line);
+ mi2s_pdata->rx_sd_lines = rx_line;
+ mi2s_pdata->tx_sd_lines = tx_line;
+ mi2s_pdata->intf_id = mi2s_intf;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_mi2s_dai_data),
+ GFP_KERNEL);
+ if (!dai_data) {
+ dev_err(&pdev->dev, "fail to allocate dai data\n");
+ rc = -ENOMEM;
+ goto free_pdata;
+ } else
+ dev_set_drvdata(&pdev->dev, dai_data);
+
+ pdev->dev.platform_data = mi2s_pdata;
+
+ rc = msm_dai_q6_mi2s_platform_data_validation(pdev,
+ &msm_dai_q6_mi2s_dai[mi2s_intf]);
+ if (IS_ERR_VALUE(rc))
+ goto free_dai_data;
+
+ rc = snd_soc_register_component(&pdev->dev, &msm_q6_mi2s_dai_component,
+ &msm_dai_q6_mi2s_dai[mi2s_intf], 1);
+
+ if (IS_ERR_VALUE(rc))
+ goto err_register;
+
+ snprintf(boot_marker, sizeof(boot_marker),
+ "M - DRIVER MSM I2S_%d Ready", mi2s_intf);
+ place_marker(boot_marker);
+
+ return 0;
+
+err_register:
+ dev_err(&pdev->dev, "fail to msm_dai_q6_mi2s_dev_probe\n");
+free_dai_data:
+ kfree(dai_data);
+free_pdata:
+ kfree(mi2s_pdata);
+rtn:
+ return rc;
+}
+
+static int msm_dai_q6_mi2s_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+ return 0;
+}
+
+static const struct snd_soc_component_driver msm_dai_q6_component = {
+ .name = "msm-dai-q6-dev",
+};
+
+static int msm_dai_q6_dev_probe(struct platform_device *pdev)
+{
+ int rc, id, i, len;
+ const char *q6_dev_id = "qcom,msm-dai-q6-dev-id";
+ char stream_name[80];
+
+ rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: missing %s in dt node\n", __func__, q6_dev_id);
+ return rc;
+ }
+
+ pdev->id = id;
+
+ pr_debug("%s: dev name %s, id:%d\n", __func__,
+ dev_name(&pdev->dev), pdev->id);
+
+ switch (id) {
+ case SLIMBUS_0_RX:
+ strlcpy(stream_name, "Slimbus Playback", 80);
+ goto register_slim_playback;
+ case SLIMBUS_2_RX:
+ strlcpy(stream_name, "Slimbus2 Playback", 80);
+ goto register_slim_playback;
+ case SLIMBUS_1_RX:
+ strlcpy(stream_name, "Slimbus1 Playback", 80);
+ goto register_slim_playback;
+ case SLIMBUS_3_RX:
+ strlcpy(stream_name, "Slimbus3 Playback", 80);
+ goto register_slim_playback;
+ case SLIMBUS_4_RX:
+ strlcpy(stream_name, "Slimbus4 Playback", 80);
+ goto register_slim_playback;
+ case SLIMBUS_5_RX:
+ strlcpy(stream_name, "Slimbus5 Playback", 80);
+ goto register_slim_playback;
+ case SLIMBUS_6_RX:
+ strlcpy(stream_name, "Slimbus6 Playback", 80);
+ goto register_slim_playback;
+ case SLIMBUS_7_RX:
+ strlcpy(stream_name, "Slimbus7 Playback", sizeof(stream_name));
+ goto register_slim_playback;
+ case SLIMBUS_8_RX:
+ strlcpy(stream_name, "Slimbus8 Playback", sizeof(stream_name));
+ goto register_slim_playback;
+register_slim_playback:
+ rc = -ENODEV;
+ len = strnlen(stream_name , 80);
+ for (i = 0; i < ARRAY_SIZE(msm_dai_q6_slimbus_rx_dai); i++) {
+ if (msm_dai_q6_slimbus_rx_dai[i].playback.stream_name &&
+ !strncmp(stream_name,
+ msm_dai_q6_slimbus_rx_dai[i] \
+ .playback.stream_name,
+ len)) {
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component, &msm_dai_q6_slimbus_rx_dai[i], 1);
+ break;
+ }
+ }
+ if (rc)
+ pr_err("%s: Device not found stream name %s\n",
+ __func__, stream_name);
+ break;
+ case SLIMBUS_0_TX:
+ strlcpy(stream_name, "Slimbus Capture", 80);
+ goto register_slim_capture;
+ case SLIMBUS_1_TX:
+ strlcpy(stream_name, "Slimbus1 Capture", 80);
+ goto register_slim_capture;
+ case SLIMBUS_2_TX:
+ strlcpy(stream_name, "Slimbus2 Capture", 80);
+ goto register_slim_capture;
+ case SLIMBUS_3_TX:
+ strlcpy(stream_name, "Slimbus3 Capture", 80);
+ goto register_slim_capture;
+ case SLIMBUS_4_TX:
+ strlcpy(stream_name, "Slimbus4 Capture", 80);
+ goto register_slim_capture;
+ case SLIMBUS_5_TX:
+ strlcpy(stream_name, "Slimbus5 Capture", 80);
+ goto register_slim_capture;
+ case SLIMBUS_6_TX:
+ strlcpy(stream_name, "Slimbus6 Capture", 80);
+ goto register_slim_capture;
+ case SLIMBUS_7_TX:
+ strlcpy(stream_name, "Slimbus7 Capture", sizeof(stream_name));
+ goto register_slim_capture;
+ case SLIMBUS_8_TX:
+ strlcpy(stream_name, "Slimbus8 Capture", sizeof(stream_name));
+ goto register_slim_capture;
+register_slim_capture:
+ rc = -ENODEV;
+ len = strnlen(stream_name , 80);
+ for (i = 0; i < ARRAY_SIZE(msm_dai_q6_slimbus_tx_dai); i++) {
+ if (msm_dai_q6_slimbus_tx_dai[i].capture.stream_name &&
+ !strncmp(stream_name,
+ msm_dai_q6_slimbus_tx_dai[i] \
+ .capture.stream_name,
+ len)) {
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component, &msm_dai_q6_slimbus_tx_dai[i], 1);
+ break;
+ }
+ }
+ if (rc)
+ pr_err("%s: Device not found stream name %s\n",
+ __func__, stream_name);
+ break;
+ case INT_BT_SCO_RX:
+ rc = snd_soc_register_component(&pdev->dev, &msm_dai_q6_component,
+ &msm_dai_q6_bt_sco_rx_dai, 1);
+ break;
+ case INT_BT_SCO_TX:
+ rc = snd_soc_register_component(&pdev->dev, &msm_dai_q6_component,
+ &msm_dai_q6_bt_sco_tx_dai, 1);
+ break;
+ case INT_BT_A2DP_RX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component, &msm_dai_q6_bt_a2dp_rx_dai, 1);
+ break;
+ case INT_FM_RX:
+ rc = snd_soc_register_component(&pdev->dev, &msm_dai_q6_component,
+ &msm_dai_q6_fm_rx_dai, 1);
+ break;
+ case INT_FM_TX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component, &msm_dai_q6_fm_tx_dai, 1);
+ break;
+ case AFE_PORT_ID_USB_RX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component,
+ &msm_dai_q6_usb_rx_dai, 1);
+ break;
+ case AFE_PORT_ID_USB_TX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component,
+ &msm_dai_q6_usb_tx_dai, 1);
+ break;
+ case RT_PROXY_DAI_001_RX:
+ strlcpy(stream_name, "AFE Playback", 80);
+ goto register_afe_playback;
+ case RT_PROXY_DAI_002_RX:
+ strlcpy(stream_name, "AFE-PROXY RX", 80);
+register_afe_playback:
+ rc = -ENODEV;
+ len = strnlen(stream_name , 80);
+ for (i = 0; i < ARRAY_SIZE(msm_dai_q6_afe_rx_dai); i++) {
+ if (msm_dai_q6_afe_rx_dai[i].playback.stream_name &&
+ !strncmp(stream_name,
+ msm_dai_q6_afe_rx_dai[i].playback.stream_name,
+ len)) {
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component, &msm_dai_q6_afe_rx_dai[i], 1);
+ break;
+ }
+ }
+ if (rc)
+ pr_err("%s: Device not found stream name %s\n",
+ __func__, stream_name);
+ break;
+ case RT_PROXY_DAI_001_TX:
+ strlcpy(stream_name, "AFE-PROXY TX", 80);
+ goto register_afe_capture;
+ case RT_PROXY_DAI_002_TX:
+ strlcpy(stream_name, "AFE Capture", 80);
+register_afe_capture:
+ rc = -ENODEV;
+ len = strnlen(stream_name , 80);
+ for (i = 0; i < ARRAY_SIZE(msm_dai_q6_afe_tx_dai); i++) {
+ if (msm_dai_q6_afe_tx_dai[i].capture.stream_name &&
+ !strncmp(stream_name,
+ msm_dai_q6_afe_tx_dai[i].capture.stream_name,
+ len)) {
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component, &msm_dai_q6_afe_tx_dai[i], 1);
+ break;
+ }
+ }
+ if (rc)
+ pr_err("%s: Device not found stream name %s\n",
+ __func__, stream_name);
+ break;
+ case VOICE_PLAYBACK_TX:
+ strlcpy(stream_name, "Voice Farend Playback", 80);
+ goto register_voice_playback;
+ case VOICE2_PLAYBACK_TX:
+ strlcpy(stream_name, "Voice2 Farend Playback", 80);
+register_voice_playback:
+ rc = -ENODEV;
+ len = strnlen(stream_name , 80);
+ for (i = 0; i < ARRAY_SIZE(msm_dai_q6_voc_playback_dai); i++) {
+ if (msm_dai_q6_voc_playback_dai[i].playback.stream_name
+ && !strcmp(stream_name,
+ msm_dai_q6_voc_playback_dai[i].playback.stream_name)) {
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component,
+ &msm_dai_q6_voc_playback_dai[i], 1);
+ break;
+ }
+ }
+ if (rc)
+ pr_err("%s Device not found stream name %s\n",
+ __func__, stream_name);
+ break;
+ case VOICE_RECORD_RX:
+ strlcpy(stream_name, "Voice Downlink Capture", 80);
+ goto register_uplink_capture;
+ case VOICE_RECORD_TX:
+ strlcpy(stream_name, "Voice Uplink Capture", 80);
+register_uplink_capture:
+ rc = -ENODEV;
+ len = strnlen(stream_name , 80);
+ for (i = 0; i < ARRAY_SIZE(msm_dai_q6_incall_record_dai); i++) {
+ if (msm_dai_q6_incall_record_dai[i].capture.stream_name &&
+ !strncmp(stream_name,
+ msm_dai_q6_incall_record_dai[i].capture.stream_name,
+ len)) {
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component, &msm_dai_q6_incall_record_dai[i], 1);
+ break;
+ }
+ }
+ if (rc)
+ pr_err("%s: Device not found stream name %s\n",
+ __func__, stream_name);
+ break;
+ case RT_PROXY_PORT_002_RX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component, &msm_dai_q6_proxy_rx_dai, 1);
+ break;
+ case RT_PROXY_PORT_002_TX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_q6_component, &msm_dai_q6_proxy_tx_dai, 1);
+ break;
+ default:
+ rc = -ENODEV;
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_dai_q6_dev_dt_match[] = {
+ { .compatible = "qcom,msm-dai-q6-dev", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_dev_dt_match);
+
+static struct platform_driver msm_dai_q6_dev = {
+ .probe = msm_dai_q6_dev_probe,
+ .remove = msm_dai_q6_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6-dev",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_q6_dev_dt_match,
+ },
+};
+
+static int msm_dai_q6_probe(struct platform_device *pdev)
+{
+ int rc;
+ pr_debug("%s: dev name %s, id:%d\n", __func__,
+ dev_name(&pdev->dev), pdev->id);
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+ __func__, rc);
+ } else
+ dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
+
+ return rc;
+}
+
+static int msm_dai_q6_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id msm_dai_q6_dt_match[] = {
+ { .compatible = "qcom,msm-dai-q6", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_dt_match);
+static struct platform_driver msm_dai_q6 = {
+ .probe = msm_dai_q6_probe,
+ .remove = msm_dai_q6_remove,
+ .driver = {
+ .name = "msm-dai-q6",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_q6_dt_match,
+ },
+};
+
+static int msm_dai_mi2s_q6_probe(struct platform_device *pdev)
+{
+ int rc;
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+ __func__, rc);
+ } else
+ dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
+ return rc;
+}
+
+static int msm_dai_mi2s_q6_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id msm_dai_mi2s_dt_match[] = {
+ { .compatible = "qcom,msm-dai-mi2s", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_mi2s_dt_match);
+
+static struct platform_driver msm_dai_mi2s_q6 = {
+ .probe = msm_dai_mi2s_q6_probe,
+ .remove = msm_dai_mi2s_q6_remove,
+ .driver = {
+ .name = "msm-dai-mi2s",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_mi2s_dt_match,
+ },
+};
+
+static const struct of_device_id msm_dai_q6_mi2s_dev_dt_match[] = {
+ { .compatible = "qcom,msm-dai-q6-mi2s", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_q6_mi2s_dev_dt_match);
+
+static struct platform_driver msm_dai_q6_mi2s_driver = {
+ .probe = msm_dai_q6_mi2s_dev_probe,
+ .remove = msm_dai_q6_mi2s_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6-mi2s",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_q6_mi2s_dev_dt_match,
+ },
+};
+
+static int msm_dai_group_mi2s_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ /* probe child node info */
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+ __func__, rc);
+ goto rtn;
+ } else
+ dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
+
+rtn:
+ return rc;
+}
+
+static int msm_dai_group_mi2s_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int msm_dai_q6_group_mi2s_dev_id_validation(u32 dev_id)
+{
+ switch (dev_id) {
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_component_driver msm_q6_group_mi2s_dai_component = {
+ .name = "msm-dai-q6-mi2s-group",
+};
+
+static int msm_dai_group_mi2s_dev_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct msm_dai_q6_group_mi2s_dai_data *dai_data = NULL;
+ u32 mi2s_dev_id = 0;
+ u32 ch_mode = 0;
+ int port_idx = 0;
+ struct device_node *mi2s_parent_node = NULL;
+ u32 num_group_mi2s_ports = 0;
+ const uint32_t *port_id_array = NULL;
+ u32 array_length, i = 0;
+ u32 group_idx = 0;
+ u32 sync_mode = 0;
+
+ mi2s_parent_node = of_get_parent(pdev->dev.of_node);
+ /* extract mi2s group info into static */
+ rc = of_property_read_u32(mi2s_parent_node,
+ "qcom,msm-cpudai-mi2s-group-id",
+ (u32 *)&group_mi2s_cfg.group_id);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Group ID from DT file %s\n",
+ __func__, "qcom,msm-cpudai-mi2s-group-id");
+ goto rtn;
+ }
+
+ rc = of_property_read_u32(mi2s_parent_node,
+ "qcom,msm-cpudai-mi2s-group-num-ports",
+ &num_group_mi2s_ports);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ch_mode from DT file %s\n",
+ __func__, "qcom,msm-cpudai-mi2s-group-num-ports");
+ goto rtn;
+ }
+
+ if (num_group_mi2s_ports > AFE_GROUP_DEVICE_NUM_PORTS) {
+ dev_err(&pdev->dev, "%s: Group Num Ports %d greater than Man %d\n",
+ __func__, num_group_mi2s_ports,
+ AFE_GROUP_DEVICE_NUM_PORTS);
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ port_id_array = of_get_property(mi2s_parent_node,
+ "qcom,msm-cpudai-mi2s-group-port-id",
+ &array_length);
+ if (port_id_array == NULL) {
+ dev_err(&pdev->dev, "%s: prot_id_array is not valid\n",
+ __func__);
+ rc = -EINVAL;
+ goto rtn;
+ }
+ if (array_length != sizeof(uint32_t) * num_group_mi2s_ports) {
+ dev_err(&pdev->dev, "%s array_length is %d, expected is %zd\n",
+ __func__, array_length,
+ sizeof(uint32_t) * num_group_mi2s_ports);
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ for (i = 0; i < num_group_mi2s_ports; i++)
+ group_mi2s_cfg.port_id[i] = (u16)be32_to_cpu(port_id_array[i]);
+ /* Unused index should be filled with AFE_PORT_INVALID */
+ for (i = num_group_mi2s_ports; i < AFE_GROUP_DEVICE_NUM_PORTS; i++)
+ group_mi2s_cfg.port_id[i] = AFE_PORT_INVALID;
+
+ /*extract group_mi2s group sd line info into static */
+ rc = of_property_read_u32(mi2s_parent_node,
+ "qcom,msm-cpudai-mi2s-channel-mode",
+ &ch_mode);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ch_mode from DT file %s\n",
+ __func__, "qcom,msm-cpudai-mi2s-channel-mode");
+ goto rtn;
+ }
+
+ group_mi2s_cfg.channel_mode = ch_mode;
+ group_mi2s_cfg.minor_version = AFE_API_VERSION_I2S_CONFIG;
+ group_idx = msm_dai_q6_get_mi2s_group_idx(group_mi2s_cfg.group_id);
+ if (group_idx < 0 || group_idx > IDX_GROUP_MI2S_MAX) {
+ dev_err(&pdev->dev, "%s: group id 0x%x not supported\n",
+ __func__, group_mi2s_cfg.group_id);
+ rc = -EINVAL;
+ goto rtn;
+ }
+ atomic_set(&group_mi2s_ref[group_idx], 0);
+
+ rc = of_property_read_u32(mi2s_parent_node,
+ "qcom,msm-cpudai-mi2s-sync-mode",
+ &sync_mode);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: ch_mode from DT file %s\n",
+ __func__, "qcom,msm-cpudai-mi2s-sync-mode");
+ goto rtn;
+ }
+
+ rc = of_property_read_u32(mi2s_parent_node,
+ "qcom,msm-cpudai-mi2s-mclk",
+ &group_mi2s_mclk_set.clk_freq_in_hz);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: mclk from DT file %s\n",
+ __func__, "qcom,msm-cpudai-mi2s-mclk");
+ goto rtn;
+ }
+
+ rc = of_property_read_u32(mi2s_parent_node,
+ "qcom,msm-cpudai-mi2s-bclk",
+ &group_mi2s_bclk_set.clk_freq_in_hz);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: bclk from DT file %s\n",
+ __func__, "qcom,msm-cpudai-mi2s-bclk");
+ goto rtn;
+ }
+
+ /*update default group mi2s clk set for master/slave select*/
+ rc = msm_dai_q6_group_mi2s_set_clk_param(group_mi2s_cfg.group_id,
+ &group_mi2s_mclk_set,
+ &group_mi2s_bclk_set,
+ sync_mode);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: group id not supported 0x%x\n",
+ __func__, tdm_group_cfg.group_id);
+ goto rtn;
+ }
+
+ /* retrieve device/afe id */
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-mi2s-dev-id",
+ &mi2s_dev_id);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: DeviceID missing in DT file\n",
+ __func__);
+ goto rtn;
+ }
+ rc = msm_dai_q6_group_mi2s_dev_id_validation(mi2s_dev_id);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Invalid GMI2S device ID %d in DT file\n",
+ __func__, mi2s_dev_id);
+ rc = -ENXIO;
+ goto rtn;
+ }
+
+ pdev->id = mi2s_dev_id;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-mi2s-dev-channel-mode",
+ &ch_mode);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: TX line from DT file %s\n",
+ __func__, "qcom,msm-cpudai-mi2s-dev-channel-mode");
+ goto rtn;
+ }
+
+ dai_data = kzalloc(sizeof(*dai_data),
+ GFP_KERNEL);
+ if (!dai_data) {
+ rc = -ENOMEM;
+ goto rtn;
+ }
+ dev_set_drvdata(&pdev->dev, dai_data);
+
+ dai_data->sync_mode = sync_mode;
+ dai_data->num_group_ports = num_group_mi2s_ports;
+ dai_data->port_cfg.i2s_cfg.channel_mode = ch_mode;
+ dai_data->bclk_set = group_mi2s_bclk_set;
+ dai_data->mclk_set = group_mi2s_mclk_set;
+ dai_data->group_cfg.i2s_cfg = group_mi2s_cfg;
+
+ port_idx = msm_dai_q6_get_mi2s_port_idx(mi2s_dev_id);
+ if (port_idx < 0 || port_idx > IDX_GROUP_MI2S_PORT_MAX) {
+ dev_err(&pdev->dev, "%s: Port id 0x%x not supported\n",
+ __func__, mi2s_dev_id);
+ rc = -EINVAL;
+ goto free_dai_data;
+ }
+
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_q6_group_mi2s_dai_component,
+ &msm_dai_q6_group_mi2s_dai[port_idx], 1);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: fail to register msm_q6_group_mi2s_dai_component\n",
+ __func__);
+ goto free_dai_data;
+ }
+
+ return 0;
+
+free_dai_data:
+ kfree(dai_data);
+rtn:
+ return rc;
+}
+
+static int msm_dai_group_mi2s_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_dai_group_mi2s_dt_match[] = {
+ { .compatible = "qcom,msm-dai-group-mi2s", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_group_mi2s_dt_match);
+
+static struct platform_driver msm_dai_group_mi2s_driver = {
+ .probe = msm_dai_group_mi2s_probe,
+ .remove = msm_dai_group_mi2s_remove,
+ .driver = {
+ .name = "msm-dai-group-mi2s",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_group_mi2s_dt_match,
+ },
+};
+
+static const struct of_device_id msm_dai_group_mi2s_dev_dt_match[] = {
+ { .compatible = "qcom,msm-dai-group-mi2s-dev", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_group_mi2s_dev_dt_match);
+
+static struct platform_driver msm_dai_group_mi2s_dev_driver = {
+ .probe = msm_dai_group_mi2s_dev_probe,
+ .remove = msm_dai_group_mi2s_dev_remove,
+ .driver = {
+ .name = "msm-dai-group-mi2s-dev",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_group_mi2s_dev_dt_match,
+ },
+};
+
+static int msm_dai_q6_spdif_dev_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ pdev->id = AFE_PORT_ID_SPDIF_RX;
+
+ pr_debug("%s: dev name %s, id:%d\n", __func__,
+ dev_name(&pdev->dev), pdev->id);
+
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_spdif_q6_component,
+ &msm_dai_q6_spdif_spdif_rx_dai, 1);
+ return rc;
+}
+
+static int msm_dai_q6_spdif_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_dai_q6_spdif_dt_match[] = {
+ {.compatible = "qcom,msm-dai-q6-spdif"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_spdif_dt_match);
+
+static struct platform_driver msm_dai_q6_spdif_driver = {
+ .probe = msm_dai_q6_spdif_dev_probe,
+ .remove = msm_dai_q6_spdif_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6-spdif",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_q6_spdif_dt_match,
+ },
+};
+
+static int msm_dai_q6_tdm_set_clk_param(u32 group_id,
+ struct afe_clk_set *clk_set, u32 mode)
+{
+ switch (group_id) {
+ case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX:
+ if (mode)
+ clk_set->clk_id = Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT;
+ else
+ clk_set->clk_id = Q6AFE_LPASS_CLK_ID_PRI_TDM_EBIT;
+ break;
+ case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX:
+ if (mode)
+ clk_set->clk_id = Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT;
+ else
+ clk_set->clk_id = Q6AFE_LPASS_CLK_ID_SEC_TDM_EBIT;
+ break;
+ case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX:
+ if (mode)
+ clk_set->clk_id = Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT;
+ else
+ clk_set->clk_id = Q6AFE_LPASS_CLK_ID_TER_TDM_EBIT;
+ break;
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX:
+ if (mode)
+ clk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT;
+ else
+ clk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int msm_dai_tdm_q6_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ const uint32_t *port_id_array = NULL;
+ uint32_t array_length = 0;
+ int i = 0;
+ int group_idx = 0;
+ u32 clk_mode = 0;
+
+ /* extract tdm group info into static */
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-group-id",
+ (u32 *)&tdm_group_cfg.group_id);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Group ID from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-group-id");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Group ID from DT file 0x%x\n",
+ __func__, tdm_group_cfg.group_id);
+
+ dev_info(&pdev->dev, "%s: dev_name: %s group_id: 0x%x\n",
+ __func__, dev_name(&pdev->dev), tdm_group_cfg.group_id);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-group-num-ports",
+ &num_tdm_group_ports);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Group Num Ports from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-group-num-ports");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Group Num Ports from DT file 0x%x\n",
+ __func__, num_tdm_group_ports);
+
+ if (num_tdm_group_ports > AFE_GROUP_DEVICE_NUM_PORTS) {
+ dev_err(&pdev->dev, "%s Group Num Ports %d greater than Max %d\n",
+ __func__, num_tdm_group_ports,
+ AFE_GROUP_DEVICE_NUM_PORTS);
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ port_id_array = of_get_property(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-group-port-id",
+ &array_length);
+ if (port_id_array == NULL) {
+ dev_err(&pdev->dev, "%s port_id_array is not valid\n",
+ __func__);
+ rc = -EINVAL;
+ goto rtn;
+ }
+ if (array_length != sizeof(uint32_t) * num_tdm_group_ports) {
+ dev_err(&pdev->dev, "%s array_length is %d, expected is %zd\n",
+ __func__, array_length,
+ sizeof(uint32_t) * num_tdm_group_ports);
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ for (i = 0; i < num_tdm_group_ports; i++)
+ tdm_group_cfg.port_id[i] =
+ (u16)be32_to_cpu(port_id_array[i]);
+ /* Unused index should be filled with 0 or AFE_PORT_INVALID */
+ for (i = num_tdm_group_ports; i < AFE_GROUP_DEVICE_NUM_PORTS; i++)
+ tdm_group_cfg.port_id[i] =
+ AFE_PORT_INVALID;
+
+ /* extract tdm clk info into static */
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-clk-rate",
+ &tdm_clk_set.clk_freq_in_hz);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Clk Rate from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-clk-rate");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Clk Rate from DT file %d\n",
+ __func__, tdm_clk_set.clk_freq_in_hz);
+
+ /* initialize static tdm clk attribute to default value */
+ tdm_clk_set.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO;
+
+ /* extract tdm clk attribute into static */
+ if (of_find_property(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-clk-attribute", NULL)) {
+ rc = of_property_read_u16(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-clk-attribute",
+ &tdm_clk_set.clk_attri);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: clk attribute from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-clk-attribute");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: clk attribute from DT file %d\n",
+ __func__, tdm_clk_set.clk_attri);
+ } else {
+ dev_dbg(&pdev->dev, "%s: No optional clk attribute found\n",
+ __func__);
+ }
+
+ /* extract tdm clk src master/slave info into static */
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-clk-internal",
+ &clk_mode);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Clk id from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-clk-internal");
+ goto rtn;
+ }
+ dev_dbg(&pdev->dev, "%s: Clk id from DT file %d\n",
+ __func__, clk_mode);
+
+ rc = msm_dai_q6_tdm_set_clk_param(tdm_group_cfg.group_id,
+ &tdm_clk_set, clk_mode);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: group id not supported 0x%x\n",
+ __func__, tdm_group_cfg.group_id);
+ goto rtn;
+ }
+
+ /* other initializations within device group */
+ group_idx = msm_dai_q6_get_group_idx(tdm_group_cfg.group_id);
+ if (group_idx < 0) {
+ dev_err(&pdev->dev, "%s: group id 0x%x not supported\n",
+ __func__, tdm_group_cfg.group_id);
+ rc = -EINVAL;
+ goto rtn;
+ }
+ atomic_set(&tdm_group_ref[group_idx], 0);
+
+ /* probe child node info */
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+ __func__, rc);
+ goto rtn;
+ } else
+ dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
+
+rtn:
+ return rc;
+}
+
+static int msm_dai_tdm_q6_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id msm_dai_tdm_dt_match[] = {
+ { .compatible = "qcom,msm-dai-tdm", },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_tdm_dt_match);
+
+static struct platform_driver msm_dai_tdm_q6 = {
+ .probe = msm_dai_tdm_q6_probe,
+ .remove = msm_dai_tdm_q6_remove,
+ .driver = {
+ .name = "msm-dai-tdm",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_tdm_dt_match,
+ },
+};
+
+static int msm_dai_q6_tdm_data_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+
+ switch (value) {
+ case 0:
+ dai_data->port_cfg.tdm.data_format = AFE_LINEAR_PCM_DATA;
+ break;
+ case 1:
+ dai_data->port_cfg.tdm.data_format = AFE_NON_LINEAR_DATA;
+ break;
+ case 2:
+ dai_data->port_cfg.tdm.data_format = AFE_GENERIC_COMPRESSED;
+ break;
+ default:
+ pr_err("%s: data_format invalid\n", __func__);
+ break;
+ }
+ pr_debug("%s: data_format = %d\n",
+ __func__, dai_data->port_cfg.tdm.data_format);
+ return 0;
+}
+
+static int msm_dai_q6_tdm_data_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+
+ ucontrol->value.integer.value[0] =
+ dai_data->port_cfg.tdm.data_format;
+ pr_debug("%s: data_format = %d\n",
+ __func__, dai_data->port_cfg.tdm.data_format);
+ return 0;
+}
+
+static int msm_dai_q6_tdm_header_type_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+
+ dai_data->port_cfg.custom_tdm_header.header_type = value;
+ pr_debug("%s: header_type = %d\n",
+ __func__,
+ dai_data->port_cfg.custom_tdm_header.header_type);
+ return 0;
+}
+
+static int msm_dai_q6_tdm_header_type_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+
+ ucontrol->value.integer.value[0] =
+ dai_data->port_cfg.custom_tdm_header.header_type;
+ pr_debug("%s: header_type = %d\n",
+ __func__,
+ dai_data->port_cfg.custom_tdm_header.header_type);
+ return 0;
+}
+
+static int msm_dai_q6_tdm_header_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+ int i = 0;
+
+ for (i = 0; i < AFE_CUSTOM_TDM_HEADER_MAX_CNT; i++) {
+ dai_data->port_cfg.custom_tdm_header.header[i] =
+ (u16)ucontrol->value.integer.value[i];
+ pr_debug("%s: header #%d = 0x%x\n",
+ __func__, i,
+ dai_data->port_cfg.custom_tdm_header.header[i]);
+ }
+ return 0;
+}
+
+static int msm_dai_q6_tdm_header_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+ int i = 0;
+
+ for (i = 0; i < AFE_CUSTOM_TDM_HEADER_MAX_CNT; i++) {
+ ucontrol->value.integer.value[i] =
+ dai_data->port_cfg.custom_tdm_header.header[i];
+ pr_debug("%s: header #%d = 0x%x\n",
+ __func__, i,
+ dai_data->port_cfg.custom_tdm_header.header[i]);
+ }
+ return 0;
+}
+
+static const struct snd_kcontrol_new tdm_config_controls_data_format[] = {
+ SOC_ENUM_EXT("PRI_TDM_RX_0 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_1 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_2 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_3 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_4 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_5 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_6 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_7 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_1 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_2 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_3 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_4 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_5 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_6 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_7 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_1 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_2 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_3 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_4 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_5 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_6 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_7 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_1 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_2 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_3 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_4 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_5 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_6 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_7 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_1 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_2 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_3 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_4 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_5 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_6 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_7 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_1 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_2 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_3 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_4 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_5 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_6 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_7 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_1 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_2 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_3 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_4 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_5 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_6 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_7 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_1 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_2 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_3 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_4 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_5 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_6 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_7 Data Format", tdm_config_enum[0],
+ msm_dai_q6_tdm_data_format_get,
+ msm_dai_q6_tdm_data_format_put),
+};
+
+static const struct snd_kcontrol_new tdm_config_controls_header_type[] = {
+ SOC_ENUM_EXT("PRI_TDM_RX_0 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_1 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_2 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_3 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_4 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_5 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_6 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_7 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_1 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_2 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_3 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_4 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_5 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_6 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_7 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_1 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_2 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_3 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_4 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_5 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_6 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_7 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_1 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_2 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_3 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_4 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_5 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_6 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_7 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_1 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_2 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_3 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_4 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_5 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_6 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_7 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_1 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_2 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_3 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_4 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_5 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_6 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_7 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_1 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_2 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_3 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_4 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_5 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_6 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_7 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_1 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_2 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_3 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_4 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_5 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_6 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_7 Header Type", tdm_config_enum[1],
+ msm_dai_q6_tdm_header_type_get,
+ msm_dai_q6_tdm_header_type_put),
+};
+
+static const struct snd_kcontrol_new tdm_config_controls_header[] = {
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_0 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_1 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_2 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_3 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_4 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_5 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_6 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_7 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_0 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_1 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_2 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_3 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_4 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_5 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_6 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_7 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_0 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_1 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_2 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_3 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_4 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_5 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_6 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_7 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_0 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_1 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_2 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_3 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_4 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_5 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_6 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_7 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_0 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_1 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_2 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_3 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_4 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_5 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_6 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_7 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_0 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_1 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_2 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_3 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_4 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_5 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_6 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_7 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_0 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_1 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_2 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_3 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_4 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_5 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_6 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_7 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_0 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_1 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_2 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_3 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_4 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_5 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_6 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+ SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_7 Header",
+ SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+ msm_dai_q6_tdm_header_get,
+ msm_dai_q6_tdm_header_put),
+};
+
+static int msm_dai_q6_tdm_set_clk(
+ struct msm_dai_q6_tdm_dai_data *dai_data,
+ u16 port_id, bool enable)
+{
+ int rc = 0;
+
+ dai_data->clk_set.enable = enable;
+
+ rc = afe_set_lpass_clock_v2(port_id,
+ &dai_data->clk_set);
+ if (rc < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+static int msm_dai_q6_dai_tdm_probe(struct snd_soc_dai *dai)
+{
+ int rc = 0;
+ struct msm_dai_q6_tdm_dai_data *tdm_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct snd_kcontrol *data_format_kcontrol = NULL;
+ struct snd_kcontrol *header_type_kcontrol = NULL;
+ struct snd_kcontrol *header_kcontrol = NULL;
+ int port_idx = 0;
+ const struct snd_kcontrol_new *data_format_ctrl = NULL;
+ const struct snd_kcontrol_new *header_type_ctrl = NULL;
+ const struct snd_kcontrol_new *header_ctrl = NULL;
+
+ msm_dai_q6_set_dai_id(dai);
+
+ port_idx = msm_dai_q6_get_port_idx(dai->id);
+ if (port_idx < 0) {
+ dev_err(dai->dev, "%s port id 0x%x not supported\n",
+ __func__, dai->id);
+ rc = -EINVAL;
+ goto rtn;
+ }
+
+ data_format_ctrl =
+ &tdm_config_controls_data_format[port_idx];
+ header_type_ctrl =
+ &tdm_config_controls_header_type[port_idx];
+ header_ctrl =
+ &tdm_config_controls_header[port_idx];
+
+ if (data_format_ctrl) {
+ data_format_kcontrol = snd_ctl_new1(data_format_ctrl,
+ tdm_dai_data);
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ data_format_kcontrol);
+
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: err add data format ctrl DAI = %s\n",
+ __func__, dai->name);
+ goto rtn;
+ }
+ }
+
+ if (header_type_ctrl) {
+ header_type_kcontrol = snd_ctl_new1(header_type_ctrl,
+ tdm_dai_data);
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ header_type_kcontrol);
+
+ if (IS_ERR_VALUE(rc)) {
+ if (data_format_kcontrol)
+ snd_ctl_remove(dai->component->card->snd_card,
+ data_format_kcontrol);
+ dev_err(dai->dev, "%s: err add header type ctrl DAI = %s\n",
+ __func__, dai->name);
+ goto rtn;
+ }
+ }
+
+ if (header_ctrl) {
+ header_kcontrol = snd_ctl_new1(header_ctrl,
+ tdm_dai_data);
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ header_kcontrol);
+
+ if (IS_ERR_VALUE(rc)) {
+ if (header_type_kcontrol)
+ snd_ctl_remove(dai->component->card->snd_card,
+ header_type_kcontrol);
+ if (data_format_kcontrol)
+ snd_ctl_remove(dai->component->card->snd_card,
+ data_format_kcontrol);
+ dev_err(dai->dev, "%s: err add header ctrl DAI = %s\n",
+ __func__, dai->name);
+ goto rtn;
+ }
+ }
+
+ rc = msm_dai_q6_dai_add_route(dai);
+
+rtn:
+ return rc;
+}
+
+
+static int msm_dai_q6_dai_tdm_remove(struct snd_soc_dai *dai)
+{
+ int rc = 0;
+ struct msm_dai_q6_tdm_dai_data *tdm_dai_data =
+ dev_get_drvdata(dai->dev);
+ u16 group_id = tdm_dai_data->group_cfg.tdm_cfg.group_id;
+ int group_idx = 0;
+ atomic_t *group_ref = NULL;
+
+ group_idx = msm_dai_q6_get_group_idx(dai->id);
+ if (group_idx < 0) {
+ dev_err(dai->dev, "%s port id 0x%x not supported\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ group_ref = &tdm_group_ref[group_idx];
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, tdm_dai_data->status_mask)) {
+ rc = afe_close(dai->id); /* can block */
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to close AFE port 0x%x\n",
+ __func__, dai->id);
+ }
+ atomic_dec(group_ref);
+ clear_bit(STATUS_PORT_STARTED,
+ tdm_dai_data->status_mask);
+
+ if (atomic_read(group_ref) == 0) {
+ rc = afe_port_group_enable(group_id,
+ NULL, false);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "fail to disable AFE group 0x%x\n",
+ group_id);
+ }
+ rc = msm_dai_q6_tdm_set_clk(tdm_dai_data,
+ dai->id, false);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n",
+ __func__, dai->id);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ int rc = 0;
+ struct msm_dai_q6_tdm_dai_data *dai_data =
+ dev_get_drvdata(dai->dev);
+ struct afe_param_id_group_device_tdm_cfg *tdm_group =
+ &dai_data->group_cfg.tdm_cfg;
+ unsigned int cap_mask;
+
+ dev_dbg(dai->dev, "%s: dai id = 0x%x\n", __func__, dai->id);
+
+ /* HW only supports 16 and 32 bit slot width configuration */
+ if ((slot_width != 16) && (slot_width != 32)) {
+ dev_err(dai->dev, "%s: invalid slot_width %d\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ /* HW supports 1-32 slots configuration. Typical: 1, 2, 4, 8, 16, 32 */
+ switch (slots) {
+ case 1:
+ cap_mask = 0x01;
+ break;
+ case 2:
+ cap_mask = 0x03;
+ break;
+ case 4:
+ cap_mask = 0x0F;
+ break;
+ case 8:
+ cap_mask = 0xFF;
+ break;
+ case 16:
+ cap_mask = 0xFFFF;
+ break;
+ case 32:
+ cap_mask = 0xFFFFFFFF;
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid slots %d\n",
+ __func__, slots);
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ tdm_group->nslots_per_frame = slots;
+ tdm_group->slot_width = slot_width;
+ tdm_group->slot_mask = rx_mask & cap_mask;
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ tdm_group->nslots_per_frame = slots;
+ tdm_group->slot_width = slot_width;
+ tdm_group->slot_mask = tx_mask & cap_mask;
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_tdm_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct msm_dai_q6_tdm_dai_data *dai_data =
+ dev_get_drvdata(dai->dev);
+
+ if ((dai->id >= AFE_PORT_ID_PRIMARY_TDM_RX) &&
+ (dai->id <= AFE_PORT_ID_QUATERNARY_TDM_TX_7)) {
+ dai_data->clk_set.clk_freq_in_hz = freq;
+ } else {
+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "%s: dai id = 0x%x group clk_freq %d\n",
+ __func__, dai->id, freq);
+ return 0;
+}
+
+
+static int msm_dai_q6_tdm_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ int rc = 0;
+ struct msm_dai_q6_tdm_dai_data *dai_data =
+ dev_get_drvdata(dai->dev);
+ struct afe_param_id_slot_mapping_cfg *slot_mapping =
+ &dai_data->port_cfg.slot_mapping;
+ struct afe_param_id_slot_mapping_cfg_v2 *slot_mapping_v2 =
+ &dai_data->port_cfg.slot_mapping_v2;
+ int i = 0;
+
+ dev_dbg(dai->dev, "%s: dai id = 0x%x\n", __func__, dai->id);
+
+ switch (dai->id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ if (afe_get_svc_version(APR_SVC_AFE) >=
+ ADSP_AFE_API_VERSION_V3) {
+ if (!rx_slot) {
+ dev_err(dai->dev, "%s: rx slot not found\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT_V2) {
+ dev_err(dai->dev, "%s: invalid rx num %d\n",
+ __func__,
+ rx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rx_num; i++)
+ slot_mapping_v2->offset[i] = rx_slot[i];
+ for (i = rx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT_V2;
+ i++)
+ slot_mapping_v2->offset[i] =
+ AFE_SLOT_MAPPING_OFFSET_INVALID;
+
+ slot_mapping_v2->num_channel = rx_num;
+ } else {
+ if (!rx_slot) {
+ dev_err(dai->dev, "%s: rx slot not found\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) {
+ dev_err(dai->dev, "%s: invalid rx num %d\n",
+ __func__,
+ rx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rx_num; i++)
+ slot_mapping->offset[i] = rx_slot[i];
+ for (i = rx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++)
+ slot_mapping->offset[i] =
+ AFE_SLOT_MAPPING_OFFSET_INVALID;
+
+ slot_mapping->num_channel = rx_num;
+ }
+ break;
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ if (afe_get_svc_version(APR_SVC_AFE) >=
+ ADSP_AFE_API_VERSION_V3) {
+ if (!tx_slot) {
+ dev_err(dai->dev, "%s: tx slot not found\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT_V2) {
+ dev_err(dai->dev, "%s: invalid tx num %d\n",
+ __func__,
+ tx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < tx_num; i++)
+ slot_mapping_v2->offset[i] = tx_slot[i];
+ for (i = tx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT_V2;
+ i++)
+ slot_mapping_v2->offset[i] =
+ AFE_SLOT_MAPPING_OFFSET_INVALID;
+
+ slot_mapping_v2->num_channel = tx_num;
+ } else {
+ if (!tx_slot) {
+ dev_err(dai->dev, "%s: tx slot not found\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) {
+ dev_err(dai->dev, "%s: invalid tx num %d\n",
+ __func__,
+ tx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < tx_num; i++)
+ slot_mapping->offset[i] = tx_slot[i];
+ for (i = tx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++)
+ slot_mapping->offset[i] =
+ AFE_SLOT_MAPPING_OFFSET_INVALID;
+
+ slot_mapping->num_channel = tx_num;
+ }
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_tdm_dai_data *dai_data =
+ dev_get_drvdata(dai->dev);
+
+ struct afe_param_id_group_device_tdm_cfg *tdm_group =
+ &dai_data->group_cfg.tdm_cfg;
+ struct afe_param_id_tdm_cfg *tdm =
+ &dai_data->port_cfg.tdm;
+ struct afe_param_id_slot_mapping_cfg *slot_mapping =
+ &dai_data->port_cfg.slot_mapping;
+ struct afe_param_id_slot_mapping_cfg_v2 *slot_mapping_v2 =
+ &dai_data->port_cfg.slot_mapping_v2;
+ struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header =
+ &dai_data->port_cfg.custom_tdm_header;
+
+ pr_debug("%s: dev_name: %s\n",
+ __func__, dev_name(dai->dev));
+
+ if ((params_channels(params) == 0) ||
+ (params_channels(params) > 32)) {
+ dev_err(dai->dev, "%s: invalid param channels %d\n",
+ __func__, params_channels(params));
+ return -EINVAL;
+ }
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dai_data->bitwidth = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ dai_data->bitwidth = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dai_data->bitwidth = 32;
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid param format 0x%x\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ /*
+ * update tdm group config param
+ * NOTE: group config is set to the same as slot config.
+ */
+ tdm_group->bit_width = tdm_group->slot_width;
+ tdm_group->num_channels = tdm_group->nslots_per_frame;
+ tdm_group->sample_rate = dai_data->rate;
+
+ pr_debug("%s: TDM GROUP:\n"
+ "num_channels=%d sample_rate=%d bit_width=%d\n"
+ "nslots_per_frame=%d slot_width=%d slot_mask=0x%x\n",
+ __func__,
+ tdm_group->num_channels,
+ tdm_group->sample_rate,
+ tdm_group->bit_width,
+ tdm_group->nslots_per_frame,
+ tdm_group->slot_width,
+ tdm_group->slot_mask);
+ pr_debug("%s: TDM GROUP:\n"
+ "port_id[0]=0x%x port_id[1]=0x%x port_id[2]=0x%x port_id[3]=0x%x\n"
+ "port_id[4]=0x%x port_id[5]=0x%x port_id[6]=0x%x port_id[7]=0x%x\n",
+ __func__,
+ tdm_group->port_id[0],
+ tdm_group->port_id[1],
+ tdm_group->port_id[2],
+ tdm_group->port_id[3],
+ tdm_group->port_id[4],
+ tdm_group->port_id[5],
+ tdm_group->port_id[6],
+ tdm_group->port_id[7]);
+
+ /*
+ * update tdm config param
+ * NOTE: channels/rate/bitwidth are per stream property
+ */
+ tdm->num_channels = dai_data->channels;
+ tdm->sample_rate = dai_data->rate;
+ tdm->bit_width = dai_data->bitwidth;
+ /*
+ * port slot config is the same as group slot config
+ * port slot mask should be set according to offset
+ */
+ tdm->nslots_per_frame = tdm_group->nslots_per_frame;
+ tdm->slot_width = tdm_group->slot_width;
+ tdm->slot_mask = tdm_group->slot_mask;
+
+ pr_debug("%s: TDM:\n"
+ "num_channels=%d sample_rate=%d bit_width=%d\n"
+ "nslots_per_frame=%d slot_width=%d slot_mask=0x%x\n"
+ "data_format=0x%x sync_mode=0x%x sync_src=0x%x\n"
+ "data_out=0x%x invert_sync=0x%x data_delay=0x%x\n",
+ __func__,
+ tdm->num_channels,
+ tdm->sample_rate,
+ tdm->bit_width,
+ tdm->nslots_per_frame,
+ tdm->slot_width,
+ tdm->slot_mask,
+ tdm->data_format,
+ tdm->sync_mode,
+ tdm->sync_src,
+ tdm->ctrl_data_out_enable,
+ tdm->ctrl_invert_sync_pulse,
+ tdm->ctrl_sync_data_delay);
+ if (afe_get_svc_version(APR_SVC_AFE) >=
+ ADSP_AFE_API_VERSION_V3) {
+ /*
+ * update slot mapping v2 config param
+ * NOTE: channels/rate/bitwidth are per stream property
+ */
+ slot_mapping_v2->bitwidth = dai_data->bitwidth;
+
+ pr_debug("%s: SLOT MAPPING_V2:\n"
+ "num_channel=%d bitwidth=%d data_align=0x%x\n",
+ __func__,
+ slot_mapping_v2->num_channel,
+ slot_mapping_v2->bitwidth,
+ slot_mapping_v2->data_align_type);
+ pr_debug("%s: SLOT MAPPING V2:\n"
+ "offset[0]=0x%x offset[1]=0x%x offset[2]=0x%x offset[3]=0x%x\n"
+ "offset[4]=0x%x offset[5]=0x%x offset[6]=0x%x offset[7]=0x%x\n"
+ "offset[8]=0x%x offset[9]=0x%x offset[10]=0x%x offset[11]=0x%x\n"
+ "offset[12]=0x%x offset[13]=0x%x offset[14]=0x%x offset[15]=0x%x\n"
+ "offset[16]=0x%x offset[17]=0x%x offset[18]=0x%x offset[19]=0x%x\n"
+ "offset[20]=0x%x offset[21]=0x%x offset[22]=0x%x offset[23]=0x%x\n"
+ "offset[24]=0x%x offset[25]=0x%x offset[26]=0x%x offset[27]=0x%x\n"
+ "offset[28]=0x%x offset[29]=0x%x offset[30]=0x%x offset[31]=0x%x\n",
+ __func__,
+ slot_mapping_v2->offset[0],
+ slot_mapping_v2->offset[1],
+ slot_mapping_v2->offset[2],
+ slot_mapping_v2->offset[3],
+ slot_mapping_v2->offset[4],
+ slot_mapping_v2->offset[5],
+ slot_mapping_v2->offset[6],
+ slot_mapping_v2->offset[7],
+ slot_mapping_v2->offset[8],
+ slot_mapping_v2->offset[9],
+ slot_mapping_v2->offset[10],
+ slot_mapping_v2->offset[11],
+ slot_mapping_v2->offset[12],
+ slot_mapping_v2->offset[13],
+ slot_mapping_v2->offset[14],
+ slot_mapping_v2->offset[15],
+ slot_mapping_v2->offset[16],
+ slot_mapping_v2->offset[17],
+ slot_mapping_v2->offset[18],
+ slot_mapping_v2->offset[19],
+ slot_mapping_v2->offset[20],
+ slot_mapping_v2->offset[21],
+ slot_mapping_v2->offset[22],
+ slot_mapping_v2->offset[23],
+ slot_mapping_v2->offset[24],
+ slot_mapping_v2->offset[25],
+ slot_mapping_v2->offset[26],
+ slot_mapping_v2->offset[27],
+ slot_mapping_v2->offset[28],
+ slot_mapping_v2->offset[29],
+ slot_mapping_v2->offset[30],
+ slot_mapping_v2->offset[31]);
+ } else {
+ /*
+ * update slot mapping config param
+ * NOTE: channels/rate/bitwidth are per stream property
+ */
+ slot_mapping->bitwidth = dai_data->bitwidth;
+
+ pr_debug("%s: SLOT MAPPING:\n"
+ "num_channel=%d bitwidth=%d data_align=0x%x\n",
+ __func__,
+ slot_mapping->num_channel,
+ slot_mapping->bitwidth,
+ slot_mapping->data_align_type);
+ pr_debug("%s: SLOT MAPPING:\n"
+ "offset[0]=0x%x offset[1]=0x%x offset[2]=0x%x offset[3]=0x%x\n"
+ "offset[4]=0x%x offset[5]=0x%x offset[6]=0x%x offset[7]=0x%x\n",
+ __func__,
+ slot_mapping->offset[0],
+ slot_mapping->offset[1],
+ slot_mapping->offset[2],
+ slot_mapping->offset[3],
+ slot_mapping->offset[4],
+ slot_mapping->offset[5],
+ slot_mapping->offset[6],
+ slot_mapping->offset[7]);
+ }
+ /*
+ * update custom header config param
+ * NOTE: channels/rate/bitwidth are per playback stream property.
+ * custom tdm header only applicable to playback stream.
+ */
+ if (custom_tdm_header->header_type !=
+ AFE_CUSTOM_TDM_HEADER_TYPE_INVALID) {
+ pr_debug("%s: CUSTOM TDM HEADER:\n"
+ "start_offset=0x%x header_width=%d\n"
+ "num_frame_repeat=%d header_type=0x%x\n",
+ __func__,
+ custom_tdm_header->start_offset,
+ custom_tdm_header->header_width,
+ custom_tdm_header->num_frame_repeat,
+ custom_tdm_header->header_type);
+ pr_debug("%s: CUSTOM TDM HEADER:\n"
+ "header[0]=0x%x header[1]=0x%x header[2]=0x%x header[3]=0x%x\n"
+ "header[4]=0x%x header[5]=0x%x header[6]=0x%x header[7]=0x%x\n",
+ __func__,
+ custom_tdm_header->header[0],
+ custom_tdm_header->header[1],
+ custom_tdm_header->header[2],
+ custom_tdm_header->header[3],
+ custom_tdm_header->header[4],
+ custom_tdm_header->header[5],
+ custom_tdm_header->header[6],
+ custom_tdm_header->header[7]);
+ }
+
+ return 0;
+}
+
+static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int rc = 0;
+ struct msm_dai_q6_tdm_dai_data *dai_data =
+ dev_get_drvdata(dai->dev);
+ u16 group_id = dai_data->group_cfg.tdm_cfg.group_id;
+ int group_idx = 0;
+ atomic_t *group_ref = NULL;
+
+ group_idx = msm_dai_q6_get_group_idx(dai->id);
+ if (group_idx < 0) {
+ dev_err(dai->dev, "%s port id 0x%x not supported\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&tdm_mutex);
+
+ group_ref = &tdm_group_ref[group_idx];
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ /* PORT START should be set if prepare called
+ in active state. */
+ if (atomic_read(group_ref) == 0) {
+ /* TX and RX share the same clk.
+ AFE clk is enabled per group to simplify the logic.
+ DSP will monitor the clk count. */
+ rc = msm_dai_q6_tdm_set_clk(dai_data,
+ dai->id, true);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to enable AFE clk 0x%x\n",
+ __func__, dai->id);
+ goto rtn;
+ }
+
+ /*
+ * if only one port, don't do group enable as there
+ * is no group need for only one port
+ */
+ if (dai_data->num_group_ports > 1) {
+ rc = afe_port_group_enable(group_id,
+ &dai_data->group_cfg, true);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev,
+ "%s: fail to enable AFE group 0x%x\n",
+ __func__, group_id);
+ goto rtn;
+ }
+ }
+ }
+
+ rc = afe_tdm_port_start(dai->id, &dai_data->port_cfg,
+ dai_data->rate, dai_data->num_group_ports);
+ if (IS_ERR_VALUE(rc)) {
+ if (atomic_read(group_ref) == 0) {
+ afe_port_group_enable(group_id,
+ NULL, false);
+ msm_dai_q6_tdm_set_clk(dai_data,
+ dai->id, false);
+ }
+ dev_err(dai->dev, "%s: fail to open AFE port 0x%x\n",
+ __func__, dai->id);
+ } else {
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ atomic_inc(group_ref);
+ }
+
+ /* TODO: need to monitor PCM/MI2S/TDM HW status */
+ /* NOTE: AFE should error out if HW resource contention */
+
+ }
+
+rtn:
+ mutex_unlock(&tdm_mutex);
+ return rc;
+}
+
+static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int rc = 0;
+ struct msm_dai_q6_tdm_dai_data *dai_data =
+ dev_get_drvdata(dai->dev);
+ u16 group_id = dai_data->group_cfg.tdm_cfg.group_id;
+ int group_idx = 0;
+ atomic_t *group_ref = NULL;
+
+ group_idx = msm_dai_q6_get_group_idx(dai->id);
+ if (group_idx < 0) {
+ dev_err(dai->dev, "%s port id 0x%x not supported\n",
+ __func__, dai->id);
+ return;
+ }
+
+ mutex_lock(&tdm_mutex);
+
+ group_ref = &tdm_group_ref[group_idx];
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(dai->id);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to close AFE port 0x%x\n",
+ __func__, dai->id);
+ }
+ atomic_dec(group_ref);
+ clear_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+
+ if (atomic_read(group_ref) == 0) {
+ rc = afe_port_group_enable(group_id,
+ NULL, false);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to disable AFE group 0x%x\n",
+ __func__, group_id);
+ }
+ rc = msm_dai_q6_tdm_set_clk(dai_data,
+ dai->id, false);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n",
+ __func__, dai->id);
+ }
+ }
+
+ /* TODO: need to monitor PCM/MI2S/TDM HW status */
+ /* NOTE: AFE should error out if HW resource contention */
+
+ }
+
+ mutex_unlock(&tdm_mutex);
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_tdm_ops = {
+ .prepare = msm_dai_q6_tdm_prepare,
+ .hw_params = msm_dai_q6_tdm_hw_params,
+ .set_tdm_slot = msm_dai_q6_tdm_set_tdm_slot,
+ .set_channel_map = msm_dai_q6_tdm_set_channel_map,
+ .set_sysclk = msm_dai_q6_tdm_set_sysclk,
+ .shutdown = msm_dai_q6_tdm_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = {
+ {
+ .playback = {
+ .stream_name = "Primary TDM0 Playback",
+ .aif_name = "PRI_TDM_RX_0",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_RX,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM1 Playback",
+ .aif_name = "PRI_TDM_RX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_RX_1,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM2 Playback",
+ .aif_name = "PRI_TDM_RX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_RX_2,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM3 Playback",
+ .aif_name = "PRI_TDM_RX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_RX_3,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM4 Playback",
+ .aif_name = "PRI_TDM_RX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_RX_4,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM5 Playback",
+ .aif_name = "PRI_TDM_RX_5",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_RX_5,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM6 Playback",
+ .aif_name = "PRI_TDM_RX_6",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_RX_6,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Primary TDM7 Playback",
+ .aif_name = "PRI_TDM_RX_7",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_RX_7,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM0 Capture",
+ .aif_name = "PRI_TDM_TX_0",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_TX,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM1 Capture",
+ .aif_name = "PRI_TDM_TX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_TX_1,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM2 Capture",
+ .aif_name = "PRI_TDM_TX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_TX_2,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM3 Capture",
+ .aif_name = "PRI_TDM_TX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_TX_3,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM4 Capture",
+ .aif_name = "PRI_TDM_TX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_TX_4,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM5 Capture",
+ .aif_name = "PRI_TDM_TX_5",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_TX_5,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM6 Capture",
+ .aif_name = "PRI_TDM_TX_6",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_TX_6,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Primary TDM7 Capture",
+ .aif_name = "PRI_TDM_TX_7",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_PRIMARY_TDM_TX_7,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM0 Playback",
+ .aif_name = "SEC_TDM_RX_0",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_RX,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM1 Playback",
+ .aif_name = "SEC_TDM_RX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_RX_1,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM2 Playback",
+ .aif_name = "SEC_TDM_RX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_RX_2,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM3 Playback",
+ .aif_name = "SEC_TDM_RX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_RX_3,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM4 Playback",
+ .aif_name = "SEC_TDM_RX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_RX_4,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM5 Playback",
+ .aif_name = "SEC_TDM_RX_5",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_RX_5,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM6 Playback",
+ .aif_name = "SEC_TDM_RX_6",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_RX_6,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Secondary TDM7 Playback",
+ .aif_name = "SEC_TDM_RX_7",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_RX_7,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM0 Capture",
+ .aif_name = "SEC_TDM_TX_0",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_TX,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM1 Capture",
+ .aif_name = "SEC_TDM_TX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_TX_1,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM2 Capture",
+ .aif_name = "SEC_TDM_TX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_TX_2,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM3 Capture",
+ .aif_name = "SEC_TDM_TX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_TX_3,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM4 Capture",
+ .aif_name = "SEC_TDM_TX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_TX_4,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM5 Capture",
+ .aif_name = "SEC_TDM_TX_5",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_TX_5,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM6 Capture",
+ .aif_name = "SEC_TDM_TX_6",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_TX_6,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Secondary TDM7 Capture",
+ .aif_name = "SEC_TDM_TX_7",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_SECONDARY_TDM_TX_7,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM0 Playback",
+ .aif_name = "TERT_TDM_RX_0",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_RX,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM1 Playback",
+ .aif_name = "TERT_TDM_RX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_RX_1,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM2 Playback",
+ .aif_name = "TERT_TDM_RX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_RX_2,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM3 Playback",
+ .aif_name = "TERT_TDM_RX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_RX_3,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM4 Playback",
+ .aif_name = "TERT_TDM_RX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_RX_4,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM5 Playback",
+ .aif_name = "TERT_TDM_RX_5",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_RX_5,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM6 Playback",
+ .aif_name = "TERT_TDM_RX_6",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_RX_6,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Tertiary TDM7 Playback",
+ .aif_name = "TERT_TDM_RX_7",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_RX_7,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM0 Capture",
+ .aif_name = "TERT_TDM_TX_0",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_TX,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM1 Capture",
+ .aif_name = "TERT_TDM_TX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_TX_1,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM2 Capture",
+ .aif_name = "TERT_TDM_TX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_TX_2,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM3 Capture",
+ .aif_name = "TERT_TDM_TX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_TX_3,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM4 Capture",
+ .aif_name = "TERT_TDM_TX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_TX_4,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM5 Capture",
+ .aif_name = "TERT_TDM_TX_5",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_TX_5,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM6 Capture",
+ .aif_name = "TERT_TDM_TX_6",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_TX_6,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Tertiary TDM7 Capture",
+ .aif_name = "TERT_TDM_TX_7",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_TERTIARY_TDM_TX_7,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM0 Playback",
+ .aif_name = "QUAT_TDM_RX_0",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_RX,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM1 Playback",
+ .aif_name = "QUAT_TDM_RX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_RX_1,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM2 Playback",
+ .aif_name = "QUAT_TDM_RX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_RX_2,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM3 Playback",
+ .aif_name = "QUAT_TDM_RX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_RX_3,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM4 Playback",
+ .aif_name = "QUAT_TDM_RX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_RX_4,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM5 Playback",
+ .aif_name = "QUAT_TDM_RX_5",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_RX_5,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM6 Playback",
+ .aif_name = "QUAT_TDM_RX_6",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_RX_6,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quaternary TDM7 Playback",
+ .aif_name = "QUAT_TDM_RX_7",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_RX_7,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM0 Capture",
+ .aif_name = "QUAT_TDM_TX_0",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_TX,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM1 Capture",
+ .aif_name = "QUAT_TDM_TX_1",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_TX_1,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM2 Capture",
+ .aif_name = "QUAT_TDM_TX_2",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_TX_2,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM3 Capture",
+ .aif_name = "QUAT_TDM_TX_3",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_TX_3,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM4 Capture",
+ .aif_name = "QUAT_TDM_TX_4",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_TX_4,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM5 Capture",
+ .aif_name = "QUAT_TDM_TX_5",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_TX_5,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM6 Capture",
+ .aif_name = "QUAT_TDM_TX_6",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_TX_6,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Quaternary TDM7 Capture",
+ .aif_name = "QUAT_TDM_TX_7",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 16,
+ .rate_min = 8000,
+ .rate_max = 352800,
+ },
+ .ops = &msm_dai_q6_tdm_ops,
+ .id = AFE_PORT_ID_QUATERNARY_TDM_TX_7,
+ .probe = msm_dai_q6_dai_tdm_probe,
+ .remove = msm_dai_q6_dai_tdm_remove,
+ },
+};
+
+static const struct snd_soc_component_driver msm_q6_tdm_dai_component = {
+ .name = "msm-dai-q6-tdm",
+};
+
+static int msm_dai_q6_tdm_dev_probe(struct platform_device *pdev)
+{
+ struct msm_dai_q6_tdm_dai_data *dai_data = NULL;
+ struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header = NULL;
+ int rc = 0;
+ u32 tdm_dev_id = 0;
+ int port_idx = 0;
+ struct device_node *tdm_parent_node = NULL;
+
+ /* retrieve device/afe id */
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-dev-id",
+ &tdm_dev_id);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Device ID missing in DT file\n",
+ __func__);
+ goto rtn;
+ }
+ if ((tdm_dev_id < AFE_PORT_ID_TDM_PORT_RANGE_START) ||
+ (tdm_dev_id > AFE_PORT_ID_TDM_PORT_RANGE_END)) {
+ dev_err(&pdev->dev, "%s: Invalid TDM Device ID 0x%x in DT file\n",
+ __func__, tdm_dev_id);
+ rc = -ENXIO;
+ goto rtn;
+ }
+ pdev->id = tdm_dev_id;
+
+ dev_info(&pdev->dev, "%s: dev_name: %s dev_id: 0x%x\n",
+ __func__, dev_name(&pdev->dev), tdm_dev_id);
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_tdm_dai_data),
+ GFP_KERNEL);
+ if (!dai_data) {
+ rc = -ENOMEM;
+ dev_err(&pdev->dev,
+ "%s Failed to allocate memory for tdm dai_data\n",
+ __func__);
+ goto rtn;
+ }
+ memset(dai_data, 0, sizeof(*dai_data));
+
+ /* TDM CFG */
+ tdm_parent_node = of_get_parent(pdev->dev.of_node);
+ rc = of_property_read_u32(tdm_parent_node,
+ "qcom,msm-cpudai-tdm-sync-mode",
+ (u32 *)&dai_data->port_cfg.tdm.sync_mode);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Sync Mode from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-sync-mode");
+ goto free_dai_data;
+ }
+ dev_dbg(&pdev->dev, "%s: Sync Mode from DT file 0x%x\n",
+ __func__, dai_data->port_cfg.tdm.sync_mode);
+
+ rc = of_property_read_u32(tdm_parent_node,
+ "qcom,msm-cpudai-tdm-sync-src",
+ (u32 *)&dai_data->port_cfg.tdm.sync_src);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Sync Src from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-sync-src");
+ goto free_dai_data;
+ }
+ dev_dbg(&pdev->dev, "%s: Sync Src from DT file 0x%x\n",
+ __func__, dai_data->port_cfg.tdm.sync_src);
+
+ rc = of_property_read_u32(tdm_parent_node,
+ "qcom,msm-cpudai-tdm-data-out",
+ (u32 *)&dai_data->port_cfg.tdm.ctrl_data_out_enable);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Data Out from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-data-out");
+ goto free_dai_data;
+ }
+ dev_dbg(&pdev->dev, "%s: Data Out from DT file 0x%x\n",
+ __func__, dai_data->port_cfg.tdm.ctrl_data_out_enable);
+
+ rc = of_property_read_u32(tdm_parent_node,
+ "qcom,msm-cpudai-tdm-invert-sync",
+ (u32 *)&dai_data->port_cfg.tdm.ctrl_invert_sync_pulse);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Invert Sync from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-invert-sync");
+ goto free_dai_data;
+ }
+ dev_dbg(&pdev->dev, "%s: Invert Sync from DT file 0x%x\n",
+ __func__, dai_data->port_cfg.tdm.ctrl_invert_sync_pulse);
+
+ rc = of_property_read_u32(tdm_parent_node,
+ "qcom,msm-cpudai-tdm-data-delay",
+ (u32 *)&dai_data->port_cfg.tdm.ctrl_sync_data_delay);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Data Delay from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-data-delay");
+ goto free_dai_data;
+ }
+ dev_dbg(&pdev->dev, "%s: Data Delay from DT file 0x%x\n",
+ __func__, dai_data->port_cfg.tdm.ctrl_sync_data_delay);
+
+ /* TDM CFG -- set default */
+ dai_data->port_cfg.tdm.data_format = AFE_LINEAR_PCM_DATA;
+ dai_data->port_cfg.tdm.tdm_cfg_minor_version =
+ AFE_API_VERSION_TDM_CONFIG;
+
+ /* TDM SLOT MAPPING CFG */
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-data-align",
+ &dai_data->port_cfg.slot_mapping.data_align_type);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Data Align from DT file %s\n",
+ __func__,
+ "qcom,msm-cpudai-tdm-data-align");
+ goto free_dai_data;
+ }
+ dev_dbg(&pdev->dev, "%s: Data Align from DT file 0x%x\n",
+ __func__, dai_data->port_cfg.slot_mapping.data_align_type);
+
+ /* TDM SLOT MAPPING CFG -- set default */
+ dai_data->port_cfg.slot_mapping.minor_version =
+ AFE_API_VERSION_SLOT_MAPPING_CONFIG;
+
+ dai_data->port_cfg.slot_mapping_v2.minor_version =
+ AFE_API_VERSION_SLOT_MAPPING_CONFIG_V2;
+
+ /* CUSTOM TDM HEADER CFG */
+ custom_tdm_header = &dai_data->port_cfg.custom_tdm_header;
+ if (of_find_property(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-header-start-offset", NULL) &&
+ of_find_property(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-header-width", NULL) &&
+ of_find_property(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-header-num-frame-repeat", NULL)) {
+ /* if the property exist */
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-header-start-offset",
+ (u32 *)&custom_tdm_header->start_offset);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Header Start Offset from DT file %s\n",
+ __func__,
+ "qcom,msm-cpudai-tdm-header-start-offset");
+ goto free_dai_data;
+ }
+ dev_dbg(&pdev->dev, "%s: Header Start Offset from DT file 0x%x\n",
+ __func__, custom_tdm_header->start_offset);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-header-width",
+ (u32 *)&custom_tdm_header->header_width);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Header Width from DT file %s\n",
+ __func__, "qcom,msm-cpudai-tdm-header-width");
+ goto free_dai_data;
+ }
+ dev_dbg(&pdev->dev, "%s: Header Width from DT file 0x%x\n",
+ __func__, custom_tdm_header->header_width);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-cpudai-tdm-header-num-frame-repeat",
+ (u32 *)&custom_tdm_header->num_frame_repeat);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Header Num Frame Repeat from DT file %s\n",
+ __func__,
+ "qcom,msm-cpudai-tdm-header-num-frame-repeat");
+ goto free_dai_data;
+ }
+ dev_dbg(&pdev->dev, "%s: Header Num Frame Repeat from DT file 0x%x\n",
+ __func__, custom_tdm_header->num_frame_repeat);
+
+ /* CUSTOM TDM HEADER CFG -- set default */
+ custom_tdm_header->minor_version =
+ AFE_API_VERSION_CUSTOM_TDM_HEADER_CONFIG;
+ custom_tdm_header->header_type =
+ AFE_CUSTOM_TDM_HEADER_TYPE_INVALID;
+ } else {
+ dev_info(&pdev->dev,
+ "%s: Custom tdm header not supported\n", __func__);
+ /* CUSTOM TDM HEADER CFG -- set default */
+ custom_tdm_header->header_type =
+ AFE_CUSTOM_TDM_HEADER_TYPE_INVALID;
+ /* proceed with probe */
+ }
+
+ /* copy static clk per parent node */
+ dai_data->clk_set = tdm_clk_set;
+ /* copy static group cfg per parent node */
+ dai_data->group_cfg.tdm_cfg = tdm_group_cfg;
+ /* copy static num group ports per parent node */
+ dai_data->num_group_ports = num_tdm_group_ports;
+
+
+ dev_set_drvdata(&pdev->dev, dai_data);
+
+ port_idx = msm_dai_q6_get_port_idx(tdm_dev_id);
+ if (port_idx < 0) {
+ dev_err(&pdev->dev, "%s Port id 0x%x not supported\n",
+ __func__, tdm_dev_id);
+ rc = -EINVAL;
+ goto free_dai_data;
+ }
+
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_q6_tdm_dai_component,
+ &msm_dai_q6_tdm_dai[port_idx], 1);
+
+ if (rc) {
+ dev_err(&pdev->dev, "%s: TDM dai 0x%x register failed, rc=%d\n",
+ __func__, tdm_dev_id, rc);
+ goto err_register;
+ }
+
+ return 0;
+
+err_register:
+free_dai_data:
+ kfree(dai_data);
+rtn:
+ return rc;
+}
+
+static int msm_dai_q6_tdm_dev_remove(struct platform_device *pdev)
+{
+ struct msm_dai_q6_tdm_dai_data *dai_data =
+ dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_component(&pdev->dev);
+
+ kfree(dai_data);
+
+ return 0;
+}
+
+static const struct of_device_id msm_dai_q6_tdm_dev_dt_match[] = {
+ { .compatible = "qcom,msm-dai-q6-tdm", },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_q6_tdm_dev_dt_match);
+
+static struct platform_driver msm_dai_q6_tdm_driver = {
+ .probe = msm_dai_q6_tdm_dev_probe,
+ .remove = msm_dai_q6_tdm_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6-tdm",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_q6_tdm_dev_dt_match,
+ },
+};
+
+static int __init msm_dai_q6_init(void)
+{
+ int rc;
+
+ rc = platform_driver_register(&msm_auxpcm_dev_driver);
+ if (rc) {
+ pr_err("%s: fail to register auxpcm dev driver", __func__);
+ goto fail;
+ }
+
+ rc = platform_driver_register(&msm_dai_q6);
+ if (rc) {
+ pr_err("%s: fail to register dai q6 driver", __func__);
+ goto dai_q6_fail;
+ }
+
+ rc = platform_driver_register(&msm_dai_q6_dev);
+ if (rc) {
+ pr_err("%s: fail to register dai q6 dev driver", __func__);
+ goto dai_q6_dev_fail;
+ }
+
+ rc = platform_driver_register(&msm_dai_q6_mi2s_driver);
+ if (rc) {
+ pr_err("%s: fail to register dai MI2S dev drv\n", __func__);
+ goto dai_q6_mi2s_drv_fail;
+ }
+
+ rc = platform_driver_register(&msm_dai_mi2s_q6);
+ if (rc) {
+ pr_err("%s: fail to register dai MI2S\n", __func__);
+ goto dai_mi2s_q6_fail;
+ }
+
+ rc = platform_driver_register(&msm_dai_q6_spdif_driver);
+ if (rc) {
+ pr_err("%s: fail to register dai SPDIF\n", __func__);
+ goto dai_spdif_q6_fail;
+ }
+
+ rc = platform_driver_register(&msm_dai_q6_tdm_driver);
+ if (rc) {
+ pr_err("%s: fail to register dai TDM dev drv\n", __func__);
+ goto dai_q6_tdm_drv_fail;
+ }
+
+ rc = platform_driver_register(&msm_dai_tdm_q6);
+ if (rc) {
+ pr_err("%s: fail to register dai TDM\n", __func__);
+ goto dai_tdm_q6_fail;
+ }
+
+ rc = platform_driver_register(&msm_dai_group_mi2s_dev_driver);
+ if (rc) {
+ pr_err("%s: fail to register dai GMI2S dev drv\n",
+ __func__);
+ goto dai_q6_group_mi2s_dev_fail;
+ }
+
+ rc = platform_driver_register(&msm_dai_group_mi2s_driver);
+ if (rc) {
+ pr_err("%s: fail to register dai GMI2S\n",
+ __func__);
+ goto dai_group_mi2s_fail;
+ }
+
+ return rc;
+
+dai_group_mi2s_fail:
+ platform_driver_unregister(&msm_dai_group_mi2s_driver);
+dai_q6_group_mi2s_dev_fail:
+ platform_driver_unregister(&msm_dai_group_mi2s_dev_driver);
+dai_tdm_q6_fail:
+ platform_driver_unregister(&msm_dai_q6_tdm_driver);
+dai_q6_tdm_drv_fail:
+ platform_driver_unregister(&msm_dai_q6_spdif_driver);
+dai_spdif_q6_fail:
+ platform_driver_unregister(&msm_dai_mi2s_q6);
+dai_mi2s_q6_fail:
+ platform_driver_unregister(&msm_dai_q6_mi2s_driver);
+dai_q6_mi2s_drv_fail:
+ platform_driver_unregister(&msm_dai_q6_dev);
+dai_q6_dev_fail:
+ platform_driver_unregister(&msm_dai_q6);
+dai_q6_fail:
+ platform_driver_unregister(&msm_auxpcm_dev_driver);
+fail:
+ return rc;
+}
+module_init(msm_dai_q6_init);
+
+static void __exit msm_dai_q6_exit(void)
+{
+ platform_driver_unregister(&msm_dai_q6_dev);
+ platform_driver_unregister(&msm_dai_q6);
+ platform_driver_unregister(&msm_auxpcm_dev_driver);
+ platform_driver_unregister(&msm_dai_q6_spdif_driver);
+ platform_driver_unregister(&msm_dai_group_mi2s_driver);
+ platform_driver_unregister(&msm_dai_group_mi2s_dev_driver);
+}
+module_exit(msm_dai_q6_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-slim.c b/sound/soc/msm/qdsp6v2/msm-dai-slim.c
new file mode 100644
index 000000000000..64ecd7e031f0
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-slim.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/bitops.h>
+#include <linux/slimbus/slimbus.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/msm-slim-dma.h>
+
+#define SLIM_DEV_NAME "msm-dai-slim"
+
+#define SLIM_DAI_RATES (SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000 | \
+ SNDRV_PCM_RATE_384000)
+
+#define SLIM_DAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define DAI_STATE_INITIALIZED (0x01 << 0)
+#define DAI_STATE_PREPARED (0x01 << 1)
+#define DAI_STATE_RUNNING (0x01 << 2)
+
+#define SET_DAI_STATE(status, state) \
+ (status |= state)
+
+#define CLR_DAI_STATE(status, state) \
+ (status = status & (~state))
+
+enum {
+ MSM_DAI_SLIM0 = 0,
+ NUM_SLIM_DAIS,
+};
+
+struct msm_slim_dai_data {
+ unsigned int dai_id;
+ u16 *chan_h;
+ u16 *sh_ch;
+ u16 grph;
+ u32 rate;
+ u16 bits;
+ u16 ch_cnt;
+ u8 status;
+ struct snd_soc_dai_driver *dai_drv;
+ struct msm_slim_dma_data dma_data;
+ struct slim_port_cfg port_cfg;
+};
+
+struct msm_dai_slim_drv_data {
+ struct slim_device *sdev;
+ u16 num_dais;
+ struct msm_slim_dai_data slim_dai_data[NUM_SLIM_DAIS];
+};
+
+struct msm_slim_dai_data *msm_slim_get_dai_data(
+ struct msm_dai_slim_drv_data *drv_data,
+ struct snd_soc_dai *dai)
+{
+ struct msm_slim_dai_data *dai_data_t;
+ int i;
+
+ for (i = 0; i < drv_data->num_dais; i++) {
+ dai_data_t = &drv_data->slim_dai_data[i];
+ if (dai_data_t->dai_id == dai->id)
+ return dai_data_t;
+ }
+
+ dev_err(dai->dev,
+ "%s: no dai data found for dai_id %d\n",
+ __func__, dai->id);
+ return NULL;
+}
+
+static int msm_dai_slim_ch_ctl(struct msm_slim_dma_data *dma_data,
+ struct snd_soc_dai *dai, bool enable)
+{
+ struct slim_device *sdev;
+ struct msm_dai_slim_drv_data *drv_data;
+ struct msm_slim_dai_data *dai_data;
+ int rc, rc1, i;
+
+ if (!dma_data || !dma_data->sdev) {
+ pr_err("%s: Invalid %s\n", __func__,
+ (!dma_data) ? "dma_data" : "slim_device");
+ return -EINVAL;
+ }
+
+ sdev = dma_data->sdev;
+ drv_data = dev_get_drvdata(&sdev->dev);
+ dai_data = msm_slim_get_dai_data(drv_data, dai);
+
+ if (!dai_data) {
+ dev_err(dai->dev,
+ "%s: Invalid dai_data for dai_id %d\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ dev_dbg(&sdev->dev,
+ "%s: enable = %s, rate = %u\n", __func__,
+ enable ? "true" : "false",
+ dai_data->rate);
+
+ if (enable) {
+ if (!(dai_data->status & DAI_STATE_PREPARED)) {
+ dev_err(&sdev->dev,
+ "%s: dai id (%d) has invalid state 0x%x\n",
+ __func__, dai->id, dai_data->status);
+ return -EINVAL;
+ }
+
+ rc = slim_alloc_mgrports(sdev,
+ SLIM_REQ_DEFAULT, dai_data->ch_cnt,
+ &(dma_data->ph),
+ sizeof(dma_data->ph));
+
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(&sdev->dev,
+ "%s:alloc mgrport failed rc %d\n",
+ __func__ , rc);
+ goto done;
+ }
+
+ rc = slim_config_mgrports(sdev, &(dma_data->ph),
+ dai_data->ch_cnt,
+ &(dai_data->port_cfg));
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(&sdev->dev,
+ "%s: config mgrport failed rc %d\n",
+ __func__ , rc);
+ goto err_done;
+ }
+
+ for (i = 0; i < dai_data->ch_cnt; i++) {
+ rc = slim_connect_sink(sdev,
+ &dma_data->ph, 1,
+ dai_data->chan_h[i]);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(&sdev->dev,
+ "%s: slim_connect_sink failed, ch = %d, err = %d\n",
+ __func__, i, rc);
+ goto err_done;
+ }
+ }
+
+ rc = slim_control_ch(sdev,
+ dai_data->grph,
+ SLIM_CH_ACTIVATE, true);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(&sdev->dev,
+ "%s: slim activate ch failed, err = %d\n",
+ __func__, rc);
+ goto err_done;
+ }
+ /* Mark dai status as running */
+ SET_DAI_STATE(dai_data->status, DAI_STATE_RUNNING);
+ } else {
+ if (!(dai_data->status & DAI_STATE_RUNNING)) {
+ dev_err(&sdev->dev,
+ "%s: dai id (%d) has invalid state 0x%x\n",
+ __func__, dai->id, dai_data->status);
+ return -EINVAL;
+ }
+
+ rc = slim_control_ch(sdev,
+ dai_data->grph,
+ SLIM_CH_REMOVE, true);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(&sdev->dev,
+ "%s: slim activate ch failed, err = %d\n",
+ __func__, rc);
+ goto done;
+ }
+
+ rc = slim_dealloc_mgrports(sdev,
+ &dma_data->ph, 1);
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(&sdev->dev,
+ "%s: dealloc mgrport failed, err = %d\n",
+ __func__, rc);
+ goto done;
+ }
+ /* clear running state for dai*/
+ CLR_DAI_STATE(dai_data->status, DAI_STATE_RUNNING);
+ }
+
+ return rc;
+
+err_done:
+ rc1 = slim_dealloc_mgrports(sdev,
+ &dma_data->ph, 1);
+ if (IS_ERR_VALUE(rc1))
+ dev_err(&sdev->dev,
+ "%s: dealloc mgrport failed, err = %d\n",
+ __func__, rc1);
+done:
+ return rc;
+}
+
+static int msm_dai_slim_hw_params(
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev);
+ struct msm_slim_dai_data *dai_data;
+ int rc = 0;
+
+ dai_data = msm_slim_get_dai_data(drv_data, dai);
+ if (!dai_data) {
+ dev_err(dai->dev,
+ "%s: Invalid dai_data for dai_id %d\n",
+ __func__, dai->id);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (!dai_data->ch_cnt || dai_data->ch_cnt != params_channels(params)) {
+ dev_err(dai->dev, "%s: invalid ch_cnt %d %d\n",
+ __func__, dai_data->ch_cnt, params_channels(params));
+ rc = -EINVAL;
+ goto done;
+ }
+
+ dai_data->rate = params_rate(params);
+ dai_data->port_cfg.port_opts = SLIM_OPT_NONE;
+ if (dai_data->rate >= SNDRV_PCM_RATE_48000)
+ dai_data->port_cfg.watermark = 16;
+ else
+ dai_data->port_cfg.watermark = 8;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dai_data->bits = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dai_data->bits = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dai_data->bits = 32;
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid format %d\n", __func__,
+ params_format(params));
+ rc = -EINVAL;
+ goto done;
+ }
+
+ dev_dbg(dai->dev, "%s: ch_cnt=%u rate=%u, bit_width = %u\n",
+ __func__, dai_data->ch_cnt, dai_data->rate,
+ dai_data->bits);
+done:
+ return rc;
+}
+
+static int msm_dai_slim_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev);
+ struct msm_slim_dai_data *dai_data;
+ struct snd_soc_dai_driver *dai_drv;
+ u8 i = 0;
+
+ dev_dbg(dai->dev,
+ "%s: tx_num=%u, rx_num=%u\n",
+ __func__, tx_num, rx_num);
+
+ dai_data = msm_slim_get_dai_data(drv_data, dai);
+ if (!dai_data) {
+ dev_err(dai->dev,
+ "%s: Invalid dai_data for dai_id %d\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ dai_drv = dai_data->dai_drv;
+
+ if (tx_num > dai_drv->capture.channels_max) {
+ dev_err(dai->dev, "%s: tx_num %u max out master port cnt\n",
+ __func__, tx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < tx_num; i++)
+ dai_data->sh_ch[i] = tx_slot[i];
+
+ dai_data->ch_cnt = tx_num;
+ return 0;
+}
+
+static int msm_dai_slim_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev);
+ struct msm_slim_dma_data *dma_data;
+ struct msm_slim_dai_data *dai_data = NULL;
+ struct slim_ch prop;
+ int rc;
+ u8 i, j;
+
+ dai_data = msm_slim_get_dai_data(drv_data, dai);
+ if (!dai_data) {
+ dev_err(dai->dev,
+ "%s: Invalid dai_data for dai %d\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ if (!(dai_data->status & DAI_STATE_INITIALIZED)) {
+ dev_err(dai->dev,
+ "%s: dai id (%d) has invalid state 0x%x\n",
+ __func__, dai->id, dai_data->status);
+ return -EINVAL;
+ }
+
+ if (dai_data->status & DAI_STATE_PREPARED) {
+ dev_dbg(dai->dev,
+ "%s: dai id (%d) has already prepared.\n",
+ __func__, dai->id);
+ return 0;
+ }
+
+ dma_data = &dai_data->dma_data;
+ snd_soc_dai_set_dma_data(dai, substream, dma_data);
+
+ for (i = 0; i < dai_data->ch_cnt; i++) {
+ rc = slim_query_ch(drv_data->sdev, dai_data->sh_ch[i],
+ &dai_data->chan_h[i]);
+ if (rc) {
+ dev_err(dai->dev, "%s:query chan handle failed rc %d\n",
+ __func__ , rc);
+ goto error_chan_query;
+ }
+ }
+
+ prop.prot = SLIM_AUTO_ISO;
+ prop.baser = SLIM_RATE_4000HZ;
+ prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+ prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+ prop.ratem = (dai_data->rate/4000);
+ prop.sampleszbits = dai_data->bits;
+
+ rc = slim_define_ch(drv_data->sdev, &prop, dai_data->chan_h,
+ dai_data->ch_cnt, true, &dai_data->grph);
+
+ if (rc) {
+ dev_err(dai->dev, "%s:define chan failed rc %d\n",
+ __func__ , rc);
+ goto error_define_chan;
+ }
+
+ /* Mark stream status as prepared */
+ SET_DAI_STATE(dai_data->status, DAI_STATE_PREPARED);
+
+ return rc;
+
+error_define_chan:
+error_chan_query:
+ for (j = 0; j < i; j++)
+ slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[j]);
+ return rc;
+}
+
+static void msm_dai_slim_shutdown(struct snd_pcm_substream *stream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev);
+ struct msm_slim_dma_data *dma_data = NULL;
+ struct msm_slim_dai_data *dai_data;
+ int i, rc = 0;
+
+ dai_data = msm_slim_get_dai_data(drv_data, dai);
+ dma_data = snd_soc_dai_get_dma_data(dai, stream);
+ if (!dma_data || !dai_data) {
+ dev_err(dai->dev,
+ "%s: Invalid %s\n", __func__,
+ (!dma_data) ? "dma_data" : "dai_data");
+ return;
+ }
+
+ if ((!(dai_data->status & DAI_STATE_PREPARED)) ||
+ dai_data->status & DAI_STATE_RUNNING) {
+ dev_err(dai->dev,
+ "%s: dai id (%d) has invalid state 0x%x\n",
+ __func__, dai->id, dai_data->status);
+ return;
+ }
+
+ for (i = 0; i < dai_data->ch_cnt; i++) {
+ rc = slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[i]);
+ if (rc) {
+ dev_err(dai->dev,
+ "%s: dealloc_ch failed, err = %d\n",
+ __func__, rc);
+ }
+ }
+
+ snd_soc_dai_set_dma_data(dai, stream, NULL);
+ /* clear prepared state for the dai */
+ CLR_DAI_STATE(dai_data->status, DAI_STATE_PREPARED);
+
+ return;
+}
+
+static const struct snd_soc_component_driver msm_dai_slim_component = {
+ .name = "msm-dai-slim-cmpnt",
+};
+
+static struct snd_soc_dai_ops msm_dai_slim_ops = {
+ .prepare = msm_dai_slim_prepare,
+ .hw_params = msm_dai_slim_hw_params,
+ .shutdown = msm_dai_slim_shutdown,
+ .set_channel_map = msm_dai_slim_set_channel_map,
+};
+
+static struct snd_soc_dai_driver msm_slim_dais[] = {
+ {
+ /*
+ * The first dai name should be same as device name
+ * to support registering single and multile dais.
+ */
+ .name = SLIM_DEV_NAME,
+ .id = MSM_DAI_SLIM0,
+ .capture = {
+ .rates = SLIM_DAI_RATES,
+ .formats = SLIM_DAI_FORMATS,
+ .channels_min = 1,
+ /*
+ * max channels allowed is
+ * dependent on platform and
+ * will be updated before this
+ * dai driver is registered.
+ */
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .stream_name = "SLIM_DAI0 Capture",
+ },
+ .ops = &msm_dai_slim_ops,
+ },
+ /*
+ * If multiple dais are needed,
+ * add dais here and update the
+ * dai_id enum.
+ */
+};
+
+static void msm_dai_slim_remove_dai_data(
+ struct device *dev,
+ struct msm_dai_slim_drv_data *drv_data)
+{
+ int i;
+ struct msm_slim_dai_data *dai_data_t;
+
+ for (i = 0; i < drv_data->num_dais; i++) {
+ dai_data_t = &drv_data->slim_dai_data[i];
+
+ kfree(dai_data_t->chan_h);
+ dai_data_t->chan_h = NULL;
+ kfree(dai_data_t->sh_ch);
+ dai_data_t->sh_ch = NULL;
+ }
+}
+
+static int msm_dai_slim_populate_dai_data(struct device *dev,
+ struct msm_dai_slim_drv_data *drv_data)
+{
+ struct snd_soc_dai_driver *dai_drv;
+ struct msm_slim_dai_data *dai_data_t;
+ u8 num_ch;
+ int i, j, rc;
+
+ for (i = 0; i < drv_data->num_dais; i++) {
+ num_ch = 0;
+ dai_drv = &msm_slim_dais[i];
+ num_ch += dai_drv->capture.channels_max;
+ num_ch += dai_drv->playback.channels_max;
+
+ dai_data_t = &drv_data->slim_dai_data[i];
+ dai_data_t->dai_drv = dai_drv;
+ dai_data_t->dai_id = dai_drv->id;
+ dai_data_t->dma_data.sdev = drv_data->sdev;
+ dai_data_t->dma_data.dai_channel_ctl =
+ msm_dai_slim_ch_ctl;
+ SET_DAI_STATE(dai_data_t->status,
+ DAI_STATE_INITIALIZED);
+
+ dai_data_t->chan_h = devm_kzalloc(dev,
+ sizeof(u16) * num_ch,
+ GFP_KERNEL);
+ if (!dai_data_t->chan_h) {
+ dev_err(dev,
+ "%s: DAI ID %d, Failed to alloc channel handles\n",
+ __func__, i);
+ rc = -ENOMEM;
+ goto err_mem_alloc;
+ }
+
+ dai_data_t->sh_ch = devm_kzalloc(dev,
+ sizeof(u16) * num_ch,
+ GFP_KERNEL);
+ if (!dai_data_t->sh_ch) {
+ dev_err(dev,
+ "%s: DAI ID %d, Failed to alloc sh_ch\n",
+ __func__, i);
+ rc = -ENOMEM;
+ goto err_mem_alloc;
+ }
+ }
+ return 0;
+
+err_mem_alloc:
+ for (j = 0; j < i; j++) {
+ dai_data_t = &drv_data->slim_dai_data[i];
+
+ devm_kfree(dev, dai_data_t->chan_h);
+ dai_data_t->chan_h = NULL;
+
+ devm_kfree(dev, dai_data_t->sh_ch);
+ dai_data_t->sh_ch = NULL;
+ }
+ return rc;
+}
+
+static int msm_dai_slim_dev_probe(struct slim_device *sdev)
+{
+ int rc, i;
+ u8 max_channels;
+ u32 apps_ch_pipes;
+ struct msm_dai_slim_drv_data *drv_data;
+ struct device *dev = &sdev->dev;
+ struct snd_soc_dai_driver *dai_drv;
+
+ if (!dev->of_node ||
+ !dev->of_node->parent) {
+ dev_err(dev,
+ "%s: Invalid %s\n", __func__,
+ (!dev->of_node) ? "of_node" : "parent_of_node");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(dev->of_node->parent,
+ "qcom,apps-ch-pipes",
+ &apps_ch_pipes);
+ if (rc) {
+ dev_err(dev,
+ "%s: Failed to lookup property %s in node %s, err = %d\n",
+ __func__, "qcom,apps-ch-pipes",
+ dev->of_node->parent->full_name, rc);
+ goto err_ret;
+ }
+
+ max_channels = hweight_long(apps_ch_pipes);
+ if (max_channels <= 0) {
+ dev_err(dev,
+ "%s: Invalid apps owned ports %d\n",
+ __func__, max_channels);
+ goto err_ret;
+ }
+
+ dev_dbg(dev, "%s: max channels = %u\n",
+ __func__, max_channels);
+
+ for (i = 0; i < ARRAY_SIZE(msm_slim_dais); i++) {
+ dai_drv = &msm_slim_dais[i];
+ dai_drv->capture.channels_max = max_channels;
+ dai_drv->playback.channels_max = max_channels;
+ }
+
+ drv_data = devm_kzalloc(dev, sizeof(*drv_data),
+ GFP_KERNEL);
+ if (!drv_data) {
+ dev_err(dev, "%s: dai driver struct alloc failed\n",
+ __func__);
+ rc = -ENOMEM;
+ goto err_ret;
+ }
+
+ drv_data->sdev = sdev;
+ drv_data->num_dais = NUM_SLIM_DAIS;
+
+ rc = msm_dai_slim_populate_dai_data(dev, drv_data);
+ if (rc) {
+ dev_err(dev,
+ "%s: failed to setup dai_data, err = %d\n",
+ __func__, rc);
+ goto err_populate_dai;
+ }
+
+ rc = snd_soc_register_component(&sdev->dev, &msm_dai_slim_component,
+ msm_slim_dais, NUM_SLIM_DAIS);
+
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dev, "%s: failed to register DAI, err = %d\n",
+ __func__, rc);
+ goto err_reg_comp;
+ }
+
+ dev_set_drvdata(dev, drv_data);
+ return rc;
+
+err_reg_comp:
+ msm_dai_slim_remove_dai_data(dev, drv_data);
+
+err_populate_dai:
+ devm_kfree(dev, drv_data);
+
+err_ret:
+ return rc;
+}
+
+static int msm_dai_slim_dev_remove(struct slim_device *sdev)
+{
+ snd_soc_unregister_component(&sdev->dev);
+ return 0;
+}
+
+static const struct slim_device_id msm_dai_slim_dt_match[] = {
+ {SLIM_DEV_NAME, 0 },
+ {}
+};
+
+static struct slim_driver msm_dai_slim_driver = {
+ .driver = {
+ .name = SLIM_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_dai_slim_dev_probe,
+ .remove = msm_dai_slim_dev_remove,
+ .id_table = msm_dai_slim_dt_match,
+};
+
+static int __init msm_dai_slim_init(void)
+{
+ int rc;
+ rc = slim_driver_register(&msm_dai_slim_driver);
+ if (rc)
+ pr_err("%s: failed to register with slimbus driver rc = %d",
+ __func__, rc);
+ return rc;
+}
+module_init(msm_dai_slim_init);
+
+static void __exit msm_dai_slim_exit(void)
+{
+ return;
+}
+module_exit(msm_dai_slim_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("Slimbus apps-owned channel handling driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c
new file mode 100644
index 000000000000..616a1b22b0c3
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c
@@ -0,0 +1,394 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+enum {
+ STUB_RX,
+ STUB_TX,
+ STUB_1_RX,
+ STUB_1_TX,
+ STUB_DTMF_TX,
+ STUB_HOST_RX_CAPTURE_TX,
+ STUB_HOST_RX_PLAYBACK_RX,
+ STUB_HOST_TX_CAPTURE_TX,
+ STUB_HOST_TX_PLAYBACK_RX,
+};
+
+static int msm_dai_stub_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ pr_debug("%s:\n", __func__);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_dai_stub_ops = {
+ .set_channel_map = msm_dai_stub_set_channel_map,
+};
+
+static int msm_dai_stub_add_route(struct snd_soc_dai *dai)
+{
+ struct snd_soc_dapm_route intercon;
+ struct snd_soc_dapm_context *dapm;
+
+ if (!dai || !dai->driver) {
+ pr_err("%s Invalid params\n", __func__);
+ return -EINVAL;
+ }
+ dapm = snd_soc_component_get_dapm(dai->component);
+ memset(&intercon, 0 , sizeof(intercon));
+ if (dai->driver->playback.stream_name &&
+ dai->driver->playback.aif_name) {
+ dev_dbg(dai->dev, "%s add route for widget %s",
+ __func__, dai->driver->playback.stream_name);
+ intercon.source = dai->driver->playback.aif_name;
+ intercon.sink = dai->driver->playback.stream_name;
+ dev_dbg(dai->dev, "%s src %s sink %s\n",
+ __func__, intercon.source, intercon.sink);
+ snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ }
+ if (dai->driver->capture.stream_name &&
+ dai->driver->capture.aif_name) {
+ dev_dbg(dai->dev, "%s add route for widget %s",
+ __func__, dai->driver->capture.stream_name);
+ intercon.sink = dai->driver->capture.aif_name;
+ intercon.source = dai->driver->capture.stream_name;
+ dev_dbg(dai->dev, "%s src %s sink %s\n",
+ __func__, intercon.source, intercon.sink);
+ snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ }
+ return 0;
+}
+
+static int msm_dai_stub_dai_probe(struct snd_soc_dai *dai)
+{
+ return msm_dai_stub_add_route(dai);
+}
+
+static int msm_dai_stub_dai_remove(struct snd_soc_dai *dai)
+{
+ pr_debug("%s:\n", __func__);
+ return 0;
+}
+
+static struct snd_soc_dai_driver msm_dai_stub_dai_rx = {
+ .playback = {
+ .stream_name = "Stub Playback",
+ .aif_name = "STUB_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_stub_ops,
+ .probe = &msm_dai_stub_dai_probe,
+ .remove = &msm_dai_stub_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_stub_dai_tx[] = {
+ {
+ .capture = {
+ .stream_name = "Stub Capture",
+ .aif_name = "STUB_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_stub_ops,
+ .probe = &msm_dai_stub_dai_probe,
+ .remove = &msm_dai_stub_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "Stub1 Capture",
+ .aif_name = "STUB_1_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_stub_ops,
+ .probe = &msm_dai_stub_dai_probe,
+ .remove = &msm_dai_stub_dai_remove,
+ }
+};
+
+static struct snd_soc_dai_driver msm_dai_stub_dtmf_tx_dai = {
+ .capture = {
+ .stream_name = "DTMF TX",
+ .aif_name = "STUB_DTMF_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_stub_ops,
+ .probe = &msm_dai_stub_dai_probe,
+ .remove = &msm_dai_stub_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_stub_host_capture_tx_dai[] = {
+ {
+ .capture = {
+ .stream_name = "CS-VOICE HOST RX CAPTURE",
+ .aif_name = "STUB_HOST_RX_CAPTURE_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_stub_ops,
+ .probe = &msm_dai_stub_dai_probe,
+ .remove = &msm_dai_stub_dai_remove,
+ },
+ {
+ .capture = {
+ .stream_name = "CS-VOICE HOST TX CAPTURE",
+ .aif_name = "STUB_HOST_TX_CAPTURE_TX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_stub_ops,
+ .probe = &msm_dai_stub_dai_probe,
+ .remove = &msm_dai_stub_dai_remove,
+ },
+};
+
+static struct snd_soc_dai_driver msm_dai_stub_host_playback_rx_dai[] = {
+ {
+ .playback = {
+ .stream_name = "CS-VOICE HOST RX PLAYBACK",
+ .aif_name = "STUB_HOST_RX_PLAYBACK_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_stub_ops,
+ .probe = &msm_dai_stub_dai_probe,
+ .remove = &msm_dai_stub_dai_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "CS-VOICE HOST TX PLAYBACK",
+ .aif_name = "STUB_HOST_TX_PLAYBACK_RX",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_stub_ops,
+ .probe = &msm_dai_stub_dai_probe,
+ .remove = &msm_dai_stub_dai_remove,
+ },
+};
+
+static const struct snd_soc_component_driver msm_dai_stub_component = {
+ .name = "msm-dai-stub-dev",
+};
+
+static int msm_dai_stub_dev_probe(struct platform_device *pdev)
+{
+ int rc, id = -1;
+ const char *stub_dev_id = "qcom,msm-dai-stub-dev-id";
+
+ rc = of_property_read_u32(pdev->dev.of_node, stub_dev_id, &id);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: missing %s in dt node\n", __func__, stub_dev_id);
+ return rc;
+ }
+
+ pdev->id = id;
+
+ pr_debug("%s: dev name %s, id:%d\n", __func__,
+ dev_name(&pdev->dev), pdev->id);
+
+ switch (id) {
+ case STUB_RX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_stub_component, &msm_dai_stub_dai_rx, 1);
+ break;
+ case STUB_TX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_stub_component, &msm_dai_stub_dai_tx[0], 1);
+ break;
+ case STUB_1_TX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_stub_component, &msm_dai_stub_dai_tx[1], 1);
+ break;
+ case STUB_DTMF_TX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_stub_component,
+ &msm_dai_stub_dtmf_tx_dai, 1);
+ break;
+ case STUB_HOST_RX_CAPTURE_TX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_stub_component,
+ &msm_dai_stub_host_capture_tx_dai[0], 1);
+ break;
+ case STUB_HOST_TX_CAPTURE_TX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_stub_component,
+ &msm_dai_stub_host_capture_tx_dai[1], 1);
+ break;
+ case STUB_HOST_RX_PLAYBACK_RX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_stub_component,
+ &msm_dai_stub_host_playback_rx_dai[0], 1);
+ break;
+ case STUB_HOST_TX_PLAYBACK_RX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_stub_component,
+ &msm_dai_stub_host_playback_rx_dai[1], 1);
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_dai_stub_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_dai_stub_dev_dt_match[] = {
+ { .compatible = "qcom,msm-dai-stub-dev", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, msm_dai_stub_dev_dt_match);
+
+static struct platform_driver msm_dai_stub_dev = {
+ .probe = msm_dai_stub_dev_probe,
+ .remove = msm_dai_stub_dev_remove,
+ .driver = {
+ .name = "msm-dai-stub-dev",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_stub_dev_dt_match,
+ },
+};
+
+static int msm_dai_stub_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+ __func__, rc);
+ } else
+ dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
+
+ return rc;
+}
+
+static int msm_dai_stub_remove(struct platform_device *pdev)
+{
+ pr_debug("%s:\n", __func__);
+
+ return 0;
+}
+
+static const struct of_device_id msm_dai_stub_dt_match[] = {
+ {.compatible = "qcom,msm-dai-stub"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_stub_dt_match);
+
+
+static struct platform_driver msm_dai_stub_driver = {
+ .probe = msm_dai_stub_probe,
+ .remove = msm_dai_stub_remove,
+ .driver = {
+ .name = "msm-dai-stub",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_stub_dt_match,
+ },
+};
+
+static int __init msm_dai_stub_init(void)
+{
+ int rc = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ rc = platform_driver_register(&msm_dai_stub_driver);
+ if (rc) {
+ pr_err("%s: fail to register dai q6 driver", __func__);
+ goto fail;
+ }
+
+ rc = platform_driver_register(&msm_dai_stub_dev);
+ if (rc) {
+ pr_err("%s: fail to register dai q6 dev driver", __func__);
+ goto dai_stub_dev_fail;
+ }
+ return rc;
+
+dai_stub_dev_fail:
+ platform_driver_unregister(&msm_dai_stub_driver);
+fail:
+ return rc;
+}
+module_init(msm_dai_stub_init);
+
+static void __exit msm_dai_stub_exit(void)
+{
+ pr_debug("%s:\n", __func__);
+
+ platform_driver_unregister(&msm_dai_stub_dev);
+ platform_driver_unregister(&msm_dai_stub_driver);
+}
+module_exit(msm_dai_stub_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM Stub DSP DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-common.h b/sound/soc/msm/qdsp6v2/msm-dolby-common.h
new file mode 100644
index 000000000000..f14e42e3faa0
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dolby-common.h
@@ -0,0 +1,266 @@
+
+/* Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_DOLBY_COMMON_H_
+#define _MSM_DOLBY_COMMON_H_
+
+#include <sound/soc.h>
+
+
+#define DOLBY_BUNDLE_MODULE_ID 0x00010723
+#define DOLBY_VISUALIZER_MODULE_ID 0x0001072B
+
+#define DOLBY_PARAM_ID_VDHE 0x0001074D
+#define DOLBY_PARAM_ID_VSPE 0x00010750
+#define DOLBY_PARAM_ID_DSSF 0x00010753
+#define DOLBY_PARAM_ID_DVLI 0x0001073E
+#define DOLBY_PARAM_ID_DVLO 0x0001073F
+#define DOLBY_PARAM_ID_DVLE 0x0001073C
+#define DOLBY_PARAM_ID_DVMC 0x00010741
+#define DOLBY_PARAM_ID_DVME 0x00010740
+#define DOLBY_PARAM_ID_IENB 0x00010744
+#define DOLBY_PARAM_ID_IEBF 0x00010745
+#define DOLBY_PARAM_ID_IEON 0x00010743
+#define DOLBY_PARAM_ID_DEON 0x00010738
+#define DOLBY_PARAM_ID_NGON 0x00010736
+#define DOLBY_PARAM_ID_GEON 0x00010748
+#define DOLBY_PARAM_ID_GENB 0x00010749
+#define DOLBY_PARAM_ID_GEBF 0x0001074A
+#define DOLBY_PARAM_ID_AONB 0x0001075B
+#define DOLBY_PARAM_ID_AOBF 0x0001075C
+#define DOLBY_PARAM_ID_AOBG 0x0001075D
+#define DOLBY_PARAM_ID_AOON 0x00010759
+#define DOLBY_PARAM_ID_ARNB 0x0001075F
+#define DOLBY_PARAM_ID_ARBF 0x00010760
+#define DOLBY_PARAM_ID_PLB 0x00010768
+#define DOLBY_PARAM_ID_PLMD 0x00010767
+#define DOLBY_PARAM_ID_DHSB 0x0001074E
+#define DOLBY_PARAM_ID_DHRG 0x0001074F
+#define DOLBY_PARAM_ID_DSSB 0x00010751
+#define DOLBY_PARAM_ID_DSSA 0x00010752
+#define DOLBY_PARAM_ID_DVLA 0x0001073D
+#define DOLBY_PARAM_ID_IEBT 0x00010746
+#define DOLBY_PARAM_ID_IEA 0x0001076A
+#define DOLBY_PARAM_ID_DEA 0x00010739
+#define DOLBY_PARAM_ID_DED 0x0001073A
+#define DOLBY_PARAM_ID_GEBG 0x0001074B
+#define DOLBY_PARAM_ID_AOCC 0x0001075A
+#define DOLBY_PARAM_ID_ARBI 0x00010761
+#define DOLBY_PARAM_ID_ARBL 0x00010762
+#define DOLBY_PARAM_ID_ARBH 0x00010763
+#define DOLBY_PARAM_ID_AROD 0x00010764
+#define DOLBY_PARAM_ID_ARTP 0x00010765
+#define DOLBY_PARAM_ID_VMON 0x00010756
+#define DOLBY_PARAM_ID_VMB 0x00010757
+#define DOLBY_PARAM_ID_VCNB 0x00010733
+#define DOLBY_PARAM_ID_VCBF 0x00010734
+#define DOLBY_PARAM_ID_PREG 0x00010728
+#define DOLBY_PARAM_ID_VEN 0x00010732
+#define DOLBY_PARAM_ID_PSTG 0x00010729
+#define DOLBY_PARAM_ID_INIT_ENDP 0x00010727
+
+/* Not Used with Set Param kcontrol, only to query using Get Param */
+#define DOLBY_PARAM_ID_VER 0x00010726
+
+#define DOLBY_PARAM_ID_VCBG 0x00010730
+#define DOLBY_PARAM_ID_VCBE 0x00010731
+
+/* DOLBY DAP control params */
+#define DOLBY_COMMIT_ALL_TO_DSP 0x70000001
+#define DOLBY_COMMIT_TO_DSP 0x70000002
+#define DOLBY_USE_CACHE 0x70000003
+#define DOLBY_AUTO_ENDP 0x70000004
+#define DOLBY_AUTO_ENDDEP_PARAMS 0x70000005
+#define DOLBY_DAP_BYPASS 0x70000006
+
+#define DOLBY_ENABLE_CUSTOM_STEREO 0x000108c7
+
+/* DOLBY DAP offsets start */
+#define DOLBY_PARAM_VDHE_LENGTH 1
+#define DOLBY_PARAM_VDHE_OFFSET 0
+#define DOLBY_PARAM_VSPE_LENGTH 1
+#define DOLBY_PARAM_VSPE_OFFSET (DOLBY_PARAM_VDHE_OFFSET + \
+ DOLBY_PARAM_VDHE_LENGTH)
+#define DOLBY_PARAM_DSSF_LENGTH 1
+#define DOLBY_PARAM_DSSF_OFFSET (DOLBY_PARAM_VSPE_OFFSET + \
+ DOLBY_PARAM_VSPE_LENGTH)
+#define DOLBY_PARAM_DVLI_LENGTH 1
+#define DOLBY_PARAM_DVLI_OFFSET (DOLBY_PARAM_DSSF_OFFSET + \
+ DOLBY_PARAM_DSSF_LENGTH)
+#define DOLBY_PARAM_DVLO_LENGTH 1
+#define DOLBY_PARAM_DVLO_OFFSET (DOLBY_PARAM_DVLI_OFFSET + \
+ DOLBY_PARAM_DVLI_LENGTH)
+#define DOLBY_PARAM_DVLE_LENGTH 1
+#define DOLBY_PARAM_DVLE_OFFSET (DOLBY_PARAM_DVLO_OFFSET + \
+ DOLBY_PARAM_DVLO_LENGTH)
+#define DOLBY_PARAM_DVMC_LENGTH 1
+#define DOLBY_PARAM_DVMC_OFFSET (DOLBY_PARAM_DVLE_OFFSET + \
+ DOLBY_PARAM_DVLE_LENGTH)
+#define DOLBY_PARAM_DVME_LENGTH 1
+#define DOLBY_PARAM_DVME_OFFSET (DOLBY_PARAM_DVMC_OFFSET + \
+ DOLBY_PARAM_DVMC_LENGTH)
+#define DOLBY_PARAM_IENB_LENGTH 1
+#define DOLBY_PARAM_IENB_OFFSET (DOLBY_PARAM_DVME_OFFSET + \
+ DOLBY_PARAM_DVME_LENGTH)
+#define DOLBY_PARAM_IEBF_LENGTH 40
+#define DOLBY_PARAM_IEBF_OFFSET (DOLBY_PARAM_IENB_OFFSET + \
+ DOLBY_PARAM_IENB_LENGTH)
+#define DOLBY_PARAM_IEON_LENGTH 1
+#define DOLBY_PARAM_IEON_OFFSET (DOLBY_PARAM_IEBF_OFFSET + \
+ DOLBY_PARAM_IEBF_LENGTH)
+#define DOLBY_PARAM_DEON_LENGTH 1
+#define DOLBY_PARAM_DEON_OFFSET (DOLBY_PARAM_IEON_OFFSET + \
+ DOLBY_PARAM_IEON_LENGTH)
+#define DOLBY_PARAM_NGON_LENGTH 1
+#define DOLBY_PARAM_NGON_OFFSET (DOLBY_PARAM_DEON_OFFSET + \
+ DOLBY_PARAM_DEON_LENGTH)
+#define DOLBY_PARAM_GEON_LENGTH 1
+#define DOLBY_PARAM_GEON_OFFSET (DOLBY_PARAM_NGON_OFFSET + \
+ DOLBY_PARAM_NGON_LENGTH)
+#define DOLBY_PARAM_GENB_LENGTH 1
+#define DOLBY_PARAM_GENB_OFFSET (DOLBY_PARAM_GEON_OFFSET + \
+ DOLBY_PARAM_GEON_LENGTH)
+#define DOLBY_PARAM_GEBF_LENGTH 40
+#define DOLBY_PARAM_GEBF_OFFSET (DOLBY_PARAM_GENB_OFFSET + \
+ DOLBY_PARAM_GENB_LENGTH)
+#define DOLBY_PARAM_AONB_LENGTH 1
+#define DOLBY_PARAM_AONB_OFFSET (DOLBY_PARAM_GEBF_OFFSET + \
+ DOLBY_PARAM_GEBF_LENGTH)
+#define DOLBY_PARAM_AOBF_LENGTH 40
+#define DOLBY_PARAM_AOBF_OFFSET (DOLBY_PARAM_AONB_OFFSET + \
+ DOLBY_PARAM_AONB_LENGTH)
+#define DOLBY_PARAM_AOBG_LENGTH 329
+#define DOLBY_PARAM_AOBG_OFFSET (DOLBY_PARAM_AOBF_OFFSET + \
+ DOLBY_PARAM_AOBF_LENGTH)
+#define DOLBY_PARAM_AOON_LENGTH 1
+#define DOLBY_PARAM_AOON_OFFSET (DOLBY_PARAM_AOBG_OFFSET + \
+ DOLBY_PARAM_AOBG_LENGTH)
+#define DOLBY_PARAM_ARNB_LENGTH 1
+#define DOLBY_PARAM_ARNB_OFFSET (DOLBY_PARAM_AOON_OFFSET + \
+ DOLBY_PARAM_AOON_LENGTH)
+#define DOLBY_PARAM_ARBF_LENGTH 40
+#define DOLBY_PARAM_ARBF_OFFSET (DOLBY_PARAM_ARNB_OFFSET + \
+ DOLBY_PARAM_ARNB_LENGTH)
+#define DOLBY_PARAM_PLB_LENGTH 1
+#define DOLBY_PARAM_PLB_OFFSET (DOLBY_PARAM_ARBF_OFFSET + \
+ DOLBY_PARAM_ARBF_LENGTH)
+#define DOLBY_PARAM_PLMD_LENGTH 1
+#define DOLBY_PARAM_PLMD_OFFSET (DOLBY_PARAM_PLB_OFFSET + \
+ DOLBY_PARAM_PLB_LENGTH)
+#define DOLBY_PARAM_DHSB_LENGTH 1
+#define DOLBY_PARAM_DHSB_OFFSET (DOLBY_PARAM_PLMD_OFFSET + \
+ DOLBY_PARAM_PLMD_LENGTH)
+#define DOLBY_PARAM_DHRG_LENGTH 1
+#define DOLBY_PARAM_DHRG_OFFSET (DOLBY_PARAM_DHSB_OFFSET + \
+ DOLBY_PARAM_DHSB_LENGTH)
+#define DOLBY_PARAM_DSSB_LENGTH 1
+#define DOLBY_PARAM_DSSB_OFFSET (DOLBY_PARAM_DHRG_OFFSET + \
+ DOLBY_PARAM_DHRG_LENGTH)
+#define DOLBY_PARAM_DSSA_LENGTH 1
+#define DOLBY_PARAM_DSSA_OFFSET (DOLBY_PARAM_DSSB_OFFSET + \
+ DOLBY_PARAM_DSSB_LENGTH)
+#define DOLBY_PARAM_DVLA_LENGTH 1
+#define DOLBY_PARAM_DVLA_OFFSET (DOLBY_PARAM_DSSA_OFFSET + \
+ DOLBY_PARAM_DSSA_LENGTH)
+#define DOLBY_PARAM_IEBT_LENGTH 40
+#define DOLBY_PARAM_IEBT_OFFSET (DOLBY_PARAM_DVLA_OFFSET + \
+ DOLBY_PARAM_DVLA_LENGTH)
+#define DOLBY_PARAM_IEA_LENGTH 1
+#define DOLBY_PARAM_IEA_OFFSET (DOLBY_PARAM_IEBT_OFFSET + \
+ DOLBY_PARAM_IEBT_LENGTH)
+#define DOLBY_PARAM_DEA_LENGTH 1
+#define DOLBY_PARAM_DEA_OFFSET (DOLBY_PARAM_IEA_OFFSET + \
+ DOLBY_PARAM_IEA_LENGTH)
+#define DOLBY_PARAM_DED_LENGTH 1
+#define DOLBY_PARAM_DED_OFFSET (DOLBY_PARAM_DEA_OFFSET + \
+ DOLBY_PARAM_DEA_LENGTH)
+#define DOLBY_PARAM_GEBG_LENGTH 40
+#define DOLBY_PARAM_GEBG_OFFSET (DOLBY_PARAM_DED_OFFSET + \
+ DOLBY_PARAM_DED_LENGTH)
+#define DOLBY_PARAM_AOCC_LENGTH 1
+#define DOLBY_PARAM_AOCC_OFFSET (DOLBY_PARAM_GEBG_OFFSET + \
+ DOLBY_PARAM_GEBG_LENGTH)
+#define DOLBY_PARAM_ARBI_LENGTH 40
+#define DOLBY_PARAM_ARBI_OFFSET (DOLBY_PARAM_AOCC_OFFSET + \
+ DOLBY_PARAM_AOCC_LENGTH)
+#define DOLBY_PARAM_ARBL_LENGTH 40
+#define DOLBY_PARAM_ARBL_OFFSET (DOLBY_PARAM_ARBI_OFFSET + \
+ DOLBY_PARAM_ARBI_LENGTH)
+#define DOLBY_PARAM_ARBH_LENGTH 40
+#define DOLBY_PARAM_ARBH_OFFSET (DOLBY_PARAM_ARBL_OFFSET + \
+ DOLBY_PARAM_ARBL_LENGTH)
+#define DOLBY_PARAM_AROD_LENGTH 1
+#define DOLBY_PARAM_AROD_OFFSET (DOLBY_PARAM_ARBH_OFFSET + \
+ DOLBY_PARAM_ARBH_LENGTH)
+#define DOLBY_PARAM_ARTP_LENGTH 1
+#define DOLBY_PARAM_ARTP_OFFSET (DOLBY_PARAM_AROD_OFFSET + \
+ DOLBY_PARAM_AROD_LENGTH)
+#define DOLBY_PARAM_VMON_LENGTH 1
+#define DOLBY_PARAM_VMON_OFFSET (DOLBY_PARAM_ARTP_OFFSET + \
+ DOLBY_PARAM_ARTP_LENGTH)
+#define DOLBY_PARAM_VMB_LENGTH 1
+#define DOLBY_PARAM_VMB_OFFSET (DOLBY_PARAM_VMON_OFFSET + \
+ DOLBY_PARAM_VMON_LENGTH)
+#define DOLBY_PARAM_VCNB_LENGTH 1
+#define DOLBY_PARAM_VCNB_OFFSET (DOLBY_PARAM_VMB_OFFSET + \
+ DOLBY_PARAM_VMB_LENGTH)
+#define DOLBY_PARAM_VCBF_LENGTH 20
+#define DOLBY_PARAM_VCBF_OFFSET (DOLBY_PARAM_VCNB_OFFSET + \
+ DOLBY_PARAM_VCNB_LENGTH)
+#define DOLBY_PARAM_PREG_LENGTH 1
+#define DOLBY_PARAM_PREG_OFFSET (DOLBY_PARAM_VCBF_OFFSET + \
+ DOLBY_PARAM_VCBF_LENGTH)
+#define DOLBY_PARAM_VEN_LENGTH 1
+#define DOLBY_PARAM_VEN_OFFSET (DOLBY_PARAM_PREG_OFFSET + \
+ DOLBY_PARAM_PREG_LENGTH)
+#define DOLBY_PARAM_PSTG_LENGTH 1
+#define DOLBY_PARAM_PSTG_OFFSET (DOLBY_PARAM_VEN_OFFSET + \
+ DOLBY_PARAM_VEN_LENGTH)
+
+#define DOLBY_PARAM_INT_ENDP_LENGTH 1
+#define DOLBY_PARAM_PAYLOAD_SIZE 3
+#define DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM 329
+
+#define TOTAL_LENGTH_DOLBY_PARAM 745
+#define DOLBY_VIS_PARAM_HEADER_SIZE 25
+#define DOLBY_PARAM_VCNB_MAX_LENGTH 40
+
+#define DOLBY_INVALID_PORT_ID -1
+
+enum {
+ DEVICE_NONE = 0x0,
+ /* output devices */
+ EARPIECE = 0x1,
+ SPEAKER = 0x2,
+ WIRED_HEADSET = 0x4,
+ WIRED_HEADPHONE = 0x8,
+ BLUETOOTH_SCO = 0x10,
+ BLUETOOTH_SCO_HEADSET = 0x20,
+ BLUETOOTH_SCO_CARKIT = 0x40,
+ BLUETOOTH_A2DP = 0x80,
+ BLUETOOTH_A2DP_HEADPHONES = 0x100,
+ BLUETOOTH_A2DP_SPEAKER = 0x200,
+ AUX_DIGITAL = 0x400,
+ ANLG_DOCK_HEADSET = 0x800,
+ DGTL_DOCK_HEADSET = 0x1000,
+ USB_ACCESSORY = 0x2000,
+ USB_DEVICE = 0x4000,
+ REMOTE_SUBMIX = 0x8000,
+ ANC_HEADSET = 0x10000,
+ ANC_HEADPHONE = 0x20000,
+ PROXY = 0x2000000,
+ FM = 0x100000,
+ FM_TX = 0x1000000,
+ DEVICE_OUT_DEFAULT = 0x40000000,
+ DEVICE_OUT_ALL = 0x403FFFFF,
+};
+#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h
new file mode 100644
index 000000000000..01cc21cef7ee
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h
@@ -0,0 +1,85 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_DOLBY_DAP_CONFIG_H_
+#define _MSM_DOLBY_DAP_CONFIG_H_
+
+#include <sound/soc.h>
+#include "msm-dolby-common.h"
+
+#ifdef CONFIG_DOLBY_DAP
+/* DOLBY DOLBY GUIDS */
+#define DOLBY_ADM_COPP_TOPOLOGY_ID 0x0001033B
+#define NUM_DOLBY_ENDP_DEVICE 23
+
+#define DOLBY_NUM_ENDP_DEPENDENT_PARAMS 3
+#define DOLBY_ENDDEP_PARAM_DVLO_OFFSET 0
+#define DOLBY_ENDDEP_PARAM_DVLO_LENGTH 1
+#define DOLBY_ENDDEP_PARAM_DVLI_OFFSET (DOLBY_ENDDEP_PARAM_DVLO_OFFSET + \
+ DOLBY_ENDDEP_PARAM_DVLO_LENGTH)
+#define DOLBY_ENDDEP_PARAM_DVLI_LENGTH 1
+#define DOLBY_ENDDEP_PARAM_VMB_OFFSET (DOLBY_ENDDEP_PARAM_DVLI_OFFSET + \
+ DOLBY_ENDDEP_PARAM_DVLI_LENGTH)
+#define DOLBY_ENDDEP_PARAM_VMB_LENGTH 1
+#define DOLBY_ENDDEP_PARAM_LENGTH (DOLBY_ENDDEP_PARAM_DVLO_LENGTH + \
+ DOLBY_ENDDEP_PARAM_DVLI_LENGTH + DOLBY_ENDDEP_PARAM_VMB_LENGTH)
+
+#define MAX_DOLBY_PARAMS 47
+#define MAX_DOLBY_CTRL_PARAMS 5
+#define ALL_DOLBY_PARAMS (MAX_DOLBY_PARAMS + \
+ MAX_DOLBY_CTRL_PARAMS)
+#define DOLBY_COMMIT_ALL_IDX MAX_DOLBY_PARAMS
+#define DOLBY_COMMIT_IDX (MAX_DOLBY_PARAMS+1)
+#define DOLBY_USE_CACHE_IDX (MAX_DOLBY_PARAMS+2)
+#define DOLBY_AUTO_ENDP_IDX (MAX_DOLBY_PARAMS+3)
+#define DOLBY_AUTO_ENDDEP_IDX (MAX_DOLBY_PARAMS+4)
+
+/* DOLBY device definitions */
+enum {
+ DOLBY_ENDP_INT_SPEAKERS = 0,
+ DOLBY_ENDP_EXT_SPEAKERS,
+ DOLBY_ENDP_HEADPHONES,
+ DOLBY_ENDP_HDMI,
+ DOLBY_ENDP_SPDIF,
+ DOLBY_ENDP_DLNA,
+ DOLBY_ENDP_ANALOG,
+};
+
+/* DOLBY device definitions end */
+
+struct dolby_dap_params {
+ uint32_t value[TOTAL_LENGTH_DOLBY_PARAM + MAX_DOLBY_PARAMS];
+} __packed;
+
+int msm_dolby_dap_init(int port_id, int copp_idx, int channels,
+ bool is_custom_stereo_on);
+void msm_dolby_dap_deinit(int port_id);
+void msm_dolby_dap_add_controls(struct snd_soc_platform *platform);
+int dolby_dap_set_custom_stereo_onoff(int port_id, int copp_idx,
+ bool is_custom_stereo_enabled);
+/* Dolby DOLBY end */
+#else
+int msm_dolby_dap_init(int port_id, int copp_idx, int channels,
+ bool is_custom_stereo_on)
+{
+ return 0;
+}
+void msm_dolby_dap_deinit(int port_id) { }
+void msm_dolby_dap_add_controls(struct snd_soc_platform *platform) { }
+int dolby_dap_set_custom_stereo_onoff(int port_id, int copp_idx,
+ bool is_custom_stereo_enabled)
+{
+ return 0;
+}
+#endif
+
+#endif
+
diff --git a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c
new file mode 100644
index 000000000000..6dda41cc85bb
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c
@@ -0,0 +1,2314 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6common.h>
+
+#include "msm-ds2-dap-config.h"
+#include "msm-pcm-routing-v2.h"
+#include <sound/q6core.h>
+
+
+#if defined(CONFIG_DOLBY_DS2) || defined(CONFIG_DOLBY_LICENSE)
+
+/* ramp up/down for 30ms */
+#define DOLBY_SOFT_VOLUME_PERIOD 40
+/* Step value 0ms or 0us */
+#define DOLBY_SOFT_VOLUME_STEP 1000
+#define DOLBY_ADDITIONAL_RAMP_WAIT 10
+#define SOFT_VOLUME_PARAM_SIZE 3
+#define PARAM_PAYLOAD_SIZE 3
+
+enum {
+ DOLBY_SOFT_VOLUME_CURVE_LINEAR = 0,
+ DOLBY_SOFT_VOLUME_CURVE_EXP,
+ DOLBY_SOFT_VOLUME_CURVE_LOG,
+};
+
+#define VOLUME_ZERO_GAIN 0x0
+#define VOLUME_UNITY_GAIN 0x2000
+/* Wait time for module enable/disble */
+#define DOLBY_MODULE_ENABLE_PERIOD 50
+
+/* DOLBY device definitions end */
+enum {
+ DOLBY_OFF_CACHE = 0,
+ DOLBY_SPEAKER_CACHE,
+ DOLBY_HEADPHONE_CACHE,
+ DOLBY_HDMI_CACHE,
+ DOLBY_WFD_CACHE,
+ DOLBY_FM_CACHE,
+ DOLBY_MAX_CACHE,
+};
+
+enum {
+ DAP_SOFT_BYPASS = 0,
+ DAP_HARD_BYPASS,
+};
+
+enum {
+ MODULE_DISABLE = 0,
+ MODULE_ENABLE,
+};
+/* dolby param ids to/from dsp */
+static uint32_t ds2_dap_params_id[MAX_DS2_PARAMS] = {
+ DOLBY_PARAM_ID_VDHE, DOLBY_PARAM_ID_VSPE, DOLBY_PARAM_ID_DSSF,
+ DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLE,
+ DOLBY_PARAM_ID_DVMC, DOLBY_PARAM_ID_DVME, DOLBY_PARAM_ID_IENB,
+ DOLBY_PARAM_ID_IEBF, DOLBY_PARAM_ID_IEON, DOLBY_PARAM_ID_DEON,
+ DOLBY_PARAM_ID_NGON, DOLBY_PARAM_ID_GEON, DOLBY_PARAM_ID_GENB,
+ DOLBY_PARAM_ID_GEBF, DOLBY_PARAM_ID_AONB, DOLBY_PARAM_ID_AOBF,
+ DOLBY_PARAM_ID_AOBG, DOLBY_PARAM_ID_AOON, DOLBY_PARAM_ID_ARNB,
+ DOLBY_PARAM_ID_ARBF, DOLBY_PARAM_ID_PLB, DOLBY_PARAM_ID_PLMD,
+ DOLBY_PARAM_ID_DHSB, DOLBY_PARAM_ID_DHRG, DOLBY_PARAM_ID_DSSB,
+ DOLBY_PARAM_ID_DSSA, DOLBY_PARAM_ID_DVLA, DOLBY_PARAM_ID_IEBT,
+ DOLBY_PARAM_ID_IEA, DOLBY_PARAM_ID_DEA, DOLBY_PARAM_ID_DED,
+ DOLBY_PARAM_ID_GEBG, DOLBY_PARAM_ID_AOCC, DOLBY_PARAM_ID_ARBI,
+ DOLBY_PARAM_ID_ARBL, DOLBY_PARAM_ID_ARBH, DOLBY_PARAM_ID_AROD,
+ DOLBY_PARAM_ID_ARTP, DOLBY_PARAM_ID_VMON, DOLBY_PARAM_ID_VMB,
+ DOLBY_PARAM_ID_VCNB, DOLBY_PARAM_ID_VCBF, DOLBY_PARAM_ID_PREG,
+ DOLBY_PARAM_ID_VEN, DOLBY_PARAM_ID_PSTG, DOLBY_PARAM_ID_INIT_ENDP,
+};
+
+/* modifed state: 0x00000000 - Not updated
+* > 0x00000000 && < 0x00010000
+* Updated and not commited to DSP
+* 0x00010001 - Updated and commited to DSP
+* > 0x00010001 - Modified the commited value
+*/
+/* param offset */
+static uint32_t ds2_dap_params_offset[MAX_DS2_PARAMS] = {
+ DOLBY_PARAM_VDHE_OFFSET, DOLBY_PARAM_VSPE_OFFSET,
+ DOLBY_PARAM_DSSF_OFFSET, DOLBY_PARAM_DVLI_OFFSET,
+ DOLBY_PARAM_DVLO_OFFSET, DOLBY_PARAM_DVLE_OFFSET,
+ DOLBY_PARAM_DVMC_OFFSET, DOLBY_PARAM_DVME_OFFSET,
+ DOLBY_PARAM_IENB_OFFSET, DOLBY_PARAM_IEBF_OFFSET,
+ DOLBY_PARAM_IEON_OFFSET, DOLBY_PARAM_DEON_OFFSET,
+ DOLBY_PARAM_NGON_OFFSET, DOLBY_PARAM_GEON_OFFSET,
+ DOLBY_PARAM_GENB_OFFSET, DOLBY_PARAM_GEBF_OFFSET,
+ DOLBY_PARAM_AONB_OFFSET, DOLBY_PARAM_AOBF_OFFSET,
+ DOLBY_PARAM_AOBG_OFFSET, DOLBY_PARAM_AOON_OFFSET,
+ DOLBY_PARAM_ARNB_OFFSET, DOLBY_PARAM_ARBF_OFFSET,
+ DOLBY_PARAM_PLB_OFFSET, DOLBY_PARAM_PLMD_OFFSET,
+ DOLBY_PARAM_DHSB_OFFSET, DOLBY_PARAM_DHRG_OFFSET,
+ DOLBY_PARAM_DSSB_OFFSET, DOLBY_PARAM_DSSA_OFFSET,
+ DOLBY_PARAM_DVLA_OFFSET, DOLBY_PARAM_IEBT_OFFSET,
+ DOLBY_PARAM_IEA_OFFSET, DOLBY_PARAM_DEA_OFFSET,
+ DOLBY_PARAM_DED_OFFSET, DOLBY_PARAM_GEBG_OFFSET,
+ DOLBY_PARAM_AOCC_OFFSET, DOLBY_PARAM_ARBI_OFFSET,
+ DOLBY_PARAM_ARBL_OFFSET, DOLBY_PARAM_ARBH_OFFSET,
+ DOLBY_PARAM_AROD_OFFSET, DOLBY_PARAM_ARTP_OFFSET,
+ DOLBY_PARAM_VMON_OFFSET, DOLBY_PARAM_VMB_OFFSET,
+ DOLBY_PARAM_VCNB_OFFSET, DOLBY_PARAM_VCBF_OFFSET,
+ DOLBY_PARAM_PREG_OFFSET, DOLBY_PARAM_VEN_OFFSET,
+ DOLBY_PARAM_PSTG_OFFSET, DOLBY_PARAM_INT_ENDP_OFFSET,
+};
+/* param_length */
+static uint32_t ds2_dap_params_length[MAX_DS2_PARAMS] = {
+ DOLBY_PARAM_VDHE_LENGTH, DOLBY_PARAM_VSPE_LENGTH,
+ DOLBY_PARAM_DSSF_LENGTH, DOLBY_PARAM_DVLI_LENGTH,
+ DOLBY_PARAM_DVLO_LENGTH, DOLBY_PARAM_DVLE_LENGTH,
+ DOLBY_PARAM_DVMC_LENGTH, DOLBY_PARAM_DVME_LENGTH,
+ DOLBY_PARAM_IENB_LENGTH, DOLBY_PARAM_IEBF_LENGTH,
+ DOLBY_PARAM_IEON_LENGTH, DOLBY_PARAM_DEON_LENGTH,
+ DOLBY_PARAM_NGON_LENGTH, DOLBY_PARAM_GEON_LENGTH,
+ DOLBY_PARAM_GENB_LENGTH, DOLBY_PARAM_GEBF_LENGTH,
+ DOLBY_PARAM_AONB_LENGTH, DOLBY_PARAM_AOBF_LENGTH,
+ DOLBY_PARAM_AOBG_LENGTH, DOLBY_PARAM_AOON_LENGTH,
+ DOLBY_PARAM_ARNB_LENGTH, DOLBY_PARAM_ARBF_LENGTH,
+ DOLBY_PARAM_PLB_LENGTH, DOLBY_PARAM_PLMD_LENGTH,
+ DOLBY_PARAM_DHSB_LENGTH, DOLBY_PARAM_DHRG_LENGTH,
+ DOLBY_PARAM_DSSB_LENGTH, DOLBY_PARAM_DSSA_LENGTH,
+ DOLBY_PARAM_DVLA_LENGTH, DOLBY_PARAM_IEBT_LENGTH,
+ DOLBY_PARAM_IEA_LENGTH, DOLBY_PARAM_DEA_LENGTH,
+ DOLBY_PARAM_DED_LENGTH, DOLBY_PARAM_GEBG_LENGTH,
+ DOLBY_PARAM_AOCC_LENGTH, DOLBY_PARAM_ARBI_LENGTH,
+ DOLBY_PARAM_ARBL_LENGTH, DOLBY_PARAM_ARBH_LENGTH,
+ DOLBY_PARAM_AROD_LENGTH, DOLBY_PARAM_ARTP_LENGTH,
+ DOLBY_PARAM_VMON_LENGTH, DOLBY_PARAM_VMB_LENGTH,
+ DOLBY_PARAM_VCNB_LENGTH, DOLBY_PARAM_VCBF_LENGTH,
+ DOLBY_PARAM_PREG_LENGTH, DOLBY_PARAM_VEN_LENGTH,
+ DOLBY_PARAM_PSTG_LENGTH, DOLBY_PARAM_INT_ENDP_LENGTH,
+};
+
+struct ds2_dap_params_s {
+ int32_t params_val[TOTAL_LENGTH_DS2_PARAM];
+ int32_t dap_params_modified[MAX_DS2_PARAMS];
+};
+
+struct audio_rx_cal_data {
+ char aud_proc_data[AUD_PROC_BLOCK_SIZE];
+ int32_t aud_proc_size;
+ char aud_vol_data[AUD_VOL_BLOCK_SIZE];
+ int32_t aud_vol_size;
+};
+
+static struct ds2_dap_params_s ds2_dap_params[DOLBY_MAX_CACHE];
+
+struct ds2_device_mapping {
+ int32_t device_id; /* audio_out_... */
+ int port_id; /* afe port. constant for a target variant. routing-v2*/
+ /*Only one Dolby COPP for a specific port*/
+ int copp_idx; /* idx for the copp port on which ds2 is active */
+ int cache_dev; /* idx to a shared parameter array dependent on device*/
+ uint32_t stream_ref_count;
+ bool active;
+ void *cal_data;
+};
+
+static struct ds2_device_mapping dev_map[DS2_DEVICES_ALL];
+
+struct ds2_dap_params_states_s {
+ bool use_cache;
+ bool dap_bypass;
+ bool dap_bypass_type;
+ bool node_opened;
+ int32_t device;
+ bool custom_stereo_onoff;
+};
+
+static struct ds2_dap_params_states_s ds2_dap_params_states = {true, false,
+ false, DEVICE_NONE};
+
+static int all_supported_devices = EARPIECE|SPEAKER|WIRED_HEADSET|
+ WIRED_HEADPHONE|BLUETOOTH_SCO|AUX_DIGITAL|
+ ANLG_DOCK_HEADSET|DGTL_DOCK_HEADSET|
+ REMOTE_SUBMIX|ANC_HEADSET|ANC_HEADPHONE|
+ PROXY|FM|FM_TX|DEVICE_NONE|
+ BLUETOOTH_SCO_HEADSET|BLUETOOTH_SCO_CARKIT;
+
+
+static void msm_ds2_dap_check_and_update_ramp_wait(int port_id, int copp_idx,
+ int *ramp_wait)
+{
+
+ int32_t *update_params_value = NULL;
+ uint32_t params_length = SOFT_VOLUME_PARAM_SIZE * sizeof(uint32_t);
+ uint32_t param_payload_len = PARAM_PAYLOAD_SIZE * sizeof(uint32_t);
+ struct param_hdr_v3 param_hdr = {0};
+ int rc = 0;
+
+ update_params_value = kzalloc(params_length + param_payload_len,
+ GFP_KERNEL);
+ if (!update_params_value) {
+ pr_err("%s: params memory alloc failed\n", __func__);
+ goto end;
+ }
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_VOL_CTRL;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS;
+ param_hdr.param_size = params_length + param_payload_len;
+ rc = adm_get_pp_params(port_id, copp_idx, ADM_CLIENT_ID_DEFAULT, NULL,
+ &param_hdr, (char *) update_params_value);
+ if (rc == 0) {
+ pr_debug("%s: params_value [0x%x, 0x%x, 0x%x]\n",
+ __func__, update_params_value[0],
+ update_params_value[1],
+ update_params_value[2]);
+ *ramp_wait = update_params_value[0];
+ }
+end:
+ kfree(update_params_value);
+ /*
+ * No error returned as we do not need to error out from dap on/dap
+ * bypass. The default ramp parameter will be used to wait during
+ * ramp down.
+ */
+ return;
+}
+
+static int msm_ds2_dap_set_vspe_vdhe(int dev_map_idx,
+ bool is_custom_stereo_enabled)
+{
+ u8 *packed_param_data = NULL;
+ u8 *param_data = NULL;
+ struct param_hdr_v3 param_hdr = {0};
+ u32 packed_param_size = 0;
+ u32 param_size = 0;
+ int cdev;
+ int rc = 0;
+
+ if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+ pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) {
+ pr_err("%s: Invalid port id\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if ((dev_map[dev_map_idx].copp_idx < 0) ||
+ (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) {
+ pr_err("%s: Invalid copp_idx\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if ((dev_map[dev_map_idx].port_id != SLIMBUS_0_RX) &&
+ (dev_map[dev_map_idx].port_id != RT_PROXY_PORT_001_RX)) {
+ pr_debug("%s:No Custom stereo for port:0x%x\n",
+ __func__, dev_map[dev_map_idx].port_id);
+ goto end;
+ }
+
+ /* Allocate the max space needed */
+ packed_param_size = (TOTAL_LENGTH_DOLBY_PARAM * sizeof(uint32_t)) +
+ (2 * sizeof(union param_hdrs));
+ packed_param_data = kzalloc(packed_param_size, GFP_KERNEL);
+ if (!packed_param_data)
+ return -ENOMEM;
+
+ packed_param_size = 0;
+
+ /* Set common values */
+ cdev = dev_map[dev_map_idx].cache_dev;
+ param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID;
+ param_hdr.instance_id = INSTANCE_ID_0;
+
+ /* Pack VDHE header + data */
+ param_hdr.param_id = DOLBY_PARAM_ID_VDHE;
+ param_size = DOLBY_PARAM_VDHE_LENGTH * sizeof(uint32_t);
+ param_hdr.param_size = param_size;
+
+ if (is_custom_stereo_enabled)
+ param_data = NULL;
+ else
+ param_data = (u8 *) &ds2_dap_params[cdev]
+ .params_val[DOLBY_PARAM_VDHE_OFFSET];
+
+ rc = q6common_pack_pp_params(packed_param_data, &param_hdr, param_data,
+ &param_size);
+ if (rc) {
+ pr_err("%s: Failed to pack params, error %d\n", __func__, rc);
+ goto end;
+ }
+ packed_param_size += param_size;
+
+ /* Pack VSPE header + data */
+ param_hdr.param_id = DOLBY_PARAM_ID_VSPE;
+ param_size = DOLBY_PARAM_VSPE_LENGTH * sizeof(uint32_t);
+ param_hdr.param_size = param_size;
+
+ if (is_custom_stereo_enabled)
+ param_data = NULL;
+ else
+ param_data = (u8 *) &ds2_dap_params[cdev]
+ .params_val[DOLBY_PARAM_VSPE_OFFSET];
+
+ rc = q6common_pack_pp_params(packed_param_data + packed_param_size,
+ &param_hdr, param_data, &param_size);
+ if (rc) {
+ pr_err("%s: Failed to pack params, error %d\n", __func__, rc);
+ goto end;
+ }
+ packed_param_size += param_size;
+
+ rc = adm_set_pp_params(dev_map[dev_map_idx].port_id,
+ dev_map[dev_map_idx].copp_idx, NULL,
+ packed_param_data, packed_param_size);
+ if (rc) {
+ pr_err("%s: send vdhe/vspe params failed with rc=%d\n",
+ __func__, rc);
+ rc = -EINVAL;
+ goto end;
+ }
+end:
+ kfree(packed_param_data);
+ return rc;
+}
+
+int qti_set_custom_stereo_on(int port_id, int copp_idx,
+ bool is_custom_stereo_on)
+{
+ struct custom_stereo_param custom_stereo = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ uint16_t op_FL_ip_FL_weight;
+ uint16_t op_FL_ip_FR_weight;
+ uint16_t op_FR_ip_FL_weight;
+ uint16_t op_FR_ip_FR_weight;
+ int rc = 0;
+
+ if ((port_id != SLIMBUS_0_RX) &&
+ (port_id != RT_PROXY_PORT_001_RX)) {
+ pr_debug("%s:No Custom stereo for port:0x%x\n",
+ __func__, port_id);
+ return 0;
+ }
+
+ pr_debug("%s: port 0x%x, copp_idx %d, is_custom_stereo_on %d\n",
+ __func__, port_id, copp_idx, is_custom_stereo_on);
+ if (is_custom_stereo_on) {
+ op_FL_ip_FL_weight =
+ Q14_GAIN_ZERO_POINT_FIVE;
+ op_FL_ip_FR_weight =
+ Q14_GAIN_ZERO_POINT_FIVE;
+ op_FR_ip_FL_weight =
+ Q14_GAIN_ZERO_POINT_FIVE;
+ op_FR_ip_FR_weight =
+ Q14_GAIN_ZERO_POINT_FIVE;
+ } else {
+ op_FL_ip_FL_weight = Q14_GAIN_UNITY;
+ op_FL_ip_FR_weight = 0;
+ op_FR_ip_FL_weight = 0;
+ op_FR_ip_FR_weight = Q14_GAIN_UNITY;
+ }
+
+ param_hdr.module_id = MTMX_MODULE_ID_DEFAULT_CHMIXER;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = DEFAULT_CHMIXER_PARAM_ID_COEFF;
+ param_hdr.param_size = sizeof(struct custom_stereo_param);
+
+ /* index is 32-bit param in little endian*/
+ custom_stereo.index = CUSTOM_STEREO_INDEX_PARAM;
+ custom_stereo.reserved = 0;
+ /* for stereo mixing num out ch*/
+ custom_stereo.num_out_ch = CUSTOM_STEREO_NUM_OUT_CH;
+ /* for stereo mixing num in ch*/
+ custom_stereo.num_in_ch = CUSTOM_STEREO_NUM_IN_CH;
+
+ /* Out ch map FL/FR*/
+ custom_stereo.out_fl = PCM_CHANNEL_FL;
+ custom_stereo.out_fr = PCM_CHANNEL_FR;
+
+ /* In ch map FL/FR*/
+ custom_stereo.in_fl = PCM_CHANNEL_FL;
+ custom_stereo.in_fr = PCM_CHANNEL_FR;
+
+ /* weighting coefficients as name suggests,
+ mixing will be done according to these coefficients*/
+ custom_stereo.op_FL_ip_FL_weight = op_FL_ip_FL_weight;
+ custom_stereo.op_FL_ip_FR_weight = op_FL_ip_FR_weight;
+ custom_stereo.op_FR_ip_FL_weight = op_FR_ip_FL_weight;
+ custom_stereo.op_FR_ip_FR_weight = op_FR_ip_FR_weight;
+ rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
+ (u8 *) &custom_stereo);
+ if (rc) {
+ pr_err("%s: send params failed rc=%d\n", __func__, rc);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int dap_set_custom_stereo_onoff(int dev_map_idx,
+ bool is_custom_stereo_enabled)
+{
+ uint32_t enable = is_custom_stereo_enabled ? 1 : 0;
+ struct param_hdr_v3 param_hdr = {0};
+ int rc = 0;
+
+ if ((dev_map[dev_map_idx].port_id != SLIMBUS_0_RX) &&
+ (dev_map[dev_map_idx].port_id != RT_PROXY_PORT_001_RX)) {
+ pr_debug("%s:No Custom stereo for port:0x%x\n",
+ __func__, dev_map[dev_map_idx].port_id);
+ goto end;
+ }
+
+ if ((dev_map[dev_map_idx].copp_idx < 0) ||
+ (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) {
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /* DAP custom stereo */
+ msm_ds2_dap_set_vspe_vdhe(dev_map_idx,
+ is_custom_stereo_enabled);
+ param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = DOLBY_ENABLE_CUSTOM_STEREO;
+ param_hdr.param_size = sizeof(enable);
+
+ rc = adm_pack_and_set_one_pp_param(dev_map[dev_map_idx].port_id,
+ dev_map[dev_map_idx].copp_idx,
+ param_hdr, (u8 *) &enable);
+ if (rc) {
+ pr_err("%s: set custom stereo enable failed with rc=%d\n",
+ __func__, rc);
+ rc = -EINVAL;
+ goto end;
+ }
+end:
+ return rc;
+
+}
+
+
+static int set_custom_stereo_onoff(int dev_map_idx,
+ bool is_custom_stereo_enabled)
+{
+ int rc = 0;
+ pr_debug("%s: map index %d, custom stereo %d\n", __func__, dev_map_idx,
+ is_custom_stereo_enabled);
+
+ if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+ pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) {
+ pr_err("%s: invalid port id\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if ((dev_map[dev_map_idx].copp_idx < 0) ||
+ (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) {
+ pr_err("%s: invalid copp idx\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (ds2_dap_params_states.dap_bypass == true &&
+ ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS) {
+
+ rc = qti_set_custom_stereo_on(dev_map[dev_map_idx].port_id,
+ dev_map[dev_map_idx].copp_idx,
+ is_custom_stereo_enabled);
+ if (rc < 0) {
+ pr_err("%s:qti_set_custom_stereo_on_copp failed C.S %d",
+ __func__, is_custom_stereo_enabled);
+ }
+ goto end;
+
+ }
+
+ if (ds2_dap_params_states.dap_bypass == false) {
+ rc = dap_set_custom_stereo_onoff(dev_map_idx,
+ is_custom_stereo_enabled);
+ if (rc < 0) {
+ pr_err("%s:qti_set_custom_stereo_on_copp failed C.S %d",
+ __func__, is_custom_stereo_enabled);
+ }
+ goto end;
+ }
+end:
+ return rc;
+}
+
+static int msm_ds2_dap_alloc_and_store_cal_data(int dev_map_idx, int path,
+ int perf_mode)
+{
+ int rc = 0;
+ struct audio_rx_cal_data *aud_cal_data;
+ pr_debug("%s: path %d, perf_mode %d, dev_map_idx %d\n",
+ __func__, path, perf_mode, dev_map_idx);
+
+ if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+ pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ aud_cal_data = kzalloc(sizeof(struct audio_rx_cal_data), GFP_KERNEL);
+ if (!aud_cal_data) {
+ pr_err("%s, param memory alloc failed\n", __func__);
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ rc = adm_store_cal_data(dev_map[dev_map_idx].port_id,
+ dev_map[dev_map_idx].copp_idx, path, perf_mode,
+ ADM_AUDPROC_CAL, aud_cal_data->aud_proc_data,
+ &aud_cal_data->aud_proc_size);
+ if (rc < 0) {
+ pr_err("%s: store cal data err %d\n", __func__, rc);
+ kfree(aud_cal_data);
+ goto end;
+ }
+
+ rc = adm_store_cal_data(dev_map[dev_map_idx].port_id,
+ dev_map[dev_map_idx].copp_idx, path, perf_mode,
+ ADM_AUDVOL_CAL, aud_cal_data->aud_vol_data,
+ &aud_cal_data->aud_vol_size);
+ if (rc < 0) {
+ pr_err("%s: store cal data err %d\n", __func__, rc);
+ kfree(aud_cal_data);
+ goto end;
+ }
+
+ dev_map[dev_map_idx].cal_data = (void *)aud_cal_data;
+
+end:
+ pr_debug("%s: ret %d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_ds2_dap_free_cal_data(int dev_map_idx)
+{
+ int rc = 0;
+ struct audio_rx_cal_data *aud_cal_data;
+
+ pr_debug("%s: dev_map_idx %d\n", __func__, dev_map_idx);
+ if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+ pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+ rc = -EINVAL;
+ goto end;
+ }
+ aud_cal_data = (struct audio_rx_cal_data *)
+ dev_map[dev_map_idx].cal_data;
+ kfree(aud_cal_data);
+ dev_map[dev_map_idx].cal_data = NULL;
+
+end:
+ return rc;
+}
+
+static int msm_ds2_dap_send_cal_data(int dev_map_idx)
+{
+ int rc = 0;
+ struct audio_rx_cal_data *aud_cal_data = NULL;
+
+ pr_debug("%s: devmap index %d\n", __func__, dev_map_idx);
+ if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+ pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (dev_map[dev_map_idx].cal_data == NULL) {
+ pr_err("%s: No valid calibration data stored for idx %d\n",
+ __func__, dev_map_idx);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /* send aud proc cal */
+ aud_cal_data = (struct audio_rx_cal_data *)
+ dev_map[dev_map_idx].cal_data;
+ rc = adm_send_calibration(dev_map[dev_map_idx].port_id,
+ dev_map[dev_map_idx].copp_idx,
+ ADM_PATH_PLAYBACK, 0,
+ ADM_AUDPROC_CAL,
+ aud_cal_data->aud_proc_data,
+ aud_cal_data->aud_proc_size);
+ if (rc < 0) {
+ pr_err("%s: adm_send_calibration failed %d\n", __func__, rc);
+ goto end;
+ }
+
+ /* send aud volume cal*/
+ rc = adm_send_calibration(dev_map[dev_map_idx].port_id,
+ dev_map[dev_map_idx].copp_idx,
+ ADM_PATH_PLAYBACK, 0,
+ ADM_AUDVOL_CAL,
+ aud_cal_data->aud_vol_data,
+ aud_cal_data->aud_vol_size);
+ if (rc < 0)
+ pr_err("%s: adm_send_calibration failed %d\n", __func__, rc);
+end:
+ pr_debug("%s: return %d\n", __func__, rc);
+ return rc;
+}
+
+static inline int msm_ds2_dap_can_enable_module(int32_t module_id)
+{
+ if (module_id == MTMX_MODULE_ID_DEFAULT_CHMIXER ||
+ module_id == AUDPROC_MODULE_ID_RESAMPLER ||
+ module_id == AUDPROC_MODULE_ID_VOL_CTRL) {
+ return false;
+ }
+ return true;
+}
+
+static int msm_ds2_dap_init_modules_in_topology(int dev_map_idx)
+{
+ int rc = 0, i = 0, port_id, copp_idx;
+ /* Account for 32 bit interger allocation */
+ int32_t param_sz =
+ (ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH / sizeof(uint32_t));
+ int32_t *update_param_val = NULL;
+ struct module_instance_info mod_inst_info = {0};
+ int mod_inst_info_sz = 0;
+
+ if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+ pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ port_id = dev_map[dev_map_idx].port_id;
+ copp_idx = dev_map[dev_map_idx].copp_idx;
+ pr_debug("%s: port_id 0x%x copp_idx %d\n", __func__, port_id, copp_idx);
+ update_param_val =
+ kzalloc(ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH, GFP_KERNEL);
+ if (!update_param_val) {
+ pr_err("%s, param memory alloc failed\n", __func__);
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ if (!ds2_dap_params_states.dap_bypass) {
+ /* get modules from dsp */
+ rc = adm_get_pp_topo_module_list_v2(
+ port_id, copp_idx,
+ ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH,
+ update_param_val);
+ if (rc < 0) {
+ pr_err("%s:topo list port %d, err %d,copp_idx %d\n",
+ __func__, port_id, copp_idx, rc);
+ goto end;
+ }
+
+ if (update_param_val[0] > (param_sz - 1)) {
+ pr_err("%s:max modules exp/ret [%d: %d]\n",
+ __func__, (param_sz - 1),
+ update_param_val[0]);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ mod_inst_info_sz = sizeof(struct module_instance_info) /
+ sizeof(uint32_t);
+ /* Turn off modules */
+ for (i = 1; i < update_param_val[0] * mod_inst_info_sz;
+ i += mod_inst_info_sz) {
+ if (!msm_ds2_dap_can_enable_module(
+ update_param_val[i]) ||
+ (update_param_val[i] == DS2_MODULE_ID)) {
+ pr_debug("%s: Do not enable/disable %d\n",
+ __func__, update_param_val[i]);
+ continue;
+ }
+
+ pr_debug("%s: param disable %d\n",
+ __func__, update_param_val[i]);
+ memcpy(&mod_inst_info, &update_param_val[i],
+ sizeof(mod_inst_info));
+ adm_param_enable_v2(port_id, copp_idx,
+ mod_inst_info,
+ MODULE_DISABLE);
+ }
+ } else {
+ msm_ds2_dap_send_cal_data(dev_map_idx);
+
+ }
+
+ mod_inst_info.module_id = DS2_MODULE_ID;
+ mod_inst_info.instance_id = INSTANCE_ID_0;
+ adm_param_enable_v2(port_id, copp_idx, mod_inst_info,
+ !ds2_dap_params_states.dap_bypass);
+end:
+ kfree(update_param_val);
+ return rc;
+}
+
+static bool msm_ds2_dap_check_is_param_modified(int32_t *dap_params_modified,
+ int32_t idx, int32_t commit)
+{
+ if ((dap_params_modified[idx] == 0) ||
+ (commit &&
+ ((dap_params_modified[idx] & 0x00010000) &&
+ ((dap_params_modified[idx] & 0x0000FFFF) <= 1)))) {
+ pr_debug("%s: not modified at idx %d\n", __func__, idx);
+ return false;
+ }
+ pr_debug("%s: modified at idx %d\n", __func__, idx);
+ return true;
+}
+
+static int msm_ds2_dap_map_device_to_dolby_cache_devices(int32_t device_id)
+{
+ int32_t cache_dev = -1;
+ switch (device_id) {
+ case DEVICE_NONE:
+ cache_dev = DOLBY_OFF_CACHE;
+ break;
+ case EARPIECE:
+ case SPEAKER:
+ cache_dev = DOLBY_SPEAKER_CACHE;
+ break;
+ case WIRED_HEADSET:
+ case WIRED_HEADPHONE:
+ case ANLG_DOCK_HEADSET:
+ case DGTL_DOCK_HEADSET:
+ case ANC_HEADSET:
+ case ANC_HEADPHONE:
+ case BLUETOOTH_SCO:
+ case BLUETOOTH_SCO_HEADSET:
+ case BLUETOOTH_SCO_CARKIT:
+ cache_dev = DOLBY_HEADPHONE_CACHE;
+ break;
+ case FM:
+ case FM_TX:
+ cache_dev = DOLBY_FM_CACHE;
+ break;
+ case AUX_DIGITAL:
+ cache_dev = DOLBY_HDMI_CACHE;
+ break;
+ case PROXY:
+ case REMOTE_SUBMIX:
+ cache_dev = DOLBY_WFD_CACHE;
+ break;
+ default:
+ pr_err("%s: invalid cache device\n", __func__);
+ }
+ pr_debug("%s: cache device %d\n", __func__, cache_dev);
+ return cache_dev;
+}
+
+static int msm_ds2_dap_update_num_devices(struct dolby_param_data *dolby_data,
+ int32_t *num_device, int32_t *dev_arr,
+ int32_t array_size)
+{
+ int32_t idx = 0;
+ int supported_devices = 0;
+
+ if (!array_size) {
+ pr_err("%s: array size zero\n", __func__);
+ return -EINVAL;
+ }
+
+ if (dolby_data->device_id == DEVICE_OUT_ALL ||
+ dolby_data->device_id == DEVICE_OUT_DEFAULT)
+ supported_devices = all_supported_devices;
+ else
+ supported_devices = dolby_data->device_id;
+
+ if ((idx < array_size) && (supported_devices & EARPIECE))
+ dev_arr[idx++] = EARPIECE;
+ if ((idx < array_size) && (supported_devices & SPEAKER))
+ dev_arr[idx++] = SPEAKER;
+ if ((idx < array_size) && (supported_devices & WIRED_HEADSET))
+ dev_arr[idx++] = WIRED_HEADSET;
+ if ((idx < array_size) && (supported_devices & WIRED_HEADPHONE))
+ dev_arr[idx++] = WIRED_HEADPHONE;
+ if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO))
+ dev_arr[idx++] = BLUETOOTH_SCO;
+ if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO_CARKIT))
+ dev_arr[idx++] = BLUETOOTH_SCO_CARKIT;
+ if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO_HEADSET))
+ dev_arr[idx++] = BLUETOOTH_SCO_HEADSET;
+ if ((idx < array_size) && (supported_devices & AUX_DIGITAL))
+ dev_arr[idx++] = AUX_DIGITAL;
+ if ((idx < array_size) && (supported_devices & ANLG_DOCK_HEADSET))
+ dev_arr[idx++] = ANLG_DOCK_HEADSET;
+ if ((idx < array_size) && (supported_devices & DGTL_DOCK_HEADSET))
+ dev_arr[idx++] = DGTL_DOCK_HEADSET;
+ if ((idx < array_size) && (supported_devices & REMOTE_SUBMIX))
+ dev_arr[idx++] = REMOTE_SUBMIX;
+ if ((idx < array_size) && (supported_devices & ANC_HEADSET))
+ dev_arr[idx++] = ANC_HEADSET;
+ if ((idx < array_size) && (supported_devices & ANC_HEADPHONE))
+ dev_arr[idx++] = ANC_HEADPHONE;
+ if ((idx < array_size) && (supported_devices & PROXY))
+ dev_arr[idx++] = PROXY;
+ if ((idx < array_size) && (supported_devices & FM))
+ dev_arr[idx++] = FM;
+ if ((idx < array_size) && (supported_devices & FM_TX))
+ dev_arr[idx++] = FM_TX;
+ /* CHECK device none separately */
+ if ((idx < array_size) && (supported_devices == DEVICE_NONE))
+ dev_arr[idx++] = DEVICE_NONE;
+ pr_debug("%s: dev id 0x%x, idx %d\n", __func__,
+ supported_devices, idx);
+ *num_device = idx;
+ return 0;
+}
+
+static int msm_ds2_dap_get_port_id(
+ int32_t device_id, int32_t be_id)
+{
+ struct msm_pcm_routing_bdai_data bedais;
+ int port_id = DOLBY_INVALID_PORT_ID;
+ int port_type = 0;
+
+ if (be_id < 0) {
+ port_id = -1;
+ goto end;
+ }
+
+ msm_pcm_routing_get_bedai_info(be_id, &bedais);
+ pr_debug("%s: be port_id %d\n", __func__, bedais.port_id);
+ port_id = bedais.port_id;
+ port_type = afe_get_port_type(bedais.port_id);
+ if (port_type != MSM_AFE_PORT_TYPE_RX)
+ port_id = DOLBY_INVALID_PORT_ID;
+end:
+ pr_debug("%s: device_id 0x%x, be_id %d, port_id %d\n",
+ __func__, device_id, be_id, port_id);
+ return port_id;
+}
+
+static int msm_ds2_dap_update_dev_map_port_id(int32_t device_id, int port_id)
+{
+ int i;
+ for (i = 0; i < DS2_DEVICES_ALL; i++) {
+ if (dev_map[i].device_id == device_id)
+ dev_map[i].port_id = port_id;
+ }
+ pr_debug("%s: port_id %d, device_id 0x%x\n",
+ __func__, port_id, device_id);
+ return 0;
+}
+
+static int msm_ds2_dap_handle_bypass_wait(int port_id, int copp_idx,
+ int wait_time)
+{
+ int ret = 0;
+ adm_set_wait_parameters(port_id, copp_idx);
+ msm_pcm_routing_release_lock();
+ ret = adm_wait_timeout(port_id, copp_idx, wait_time);
+ msm_pcm_routing_acquire_lock();
+ /* Reset the parameters if wait has timed out */
+ if (ret == 0)
+ adm_reset_wait_parameters(port_id, copp_idx);
+ return ret;
+}
+
+static int msm_ds2_dap_handle_bypass(struct dolby_param_data *dolby_data)
+{
+ int rc = 0, i = 0, j = 0;
+ /*Account for 32 bit interger allocation */
+ int32_t param_sz =
+ (ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH / sizeof(uint32_t));
+ int32_t *mod_list = NULL;
+ int port_id = 0, copp_idx = -1;
+ bool cs_onoff = ds2_dap_params_states.custom_stereo_onoff;
+ int ramp_wait = DOLBY_SOFT_VOLUME_PERIOD;
+ struct module_instance_info mod_inst_info = {0};
+ int mod_inst_info_sz = 0;
+
+ pr_debug("%s: bypass type %d bypass %d custom stereo %d\n", __func__,
+ ds2_dap_params_states.dap_bypass_type,
+ ds2_dap_params_states.dap_bypass,
+ ds2_dap_params_states.custom_stereo_onoff);
+ mod_list =
+ kzalloc(ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH, GFP_KERNEL);
+ if (!mod_list) {
+ pr_err("%s: param memory alloc failed\n", __func__);
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ for (i = 0; i < DS2_DEVICES_ALL; i++) {
+ pr_debug("%s: active dev %d\n", __func__, dev_map[i].active);
+ if (dev_map[i].active) {
+ port_id = dev_map[i].port_id;
+ copp_idx = dev_map[i].copp_idx;
+
+ if (port_id == DOLBY_INVALID_PORT_ID) {
+ pr_err("%s: invalid port\n", __func__);
+ rc = 0;
+ goto end;
+ }
+
+ if ((copp_idx < 0) ||
+ (copp_idx >= MAX_COPPS_PER_PORT)) {
+ pr_err("%s: Invalid copp_idx\n", __func__);
+ rc = 0;
+ goto end;
+ }
+
+ /* getmodules from dsp */
+ rc = adm_get_pp_topo_module_list_v2(
+ port_id, copp_idx,
+ ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH,
+ mod_list);
+ if (rc < 0) {
+ pr_err("%s:adm get topo list port %d",
+ __func__, port_id);
+ pr_err("copp_idx %d, err %d\n",
+ copp_idx, rc);
+ goto end;
+ }
+ if (mod_list[0] > (param_sz - 1)) {
+ pr_err("%s:max modules exp/ret [%d: %d]\n",
+ __func__, (param_sz - 1),
+ mod_list[0]);
+ rc = -EINVAL;
+ goto end;
+ }
+ /*
+ * get ramp parameters
+ * check for change in ramp parameters
+ * update ramp wait
+ */
+ msm_ds2_dap_check_and_update_ramp_wait(port_id,
+ copp_idx,
+ &ramp_wait);
+
+ /* Mute before switching modules */
+ rc = adm_set_volume(port_id, copp_idx,
+ VOLUME_ZERO_GAIN);
+ if (rc < 0) {
+ /*
+ * Not Fatal can continue bypass operations.
+ * Do not need to block playback
+ */
+ pr_info("%s :Set volume port_id %d",
+ __func__, port_id);
+ pr_info("copp_idx %d, error %d\n",
+ copp_idx, rc);
+ }
+
+ rc = msm_ds2_dap_handle_bypass_wait(port_id, copp_idx,
+ (ramp_wait +
+ DOLBY_ADDITIONAL_RAMP_WAIT));
+ if (rc == -EINTR) {
+ pr_info("%s:bypass interupted-ignore,port %d",
+ __func__, port_id);
+ pr_info("copp_idx %d\n", copp_idx);
+ rc = 0;
+ continue;
+ }
+
+ /* if dap bypass is set */
+ if (ds2_dap_params_states.dap_bypass) {
+ /* Turn off dap module */
+ mod_inst_info.module_id = DS2_MODULE_ID;
+ mod_inst_info.instance_id = INSTANCE_ID_0;
+ adm_param_enable_v2(port_id, copp_idx,
+ mod_inst_info,
+ MODULE_DISABLE);
+ /*
+ * If custom stereo is on at the time of bypass,
+ * switch off custom stereo on dap and turn on
+ * custom stereo on qti channel mixer.
+ */
+ if (cs_onoff) {
+ rc = dap_set_custom_stereo_onoff(i,
+ !cs_onoff);
+ if (rc < 0) {
+ pr_info("%s:D_CS i %d,rc %d\n",
+ __func__, i, rc);
+ }
+ rc = qti_set_custom_stereo_on(port_id,
+ copp_idx,
+ cs_onoff);
+ if (rc < 0) {
+ pr_info("%s:Q_CS port id 0x%x",
+ __func__, port_id);
+ pr_info("copp idx %d, rc %d\n",
+ copp_idx, rc);
+ }
+ }
+
+ mod_inst_info_sz =
+ sizeof(struct module_instance_info) /
+ sizeof(uint32_t);
+ /* Turn on qti modules */
+ for (j = 1; j < mod_list[0] * mod_inst_info_sz;
+ j += mod_inst_info_sz) {
+ if (!msm_ds2_dap_can_enable_module(
+ mod_list[j]) ||
+ mod_list[j] ==
+ DS2_MODULE_ID)
+ continue;
+ pr_debug("%s: param enable %d\n",
+ __func__, mod_list[j]);
+ memcpy(&mod_inst_info, &mod_list[j],
+ sizeof(mod_inst_info));
+ adm_param_enable_v2(port_id, copp_idx,
+ mod_inst_info,
+ MODULE_ENABLE);
+ }
+
+ /* Add adm api to resend calibration on port */
+ rc = msm_ds2_dap_send_cal_data(i);
+ if (rc < 0) {
+ /*
+ * Not fatal,continue bypass operations.
+ * Do not need to block playback
+ */
+ pr_info("%s:send cal err %d index %d\n",
+ __func__, rc, i);
+ }
+ } else {
+ /* Turn off qti modules */
+ for (j = 1; j < mod_list[0] * mod_inst_info_sz;
+ j += mod_inst_info_sz) {
+ if (!msm_ds2_dap_can_enable_module(
+ mod_list[j]) ||
+ mod_list[j] ==
+ DS2_MODULE_ID)
+ continue;
+ pr_debug("%s: param disable %d\n",
+ __func__, mod_list[j]);
+ memcpy(&mod_inst_info, &mod_list[j],
+ sizeof(mod_inst_info));
+ adm_param_enable_v2(port_id, copp_idx,
+ mod_inst_info,
+ MODULE_DISABLE);
+ }
+
+ /* Enable DAP modules */
+ pr_debug("%s:DS2 param enable\n", __func__);
+ mod_inst_info.module_id = DS2_MODULE_ID;
+ mod_inst_info.instance_id = INSTANCE_ID_0;
+ adm_param_enable_v2(port_id, copp_idx,
+ mod_inst_info,
+ MODULE_ENABLE);
+ /*
+ * If custom stereo is on at the time of dap on,
+ * switch off custom stereo on qti channel mixer
+ * and turn on custom stereo on DAP.
+ * mixer(qti).
+ */
+ if (cs_onoff) {
+ rc = qti_set_custom_stereo_on(port_id,
+ copp_idx,
+ !cs_onoff);
+ if (rc < 0) {
+ pr_info("%s:Q_CS port_id 0x%x",
+ __func__, port_id);
+ pr_info("copp_idx %d rc %d\n",
+ copp_idx, rc);
+ }
+ rc = dap_set_custom_stereo_onoff(i,
+ cs_onoff);
+ if (rc < 0) {
+ pr_info("%s:D_CS i %d,rc %d\n",
+ __func__, i, rc);
+ }
+ }
+ }
+
+ rc = msm_ds2_dap_handle_bypass_wait(port_id, copp_idx,
+ DOLBY_MODULE_ENABLE_PERIOD);
+ if (rc == -EINTR) {
+ pr_info("%s:bypass interupted port_id %d copp_idx %d\n",
+ __func__, port_id, copp_idx);
+ /* Interrupted ignore bypass */
+ rc = 0;
+ continue;
+ }
+
+ /* set volume to unity gain after module on/off */
+ rc = adm_set_volume(port_id, copp_idx,
+ VOLUME_UNITY_GAIN);
+ if (rc < 0) {
+ /*
+ * Not Fatal can continue bypass operations.
+ * Do not need to block playback
+ */
+ pr_info("%s: Set vol port %d copp %d, rc %d\n",
+ __func__, port_id, copp_idx, rc);
+ rc = 0;
+ }
+ }
+ }
+
+end:
+ kfree(mod_list);
+ pr_debug("%s:return rc=%d\n", __func__, rc);
+ return rc;
+}
+
+static int msm_ds2_dap_send_end_point(int dev_map_idx, int endp_idx)
+{
+ uint32_t offset = 0;
+ struct param_hdr_v3 param_hdr = {0};
+ int cache_device = 0;
+ struct ds2_dap_params_s *ds2_ap_params_obj = NULL;
+ int32_t *modified_param = NULL;
+ int rc = 0;
+
+ if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+ pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+ rc = -EINVAL;
+ goto end;
+ }
+ cache_device = dev_map[dev_map_idx].cache_dev;
+
+ ds2_ap_params_obj = &ds2_dap_params[cache_device];
+ pr_debug("%s: cache dev %d, dev_map_idx %d\n", __func__,
+ cache_device, dev_map_idx);
+ pr_debug("%s: endp - %pK %pK\n", __func__,
+ &ds2_dap_params[cache_device], ds2_ap_params_obj);
+
+ if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) {
+ pr_err("%s: invalid port\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if ((dev_map[dev_map_idx].copp_idx < 0) ||
+ (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) {
+ pr_err("%s: Invalid copp_idx\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = DOLBY_PARAM_ID_INIT_ENDP;
+ param_hdr.param_size = sizeof(offset);
+ offset = ds2_ap_params_obj->params_val[ds2_dap_params_offset[endp_idx]];
+ pr_debug("%s: off %d, length %d\n", __func__,
+ ds2_dap_params_offset[endp_idx],
+ ds2_dap_params_length[endp_idx]);
+ pr_debug("%s: param 0x%x, param val %d\n", __func__,
+ ds2_dap_params_id[endp_idx], ds2_ap_params_obj->
+ params_val[ds2_dap_params_offset[endp_idx]]);
+ rc = adm_pack_and_set_one_pp_param(dev_map[dev_map_idx].port_id,
+ dev_map[dev_map_idx].copp_idx,
+ param_hdr, (u8 *) &offset);
+ if (rc) {
+ pr_err("%s: send dolby params failed rc %d\n", __func__, rc);
+ rc = -EINVAL;
+ }
+ modified_param = ds2_ap_params_obj->dap_params_modified;
+ if (modified_param == NULL) {
+ pr_err("%s: modified param structure invalid\n",
+ __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (msm_ds2_dap_check_is_param_modified(modified_param, endp_idx, 0))
+ ds2_ap_params_obj->dap_params_modified[endp_idx] = 0x00010001;
+
+end:
+ return rc;
+}
+
+static int msm_ds2_dap_send_cached_params(int dev_map_idx,
+ int commit)
+{
+ uint8_t *packed_params = NULL;
+ uint32_t packed_params_size = 0;
+ uint32_t param_size = 0;
+ struct param_hdr_v3 param_hdr = {0};
+ uint32_t idx, i, ret = 0;
+ int cache_device = 0;
+ struct ds2_dap_params_s *ds2_ap_params_obj = NULL;
+ int32_t *modified_param = NULL;
+
+ if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+ pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+ ret = -EINVAL;
+ goto end;
+ }
+ cache_device = dev_map[dev_map_idx].cache_dev;
+
+ /* Use off profile cache in only for soft bypass */
+ if (ds2_dap_params_states.dap_bypass_type == DAP_SOFT_BYPASS &&
+ ds2_dap_params_states.dap_bypass == true) {
+ pr_debug("%s: use bypass cache 0\n", __func__);
+ cache_device = dev_map[0].cache_dev;
+ }
+
+ ds2_ap_params_obj = &ds2_dap_params[cache_device];
+ pr_debug("%s: cached param - %pK %pK, cache_device %d\n", __func__,
+ &ds2_dap_params[cache_device], ds2_ap_params_obj,
+ cache_device);
+
+ /*
+ * Allocate the max space needed. This is enough space to hold the
+ * header for each param plus the total size of all the params.
+ */
+ packed_params_size = (sizeof(param_hdr) * (MAX_DS2_PARAMS - 1)) +
+ (TOTAL_LENGTH_DOLBY_PARAM * sizeof(uint32_t));
+ packed_params = kzalloc(packed_params_size, GFP_KERNEL);
+ if (!packed_params)
+ return -ENOMEM;
+
+ if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) {
+ pr_err("%s: invalid port id\n", __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if ((dev_map[dev_map_idx].copp_idx < 0) ||
+ (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) {
+ pr_err("%s: Invalid copp_idx\n", __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ packed_params_size = 0;
+ for (i = 0; i < (MAX_DS2_PARAMS-1); i++) {
+ /*get the pointer to the param modified array in the cache*/
+ modified_param = ds2_ap_params_obj->dap_params_modified;
+ if (modified_param == NULL) {
+ pr_err("%s: modified param structure invalid\n",
+ __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+ if (!msm_ds2_dap_check_is_param_modified(modified_param, i,
+ commit))
+ continue;
+
+ param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = ds2_dap_params_id[i];
+ param_hdr.param_size =
+ ds2_dap_params_length[i] * sizeof(uint32_t);
+
+ idx = ds2_dap_params_offset[i];
+ ret = q6common_pack_pp_params(
+ packed_params + packed_params_size, &param_hdr,
+ (u8 *) &ds2_ap_params_obj->params_val[idx],
+ &param_size);
+ if (ret) {
+ pr_err("%s: Failed to pack params, error %d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ packed_params_size += param_size;
+ }
+
+ pr_debug("%s: total packed param length: %d\n", __func__,
+ packed_params_size);
+ if (packed_params_size) {
+ ret = adm_set_pp_params(dev_map[dev_map_idx].port_id,
+ dev_map[dev_map_idx].copp_idx, NULL,
+ packed_params, packed_params_size);
+ if (ret) {
+ pr_err("%s: send dolby params failed ret %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto end;
+ }
+ for (i = 0; i < MAX_DS2_PARAMS-1; i++) {
+ /*get pointer to the param modified array in the cache*/
+ modified_param = ds2_ap_params_obj->dap_params_modified;
+ if (modified_param == NULL) {
+ pr_err("%s: modified param struct invalid\n",
+ __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+ if (!msm_ds2_dap_check_is_param_modified(
+ modified_param, i, commit))
+ continue;
+ ds2_ap_params_obj->dap_params_modified[i] = 0x00010001;
+ }
+ }
+end:
+ kfree(packed_params);
+ return ret;
+}
+
+static int msm_ds2_dap_commit_params(struct dolby_param_data *dolby_data,
+ int commit)
+{
+ int ret = 0, i, idx;
+ struct ds2_dap_params_s *ds2_ap_params_obj = NULL;
+ int32_t *modified_param = NULL;
+
+ /* Do not commit params if in hard bypass */
+ if (ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS &&
+ ds2_dap_params_states.dap_bypass == true) {
+ pr_debug("%s: called in bypass", __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+ for (idx = 0; idx < MAX_DS2_PARAMS; idx++) {
+ if (DOLBY_PARAM_ID_INIT_ENDP == ds2_dap_params_id[idx])
+ break;
+ }
+ if (idx >= MAX_DS2_PARAMS || idx < 0) {
+ pr_err("%s: index of DS2 Param not found idx %d\n",
+ __func__, idx);
+ ret = -EINVAL;
+ goto end;
+ }
+ pr_debug("%s: found endp - idx %d 0x%x\n", __func__, idx,
+ ds2_dap_params_id[idx]);
+ for (i = 0; i < DS2_DEVICES_ALL; i++) {
+ pr_debug("%s:dev[0x%x,0x%x],i:%d,active:%d,bypass:%d,type:%d\n",
+ __func__, dolby_data->device_id, dev_map[i].device_id,
+ i, dev_map[i].active, ds2_dap_params_states.dap_bypass,
+ ds2_dap_params_states.dap_bypass_type);
+
+ if (((dev_map[i].device_id & ds2_dap_params_states.device) ||
+ ((ds2_dap_params_states.dap_bypass_type ==
+ DAP_SOFT_BYPASS) &&
+ (ds2_dap_params_states.dap_bypass == true))) &&
+ (dev_map[i].active == true)) {
+
+ /*get ptr to the cache storing the params for device*/
+ if ((ds2_dap_params_states.dap_bypass_type ==
+ DAP_SOFT_BYPASS) &&
+ (ds2_dap_params_states.dap_bypass == true))
+ ds2_ap_params_obj =
+ &ds2_dap_params[dev_map[0].cache_dev];
+ else
+ ds2_ap_params_obj =
+ &ds2_dap_params[dev_map[i].cache_dev];
+
+ /*get the pointer to the param modified array in cache*/
+ modified_param = ds2_ap_params_obj->dap_params_modified;
+ if (modified_param == NULL) {
+ pr_err("%s: modified_param NULL\n", __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /*
+ * Send the endp param if use cache is set
+ * or if param is modified
+ */
+ if (!commit || msm_ds2_dap_check_is_param_modified(
+ modified_param, idx, commit)) {
+ msm_ds2_dap_send_end_point(i, idx);
+ commit = 0;
+ }
+ ret = msm_ds2_dap_send_cached_params(i, commit);
+ if (ret < 0) {
+ pr_err("%s: send cached param %d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+ }
+end:
+ return ret;
+}
+
+static int msm_ds2_dap_handle_commands(u32 cmd, void *arg)
+{
+ int ret = 0, port_id = 0;
+ int32_t data;
+ struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg;
+ if (get_user(data, &dolby_data->data[0])) {
+ pr_debug("%s error getting data\n", __func__);
+ ret = -EFAULT;
+ goto end;
+ }
+
+ pr_debug("%s: param_id %d,be_id %d,device_id 0x%x,length %d,data %d\n",
+ __func__, dolby_data->param_id, dolby_data->be_id,
+ dolby_data->device_id, dolby_data->length, data);
+
+ switch (dolby_data->param_id) {
+ case DAP_CMD_COMMIT_ALL:
+ msm_ds2_dap_commit_params(dolby_data, 0);
+ break;
+
+ case DAP_CMD_COMMIT_CHANGED:
+ msm_ds2_dap_commit_params(dolby_data, 1);
+ break;
+
+ case DAP_CMD_USE_CACHE_FOR_INIT:
+ ds2_dap_params_states.use_cache = data;
+ break;
+
+ case DAP_CMD_SET_BYPASS:
+ pr_debug("%s: bypass %d bypass type %d, data %d\n", __func__,
+ ds2_dap_params_states.dap_bypass,
+ ds2_dap_params_states.dap_bypass_type,
+ data);
+ /* Do not perform bypass operation if bypass state is same*/
+ if (ds2_dap_params_states.dap_bypass == data)
+ break;
+ ds2_dap_params_states.dap_bypass = data;
+ /* hard bypass */
+ if (ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS)
+ msm_ds2_dap_handle_bypass(dolby_data);
+ /* soft bypass */
+ msm_ds2_dap_commit_params(dolby_data, 0);
+ break;
+
+ case DAP_CMD_SET_BYPASS_TYPE:
+ if (data == true)
+ ds2_dap_params_states.dap_bypass_type =
+ DAP_HARD_BYPASS;
+ else
+ ds2_dap_params_states.dap_bypass_type =
+ DAP_SOFT_BYPASS;
+ pr_debug("%s: bypass type %d", __func__,
+ ds2_dap_params_states.dap_bypass_type);
+ break;
+
+ case DAP_CMD_SET_ACTIVE_DEVICE:
+ pr_debug("%s: DAP_CMD_SET_ACTIVE_DEVICE length %d\n",
+ __func__, dolby_data->length);
+ /* TODO: need to handle multiple instance*/
+ ds2_dap_params_states.device |= dolby_data->device_id;
+ port_id = msm_ds2_dap_get_port_id(
+ dolby_data->device_id,
+ dolby_data->be_id);
+ pr_debug("%s: device id 0x%x all_dev 0x%x port_id %d\n",
+ __func__, dolby_data->device_id,
+ ds2_dap_params_states.device, port_id);
+ msm_ds2_dap_update_dev_map_port_id(dolby_data->device_id,
+ port_id);
+ if (port_id == DOLBY_INVALID_PORT_ID) {
+ pr_err("%s: invalid port id %d\n", __func__, port_id);
+ ret = -EINVAL;
+ goto end;
+ }
+ break;
+ }
+end:
+ return ret;
+
+}
+
+static int msm_ds2_dap_set_param(u32 cmd, void *arg)
+{
+ int rc = 0, idx, i, j, off, port_id = 0, cdev = 0;
+ int32_t num_device = 0;
+ int32_t data = 0;
+ int32_t dev_arr[DS2_DSP_SUPPORTED_ENDP_DEVICE] = {0};
+ struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg;
+
+ rc = msm_ds2_dap_update_num_devices(dolby_data, &num_device, dev_arr,
+ DS2_DSP_SUPPORTED_ENDP_DEVICE);
+ if (num_device == 0 || rc < 0) {
+ pr_err("%s: num devices 0\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+ for (i = 0; i < num_device; i++) {
+ port_id = msm_ds2_dap_get_port_id(dev_arr[i],
+ dolby_data->be_id);
+ if (port_id != DOLBY_INVALID_PORT_ID)
+ msm_ds2_dap_update_dev_map_port_id(dev_arr[i], port_id);
+
+ cdev = msm_ds2_dap_map_device_to_dolby_cache_devices(
+ dev_arr[i]);
+ if (cdev < 0 || cdev >= DOLBY_MAX_CACHE) {
+ pr_err("%s: Invalide cache device %d for device 0x%x\n",
+ __func__, cdev, dev_arr[i]);
+ rc = -EINVAL;
+ goto end;
+ }
+ pr_debug("%s:port:%d,be:%d,dev:0x%x,cdev:%d,param:0x%x,len:%d\n"
+ , __func__, port_id, dolby_data->be_id, dev_arr[i],
+ cdev, dolby_data->param_id, dolby_data->length);
+ for (idx = 0; idx < MAX_DS2_PARAMS; idx++) {
+ /*paramid from user space*/
+ if (dolby_data->param_id == ds2_dap_params_id[idx])
+ break;
+ }
+ if (idx > MAX_DS2_PARAMS-1) {
+ pr_err("%s: invalid param id 0x%x at idx %d\n",
+ __func__, dolby_data->param_id, idx);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ off = ds2_dap_params_offset[idx];
+ if ((dolby_data->length <= 0) ||
+ (dolby_data->length > TOTAL_LENGTH_DS2_PARAM - off)) {
+ pr_err("%s: invalid length %d at idx %d\n",
+ __func__, dolby_data->length, idx);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /* cache the parameters */
+ ds2_dap_params[cdev].dap_params_modified[idx] += 1;
+ for (j = 0; j < dolby_data->length; j++) {
+ if (get_user(data, &dolby_data->data[j])) {
+ pr_debug("%s:error getting data\n", __func__);
+ rc = -EFAULT;
+ goto end;
+ }
+ ds2_dap_params[cdev].params_val[off + j] = data;
+ pr_debug("%s:off %d,val[i/p:o/p]-[%d / %d]\n",
+ __func__, off, data,
+ ds2_dap_params[cdev].
+ params_val[off + j]);
+ }
+ }
+end:
+ return rc;
+}
+
+static int msm_ds2_dap_get_param(u32 cmd, void *arg)
+{
+ int rc = 0, i, port_id = 0, copp_idx = -1;
+ struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg;
+ int32_t *params_value = NULL;
+ uint32_t params_length = DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM *
+ sizeof(uint32_t);
+ uint32_t param_payload_len =
+ DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t);
+ struct param_hdr_v3 param_hdr;
+
+ /* Return error on get param in soft or hard bypass */
+ if (ds2_dap_params_states.dap_bypass == true) {
+ pr_err("%s: called in bypass_type %d bypass %d\n", __func__,
+ ds2_dap_params_states.dap_bypass_type,
+ ds2_dap_params_states.dap_bypass);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /* Return if invalid length */
+ if ((dolby_data->length >
+ (DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM - DOLBY_PARAM_PAYLOAD_SIZE)) ||
+ (dolby_data->length <= 0)) {
+ pr_err("Invalid length %d", dolby_data->length);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ for (i = 0; i < DS2_DEVICES_ALL; i++) {
+ if ((dev_map[i].active) &&
+ (dev_map[i].device_id & dolby_data->device_id)) {
+ port_id = dev_map[i].port_id;
+ copp_idx = dev_map[i].copp_idx;
+ break;
+ }
+ }
+
+ if (port_id == DOLBY_INVALID_PORT_ID) {
+ pr_err("%s: Invalid port\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) {
+ pr_err("%s: Invalid copp_idx\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ pr_debug("%s: port_id 0x%x, copp_idx %d, dev_map[i].device_id %x\n",
+ __func__, port_id, copp_idx, dev_map[i].device_id);
+
+ params_value = kzalloc(params_length + param_payload_len,
+ GFP_KERNEL);
+ if (!params_value)
+ return -ENOMEM;
+
+ if (dolby_data->param_id == DOLBY_PARAM_ID_VER) {
+ param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = DOLBY_PARAM_ID_VER;
+ param_hdr.param_size = params_length + param_payload_len;
+ } else {
+ for (i = 0; i < MAX_DS2_PARAMS; i++)
+ if (ds2_dap_params_id[i] ==
+ dolby_data->param_id)
+ break;
+ if (i > MAX_DS2_PARAMS-1) {
+ pr_err("%s: invalid param id 0x%x at id %d\n", __func__,
+ dolby_data->param_id, i);
+ rc = -EINVAL;
+ goto end;
+ } else {
+ params_length =
+ ds2_dap_params_length[i] * sizeof(uint32_t);
+
+ param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = ds2_dap_params_id[i];
+ param_hdr.param_size =
+ params_length + param_payload_len;
+ }
+ }
+ rc = adm_get_pp_params(port_id, copp_idx, ADM_CLIENT_ID_DEFAULT, NULL,
+ &param_hdr, (u8 *) params_value);
+ if (rc) {
+ pr_err("%s: get parameters failed rc %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto end;
+ }
+ if (copy_to_user((void __user *) dolby_data->data,
+ &params_value[DOLBY_PARAM_PAYLOAD_SIZE],
+ (dolby_data->length * sizeof(uint32_t)))) {
+ pr_err("%s: error getting param\n", __func__);
+ rc = -EFAULT;
+ goto end;
+ }
+end:
+ kfree(params_value);
+ return rc;
+}
+
+static int msm_ds2_dap_param_visualizer_control_get(u32 cmd, void *arg)
+{
+ int32_t *visualizer_data = NULL;
+ int i = 0, ret = 0, port_id = -1, cache_dev = -1, copp_idx = -1;
+ int32_t *update_visualizer_data = NULL;
+ struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg;
+ uint32_t offset, length, params_length;
+ uint32_t param_payload_len =
+ DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t);
+ struct param_hdr_v3 param_hdr = {0};
+
+ for (i = 0; i < DS2_DEVICES_ALL; i++) {
+ if ((dev_map[i].active)) {
+ port_id = dev_map[i].port_id;
+ cache_dev = dev_map[i].cache_dev;
+ copp_idx = dev_map[i].copp_idx;
+ break;
+ }
+ }
+
+ if (port_id == DOLBY_INVALID_PORT_ID ||
+ (copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) {
+ ret = 0;
+ dolby_data->length = 0;
+ pr_err("%s: no device active\n", __func__);
+ goto end;
+ }
+
+ length = ds2_dap_params[cache_dev].params_val[DOLBY_PARAM_VCNB_OFFSET];
+
+ if (length > DOLBY_PARAM_VCNB_MAX_LENGTH || length <= 0) {
+ ret = 0;
+ dolby_data->length = 0;
+ pr_err("%s Incorrect VCNB length", __func__);
+ return -EINVAL;
+ }
+
+ params_length = (2*length + DOLBY_VIS_PARAM_HEADER_SIZE) *
+ sizeof(uint32_t);
+
+ visualizer_data = kzalloc(params_length, GFP_KERNEL);
+ if (!visualizer_data) {
+ pr_err("%s: params memory alloc failed\n", __func__);
+ ret = -ENOMEM;
+ dolby_data->length = 0;
+ goto end;
+ }
+ memset(visualizer_data, 0x0, params_length);
+
+ /* Return error on get param in soft or hard bypass */
+ if (ds2_dap_params_states.dap_bypass == true) {
+ pr_debug("%s: visualizer called in bypass, return 0\n",
+ __func__);
+ ret = 0;
+ dolby_data->length = 0;
+ goto end;
+ }
+
+ offset = 0;
+ params_length = length * sizeof(uint32_t);
+ param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = DOLBY_PARAM_ID_VCBG;
+ param_hdr.param_size = length * sizeof(uint32_t) + param_payload_len;
+ ret = adm_get_pp_params(port_id, copp_idx, ADM_CLIENT_ID_DEFAULT, NULL,
+ &param_hdr,
+ (((char *) (visualizer_data)) + offset));
+ if (ret) {
+ pr_err("%s: get parameters failed ret %d\n", __func__, ret);
+ ret = -EINVAL;
+ dolby_data->length = 0;
+ goto end;
+ }
+ offset = length * sizeof(uint32_t);
+ param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = DOLBY_PARAM_ID_VCBE;
+ param_hdr.param_size = length * sizeof(uint32_t) + param_payload_len;
+ ret = adm_get_pp_params(port_id, copp_idx, ADM_CLIENT_ID_DEFAULT, NULL,
+ &param_hdr,
+ (((char *) (visualizer_data)) + offset));
+ if (ret) {
+ pr_err("%s: get parameters failed ret %d\n", __func__, ret);
+ ret = -EINVAL;
+ dolby_data->length = 0;
+ goto end;
+ }
+ update_visualizer_data = visualizer_data;
+ dolby_data->length = 2 * length;
+
+ if (copy_to_user((void *)dolby_data->data,
+ (void *)update_visualizer_data,
+ (dolby_data->length * sizeof(uint32_t)))) {
+ pr_err("%s: copy to user failed for data\n", __func__);
+ dolby_data->length = 0;
+ ret = -EFAULT;
+ goto end;
+ }
+
+end:
+ kfree(visualizer_data);
+ return ret;
+}
+
+int msm_ds2_dap_set_security_control(u32 cmd, void *arg)
+{
+ struct dolby_param_license *dolby_license =
+ ((struct dolby_param_license *)arg);
+ pr_debug("%s: dmid %d license key %d\n", __func__,
+ dolby_license->dmid, dolby_license->license_key);
+ core_set_dolby_manufacturer_id(dolby_license->dmid);
+ core_set_license(dolby_license->license_key, DOLBY_DS1_LICENSE_ID);
+ return 0;
+}
+
+int msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw, struct file *file,
+ bool open)
+{
+ int i = 0, dev_id = 0;
+ pr_debug("%s: open %d\n", __func__, open);
+ ds2_dap_params_states.node_opened = open;
+ ds2_dap_params_states.dap_bypass = true;
+ ds2_dap_params_states.dap_bypass_type = 0;
+ ds2_dap_params_states.use_cache = 0;
+ ds2_dap_params_states.device = 0;
+ ds2_dap_params_states.custom_stereo_onoff = 0;
+ for (i = 0; i < DS2_DEVICES_ALL; i++) {
+ if (i == 0)
+ dev_map[i].device_id = 0;
+ else {
+ dev_id = (1 << (i-1));
+ if (all_supported_devices & dev_id)
+ dev_map[i].device_id = dev_id;
+ else
+ continue;
+ }
+ dev_map[i].cache_dev =
+ msm_ds2_dap_map_device_to_dolby_cache_devices(
+ dev_map[i].device_id);
+ if (dev_map[i].cache_dev < 0 ||
+ dev_map[i].cache_dev >= DOLBY_MAX_CACHE)
+ pr_err("%s: Invalide cache device %d for device 0x%x\n",
+ __func__,
+ dev_map[i].cache_dev,
+ dev_map[i].device_id);
+ dev_map[i].port_id = -1;
+ dev_map[i].active = false;
+ dev_map[i].stream_ref_count = 0;
+ dev_map[i].cal_data = NULL;
+ dev_map[i].copp_idx = -1;
+ pr_debug("%s: device_id 0x%x, cache_dev %d act %d\n", __func__,
+ dev_map[i].device_id, dev_map[i].cache_dev,
+ dev_map[i].active);
+ }
+ return 0;
+
+}
+
+int msm_ds2_dap_ioctl_shared(struct snd_hwdep *hw, struct file *file,
+ u32 cmd, void *arg)
+{
+ int ret = 0;
+ pr_debug("%s: cmd: 0x%x\n", __func__, cmd);
+ switch (cmd) {
+ case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM:
+ ret = msm_ds2_dap_set_param(cmd, arg);
+ break;
+ case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM:
+ ret = msm_ds2_dap_get_param(cmd, arg);
+ break;
+ case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND:
+ ret = msm_ds2_dap_handle_commands(cmd, arg);
+ break;
+ case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE:
+ ret = msm_ds2_dap_set_security_control(cmd, arg);
+ break;
+ case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER:
+ ret = msm_ds2_dap_param_visualizer_control_get(cmd, arg);
+ break;
+ default:
+ pr_err("%s: called with invalid control 0x%x\n", __func__, cmd);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file,
+ u32 cmd, void *arg)
+{
+
+ int ret = 0;
+ pr_debug("%s: cmd: 0x%x\n", __func__, cmd);
+ if (!arg) {
+ pr_err("%s: Invalid params event status\n", __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+ switch (cmd) {
+ case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM:
+ case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND: {
+ struct dolby_param_data dolby_data;
+ if (copy_from_user((void *)&dolby_data, (void *)arg,
+ sizeof(struct dolby_param_data))) {
+ pr_err("%s: Copy from user failed\n", __func__);
+ ret = -EFAULT;
+ goto end;
+ }
+ ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data);
+ break;
+ }
+ case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE: {
+ struct dolby_param_license dolby_license;
+ if (copy_from_user((void *)&dolby_license, (void *)arg,
+ sizeof(struct dolby_param_license))) {
+ pr_err("%s: Copy from user failed\n", __func__);
+ ret = -EFAULT;
+ goto end;
+ }
+ ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_license);
+ break;
+ }
+ case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM:
+ case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER: {
+ struct dolby_param_data dolby_data;
+ if (copy_from_user((void *)&dolby_data, (void *)arg,
+ sizeof(struct dolby_param_data))) {
+ pr_err("%s: Copy from user failed\n", __func__);
+ ret = -EFAULT;
+ goto end;
+ }
+ ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data);
+ if (ret < 0)
+ pr_err("%s: ioctl cmd %d returned err %d\n",
+ __func__, cmd, ret);
+ if (copy_to_user((void *)arg, &dolby_data,
+ sizeof(struct dolby_param_data))) {
+ pr_err("%s: Copy to user failed\n", __func__);
+ ret = -EFAULT;
+ goto end;
+ }
+ break;
+ }
+ default:
+ pr_err("%s: called with invalid control 0x%x\n", __func__, cmd);
+ ret = -EINVAL;
+ }
+end:
+ return ret;
+
+}
+#ifdef CONFIG_COMPAT
+int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw, struct file *file,
+ u32 cmd, void *arg)
+{
+ int ret = 0;
+ pr_debug("%s: cmd: 0x%x\n", __func__, cmd);
+ switch (cmd) {
+ case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32:
+ cmd = SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM;
+ goto handle_set_ioctl;
+ case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32:
+ cmd = SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND;
+handle_set_ioctl:
+ {
+ struct dolby_param_data32 dolby_data32;
+ struct dolby_param_data dolby_data;
+ memset(&dolby_data32, 0, sizeof(dolby_data32));
+ memset(&dolby_data, 0, sizeof(dolby_data));
+ if (copy_from_user(&dolby_data32, (void *)arg,
+ sizeof(struct dolby_param_data32))) {
+ pr_err("%s: Copy from user failed\n", __func__);
+ ret = -EFAULT;
+ goto end;
+ }
+ dolby_data.version = dolby_data32.version;
+ dolby_data.device_id = dolby_data32.device_id;
+ dolby_data.be_id = dolby_data32.be_id;
+ dolby_data.param_id = dolby_data32.param_id;
+ dolby_data.length = dolby_data32.length;
+ dolby_data.data = compat_ptr(dolby_data32.data);
+
+ ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data);
+ break;
+ }
+ case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32:
+ cmd = SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM;
+ goto handle_get_ioctl;
+ case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32:
+ cmd = SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER;
+handle_get_ioctl:
+ {
+ struct dolby_param_data32 dolby_data32;
+ struct dolby_param_data dolby_data;
+ memset(&dolby_data32, 0, sizeof(dolby_data32));
+ memset(&dolby_data, 0, sizeof(dolby_data));
+ if (copy_from_user(&dolby_data32, (void *)arg,
+ sizeof(struct dolby_param_data32))) {
+ pr_err("%s: Copy from user failed\n", __func__);
+ ret = -EFAULT;
+ goto end;
+ }
+ dolby_data.version = dolby_data32.version;
+ dolby_data.device_id = dolby_data32.device_id;
+ dolby_data.be_id = dolby_data32.be_id;
+ dolby_data.param_id = dolby_data32.param_id;
+ dolby_data.length = dolby_data32.length;
+ dolby_data.data = compat_ptr(dolby_data32.data);
+
+ ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data);
+ if (ret < 0)
+ pr_err("%s: ioctl cmd %d, returned err %d\n",
+ __func__, cmd, ret);
+ dolby_data32.length = dolby_data.length;
+ if (copy_to_user((void *)arg, &dolby_data32,
+ sizeof(struct dolby_param_data32))) {
+ pr_err("%s: Copy to user failed\n", __func__);
+ ret = -EFAULT;
+ goto end;
+ }
+ break;
+ }
+ case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32: {
+ struct dolby_param_license32 dolby_license32;
+ struct dolby_param_license dolby_license;
+ cmd = SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE;
+ if (copy_from_user((void *)&dolby_license32, (void *)arg,
+ sizeof(struct dolby_param_license32))) {
+ pr_err("%s: Copy from user failed\n", __func__);
+ ret = -EFAULT;
+ goto end;
+ }
+ dolby_license.dmid = dolby_license32.dmid;
+ dolby_license.license_key = dolby_license32.license_key;
+ ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_license);
+ break;
+ }
+ default:
+ pr_err("%s: called with invalid control 0x%x\n",
+ __func__, cmd);
+ ret = -EINVAL;
+ }
+end:
+ return ret;
+
+}
+#endif
+
+int msm_ds2_dap_init(int port_id, int copp_idx, int channels,
+ bool is_custom_stereo_on)
+{
+ int ret = 0, idx = -1, i;
+ struct dolby_param_data dolby_data;
+
+ struct audproc_softvolume_params softvol = {
+ .period = DOLBY_SOFT_VOLUME_PERIOD,
+ .step = DOLBY_SOFT_VOLUME_STEP,
+ .rampingcurve = DOLBY_SOFT_VOLUME_CURVE_EXP,
+ };
+
+ pr_debug("%s: port id %d, copp_idx %d\n", __func__, port_id, copp_idx);
+
+ if (port_id != DOLBY_INVALID_PORT_ID) {
+ for (i = 0; i < DS2_DEVICES_ALL; i++) {
+ if ((dev_map[i].port_id == port_id) &&
+ /* device part of active device */
+ (dev_map[i].device_id &
+ ds2_dap_params_states.device)) {
+ idx = i;
+ /* Give priority to headset in case of
+ combo device */
+ if (dev_map[i].device_id == SPEAKER)
+ continue;
+ else
+ break;
+ }
+ }
+ if (idx < 0) {
+ pr_err("%s: invalid index for port %d\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto end;
+ }
+ pr_debug("%s:index %d, dev[0x%x,0x%x]\n", __func__, idx,
+ dev_map[idx].device_id, ds2_dap_params_states.device);
+ dev_map[idx].active = true;
+ dev_map[idx].copp_idx = copp_idx;
+ dolby_data.param_id = DOLBY_COMMIT_ALL_TO_DSP;
+ dolby_data.length = 0;
+ dolby_data.data = NULL;
+ dolby_data.device_id = dev_map[idx].device_id;
+ pr_debug("%s: idx %d, active %d, dev id 0x%x, ref count %d\n",
+ __func__, idx, dev_map[idx].active,
+ dev_map[idx].device_id,
+ dev_map[idx].stream_ref_count);
+ if (dev_map[idx].stream_ref_count == 0) {
+ /*perform next 3 func only if hard bypass enabled*/
+ if (ds2_dap_params_states.dap_bypass_type ==
+ DAP_HARD_BYPASS) {
+ ret = msm_ds2_dap_alloc_and_store_cal_data(idx,
+ ADM_PATH_PLAYBACK, 0);
+ if (ret < 0) {
+ pr_err("%s: Failed to alloc and store cal data for idx %d, device %d, copp_idx %d",
+ __func__,
+ idx, dev_map[idx].device_id,
+ dev_map[idx].copp_idx);
+ dev_map[idx].active = false;
+ dev_map[idx].copp_idx = -1;
+ goto end;
+ }
+
+ ret = adm_set_softvolume(port_id, copp_idx,
+ &softvol);
+ if (ret < 0) {
+ pr_err("%s: Soft volume ret error %d\n",
+ __func__, ret);
+ dev_map[idx].active = false;
+ dev_map[idx].copp_idx = -1;
+ goto end;
+ }
+
+ ret = msm_ds2_dap_init_modules_in_topology(
+ idx);
+ if (ret < 0) {
+ pr_err("%s: Failed to init modules in topolofy for idx %d, device %d, copp_idx %d\n",
+ __func__, idx,
+ dev_map[idx].device_id,
+ dev_map[idx].copp_idx);
+ dev_map[idx].active = false;
+ dev_map[idx].copp_idx = -1;
+ goto end;
+ }
+ }
+
+ ret = msm_ds2_dap_commit_params(&dolby_data, 0);
+ if (ret < 0) {
+ pr_debug("%s: commit params ret %d\n",
+ __func__, ret);
+ ret = 0;
+ }
+ }
+ dev_map[idx].stream_ref_count++;
+ if (is_custom_stereo_on) {
+ ds2_dap_params_states.custom_stereo_onoff =
+ is_custom_stereo_on;
+ set_custom_stereo_onoff(idx,
+ is_custom_stereo_on);
+ }
+ }
+
+end:
+ return ret;
+}
+
+void msm_ds2_dap_deinit(int port_id)
+{
+ /*
+ * Get the active port corrresponding to the active device
+ * Check if this is same as incoming port
+ * Set it to invalid
+ */
+ int idx = -1, i;
+ pr_debug("%s: port_id %d\n", __func__, port_id);
+ if (port_id != DOLBY_INVALID_PORT_ID) {
+ for (i = 0; i < DS2_DEVICES_ALL; i++) {
+ /* Active port */
+ if ((dev_map[i].port_id == port_id) &&
+ /* device part of active device */
+ (dev_map[i].device_id &
+ ds2_dap_params_states.device) &&
+ /*
+ * Need this check to avoid race condition of
+ * active device being set and playback
+ * instance opened
+ */
+ /* active device*/
+ dev_map[i].active) {
+ idx = i;
+ if (dev_map[i].device_id == SPEAKER)
+ continue;
+ else
+ break;
+ }
+ }
+ if (idx < 0) {
+ pr_err("%s: invalid index for port %d\n",
+ __func__, port_id);
+ return;
+ }
+ pr_debug("%s:index %d, dev [0x%x, 0x%x]\n", __func__, idx,
+ dev_map[idx].device_id, ds2_dap_params_states.device);
+ dev_map[idx].stream_ref_count--;
+ if (dev_map[idx].stream_ref_count == 0) {
+ /*perform next func only if hard bypass enabled*/
+ if (ds2_dap_params_states.dap_bypass_type ==
+ DAP_HARD_BYPASS) {
+ msm_ds2_dap_free_cal_data(idx);
+ }
+ ds2_dap_params_states.device &= ~dev_map[idx].device_id;
+ dev_map[idx].active = false;
+ dev_map[idx].copp_idx = -1;
+ }
+ pr_debug("%s:idx %d, active %d, dev id 0x%x ref count %d\n",
+ __func__, idx, dev_map[idx].active,
+ dev_map[idx].device_id, dev_map[idx].stream_ref_count);
+ }
+}
+
+int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx,
+ bool is_custom_stereo_enabled)
+{
+ int idx = -1, rc = 0, i;
+ pr_debug("%s: port_id %d\n", __func__, port_id);
+ if (port_id != DOLBY_INVALID_PORT_ID) {
+ for (i = 0; i < DS2_DEVICES_ALL; i++) {
+ if ((dev_map[i].port_id == port_id) &&
+ /* device part of active device */
+ (dev_map[i].device_id &
+ ds2_dap_params_states.device)) {
+ idx = i;
+ if (dev_map[i].device_id == SPEAKER)
+ continue;
+ else
+ break;
+ }
+ }
+ if (idx < 0) {
+ pr_err("%s: invalid index for port %d\n",
+ __func__, port_id);
+ return rc;
+ }
+ ds2_dap_params_states.custom_stereo_onoff =
+ is_custom_stereo_enabled;
+ rc = set_custom_stereo_onoff(idx,
+ is_custom_stereo_enabled);
+ if (rc < 0) {
+ pr_err("%s: Custom stereo err %d on port %d\n",
+ __func__, rc, port_id);
+ }
+ }
+ return rc;
+}
+
+#else
+
+static int msm_ds2_dap_alloc_and_store_cal_data(int dev_map_idx, int path,
+ int perf_mode)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_free_cal_data(int dev_map_idx)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_send_cal_data(int dev_map_idx)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_can_enable_module(int32_t module_id)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_init_modules_in_topology(int dev_map_idx)
+{
+ return 0;
+}
+
+static bool msm_ds2_dap_check_is_param_modified(int32_t *dap_params_modified,
+ int32_t idx, int32_t commit)
+{
+ return false;
+}
+
+
+static int msm_ds2_dap_map_device_to_dolby_cache_devices(int32_t device_id)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_update_num_devices(struct dolby_param_data *dolby_data,
+ int32_t *num_device, int32_t *dev_arr,
+ int32_t array_size)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_commit_params(struct dolby_param_data *dolby_data,
+ int commit)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_handle_commands(u32 cmd, void *arg)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_set_param(u32 cmd, void *arg)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_get_param(u32 cmd, void *arg)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_send_end_point(int dev_map_idx, int endp_idx)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_send_cached_params(int dev_map_idx,
+ int commit)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_set_vspe_vdhe(int dev_map_idx,
+ bool is_custom_stereo_enabled)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_param_visualizer_control_get(
+ u32 cmd, void *arg,
+ struct msm_pcm_routing_bdai_data *bedais)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_set_security_control(u32 cmd, void *arg)
+{
+ return 0
+}
+
+static int msm_ds2_dap_update_dev_map_port_id(int32_t device_id, int port_id)
+{
+ return 0;
+}
+
+static int32_t msm_ds2_dap_get_port_id(
+ int32_t device_id, int32_t be_id)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_handle_bypass(struct dolby_param_data *dolby_data)
+{
+ return 0;
+}
+
+static int msm_ds2_dap_handle_bypass_wait(int port_id, int copp_idx,
+ int wait_time)
+{
+ return 0;
+}
+
+static int dap_set_custom_stereo_onoff(int dev_map_idx,
+ bool is_custom_stereo_enabled)
+{
+ return 0;
+}
+int qti_set_custom_stereo_on(int port_id, int copp_idx,
+ bool is_custom_stereo_on)
+{
+ return 0;
+}
+int set_custom_stereo_onoff(int dev_map_idx,
+ bool is_custom_stereo_enabled)
+{
+ return 0;
+}
+int msm_ds2_dap_ioctl_shared(struct snd_hwdep *hw, struct file *file,
+ u32 cmd, void *arg)
+{
+ return 0;
+}
+#endif /* CONFIG_DOLBY_DS2 || CONFIG_DOLBY_LICENSE */
diff --git a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h
new file mode 100644
index 000000000000..c2687017c962
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h
@@ -0,0 +1,150 @@
+/* Copyright (c) 2013-2014, 2016-2017, The Linux Foundation. All rights
+ * reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_DS2_DAP_CONFIG_H_
+#define _MSM_DS2_DAP_CONFIG_H_
+
+#include <sound/soc.h>
+#include "msm-dolby-common.h"
+#include <sound/hwdep.h>
+#include <uapi/sound/devdep_params.h>
+
+#ifdef CONFIG_COMPAT
+struct dolby_param_data32 {
+ s32 version;
+ s32 device_id;
+ s32 be_id;
+ s32 param_id;
+ s32 length;
+ compat_uptr_t data;
+};
+
+struct dolby_param_license32 {
+ compat_uptr_t dmid;
+ compat_uptr_t license_key;
+};
+
+#define SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32\
+ _IOWR('U', 0x10, struct dolby_param_data32)
+#define SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32\
+ _IOR('U', 0x11, struct dolby_param_data32)
+#define SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32\
+ _IOWR('U', 0x13, struct dolby_param_data32)
+#define SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32\
+ _IOWR('U', 0x14, struct dolby_param_license32)
+#define SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32\
+ _IOR('U', 0x15, struct dolby_param_data32)
+#endif
+
+#if defined(CONFIG_DOLBY_DS2) || defined(CONFIG_DOLBY_LICENSE)
+/* DOLBY DOLBY GUIDS */
+#define DS2_MODULE_ID 0x00010775
+
+#define DS2_DSP_SUPPORTED_ENDP_DEVICE 17
+#define DS2_DEVICES_ALL 32 /* enum val is 4 bytes */
+
+enum {
+
+ DAP_CMD_COMMIT_ALL = 0,
+ DAP_CMD_COMMIT_CHANGED = 1,
+ DAP_CMD_USE_CACHE_FOR_INIT = 2,
+ DAP_CMD_SET_BYPASS = 3,
+ DAP_CMD_SET_ACTIVE_DEVICE = 4,
+ DAP_CMD_SET_BYPASS_TYPE = 5,
+};
+
+struct custom_stereo_param {
+ /* Index is 32-bit param in little endian */
+ u16 index;
+ u16 reserved;
+
+ /* For stereo mixing, the number of out channels */
+ u16 num_out_ch;
+ /* For stereo mixing, the number of in channels */
+ u16 num_in_ch;
+
+ /* Out channel map FL/FR*/
+ u16 out_fl;
+ u16 out_fr;
+
+ /* In channel map FL/FR*/
+ u16 in_fl;
+ u16 in_fr;
+
+ /*
+ * Weighting coefficients. Mixing will be done according to
+ * these coefficients.
+ */
+ u16 op_FL_ip_FL_weight;
+ u16 op_FL_ip_FR_weight;
+ u16 op_FR_ip_FL_weight;
+ u16 op_FR_ip_FR_weight;
+};
+
+#define DOLBY_PARAM_INT_ENDP_LENGTH 1
+#define DOLBY_PARAM_INT_ENDP_OFFSET (DOLBY_PARAM_PSTG_OFFSET + \
+ DOLBY_PARAM_PSTG_LENGTH)
+#define MAX_DS2_PARAMS 48
+#define MAX_DS2_CTRL_PARAMS 4
+#define ALL_DS2_PARAMS (MAX_DS2_PARAMS + \
+ MAX_DS2_CTRL_PARAMS)
+#define TOTAL_LENGTH_DS2_PARAM (TOTAL_LENGTH_DOLBY_PARAM + 1)
+
+int msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw, struct file *file,
+ bool open);
+int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file,
+ u32 cmd, void *arg);
+int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw,
+ struct file *file,
+ u32 cmd, void *arg);
+int msm_ds2_dap_init(int port_id, int copp_idx, int channels,
+ bool is_custom_stereo_on);
+void msm_ds2_dap_deinit(int port_id);
+int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx,
+ bool is_custom_stereo_enabled);
+/* Dolby DOLBY end */
+#else
+
+static inline int msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw,
+ struct file *file,
+ bool open)
+{
+ return 0;
+}
+
+static inline int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file,
+ u32 cmd, void *arg)
+{
+ return 0;
+}
+
+static inline int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw,
+ struct file *file,
+ u32 cmd, void *arg)
+{
+ return 0;
+}
+static inline int msm_ds2_dap_init(int port_id, int copp_idx, int channels,
+ bool is_custom_stereo_on)
+{
+ return 0;
+}
+
+static inline void msm_ds2_dap_deinit(int port_id) { }
+
+static inline int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx,
+ bool is_custom_stereo_enabled)
+{
+ return 0;
+}
+#endif
+#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c
new file mode 100644
index 000000000000..8fc49b29000c
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c
@@ -0,0 +1,354 @@
+/* Copyright (c) 2012-2014, 2016-2017, The Linux Foundation. All
+ * rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+#include <linux/msm_audio_ion.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <sound/asound.h>
+#include "msm-dts-srs-tm-config.h"
+#include "msm-pcm-routing-v2.h"
+
+static int srs_port_id[AFE_MAX_PORTS] = {-1};
+static int srs_copp_idx[AFE_MAX_PORTS] = {-1};
+static union srs_trumedia_params_u msm_srs_trumedia_params;
+static struct ion_client *ion_client;
+static struct ion_handle *ion_handle;
+static struct param_outband po;
+static atomic_t ref_cnt;
+#define ION_MEM_SIZE (8 * 1024)
+
+static int set_port_id(int port_id, int copp_idx)
+{
+ int index = adm_validate_and_get_port_index(port_id);
+ if (index < 0) {
+ pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index,
+ port_id);
+ return -EINVAL;
+ }
+ srs_port_id[index] = port_id;
+ srs_copp_idx[index] = copp_idx;
+ return 0;
+}
+
+static void msm_dts_srs_tm_send_params(__s32 port_id, __u32 techs)
+{
+ __s32 index = adm_validate_and_get_port_index(port_id);
+ if (index < 0) {
+ pr_err("%s: Invalid port idx %d port_id 0x%x\n",
+ __func__, index, port_id);
+ return;
+ }
+ if ((srs_copp_idx[index] < 0) ||
+ (srs_copp_idx[index] >= MAX_COPPS_PER_PORT)) {
+ pr_debug("%s: send params called before copp open. so, caching\n",
+ __func__);
+ return;
+ }
+ pr_debug("SRS %s: called, port_id = %d, techs flags = %u\n",
+ __func__, port_id, techs);
+ /* force all if techs is set to 1 */
+ if (techs == 1)
+ techs = 0xFFFFFFFF;
+
+ if (techs & (1 << SRS_ID_WOWHD))
+ srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_WOWHD,
+ (void *)&msm_srs_trumedia_params.srs_params.wowhd);
+ if (techs & (1 << SRS_ID_CSHP))
+ srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_CSHP,
+ (void *)&msm_srs_trumedia_params.srs_params.cshp);
+ if (techs & (1 << SRS_ID_HPF))
+ srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_HPF,
+ (void *)&msm_srs_trumedia_params.srs_params.hpf);
+ if (techs & (1 << SRS_ID_AEQ))
+ srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_AEQ,
+ (void *)&msm_srs_trumedia_params.srs_params.aeq);
+ if (techs & (1 << SRS_ID_HL))
+ srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_HL,
+ (void *)&msm_srs_trumedia_params.srs_params.hl);
+ if (techs & (1 << SRS_ID_GEQ))
+ srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_GEQ,
+ (void *)&msm_srs_trumedia_params.srs_params.geq);
+ if (techs & (1 << SRS_ID_GLOBAL))
+ srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_GLOBAL,
+ (void *)&msm_srs_trumedia_params.srs_params.global);
+}
+
+
+static int msm_dts_srs_trumedia_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_dts_srs_trumedia_control_set_(int port_id,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ __u16 offset, value, max = sizeof(msm_srs_trumedia_params) >> 1;
+ if (SRS_CMD_UPLOAD ==
+ (ucontrol->value.integer.value[0] & SRS_CMD_UPLOAD)) {
+ __u32 techs = ucontrol->value.integer.value[0] & 0xFF;
+ __s32 index = adm_validate_and_get_port_index(port_id);
+ if (index < 0) {
+ pr_err("%s: Invalid port idx %d port_id 0x%x\n",
+ __func__, index, port_id);
+ return -EINVAL;
+ }
+ pr_debug("SRS %s: send params request, flag = %u\n",
+ __func__, techs);
+ if (srs_port_id[index] >= 0 && techs)
+ msm_dts_srs_tm_send_params(port_id, techs);
+ return 0;
+ }
+ offset = (__u16)((ucontrol->value.integer.value[0] &
+ SRS_PARAM_OFFSET_MASK) >> 16);
+ value = (__u16)(ucontrol->value.integer.value[0] &
+ SRS_PARAM_VALUE_MASK);
+ if (offset < max) {
+ msm_srs_trumedia_params.raw_params[offset] = value;
+ pr_debug("SRS %s: index set... (max %d, requested %d, value 0x%X)\n",
+ __func__, max, offset, value);
+ } else {
+ pr_err("SRS %s: index out of bounds! (max %d, requested %d)\n",
+ __func__, max, offset);
+ }
+ return 0;
+}
+
+static int msm_dts_srs_trumedia_control_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret, port_id;
+
+ pr_debug("SRS control normal called\n");
+ msm_pcm_routing_acquire_lock();
+ port_id = SLIMBUS_0_RX;
+ ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol);
+ msm_pcm_routing_release_lock();
+ return ret;
+}
+
+static int msm_dts_srs_trumedia_control_i2s_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret, port_id;
+
+ pr_debug("SRS control I2S called\n");
+ msm_pcm_routing_acquire_lock();
+ port_id = PRIMARY_I2S_RX;
+ ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol);
+ msm_pcm_routing_release_lock();
+ return ret;
+}
+
+static int msm_dts_srs_trumedia_control_mi2s_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret, port_id;
+
+ pr_debug("SRS control MI2S called\n");
+ msm_pcm_routing_acquire_lock();
+ port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+ ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol);
+ msm_pcm_routing_release_lock();
+ return ret;
+}
+
+static int msm_dts_srs_trumedia_control_hdmi_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret, port_id;
+
+ pr_debug("SRS control HDMI called\n");
+ msm_pcm_routing_acquire_lock();
+ port_id = HDMI_RX;
+ ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol);
+ msm_pcm_routing_release_lock();
+ return ret;
+}
+
+static const struct snd_kcontrol_new lpa_srs_trumedia_controls[] = {
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SRS TruMedia",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_soc_info_volsw,
+ .get = msm_dts_srs_trumedia_control_get,
+ .put = msm_dts_srs_trumedia_control_set,
+ .private_value = ((unsigned long)&(struct soc_mixer_control)
+ {.reg = SND_SOC_NOPM,
+ .rreg = SND_SOC_NOPM,
+ .shift = 0,
+ .rshift = 0,
+ .max = 0xFFFFFFFF,
+ .platform_max = 0xFFFFFFFF,
+ .invert = 0
+ })
+ }
+};
+
+static const struct snd_kcontrol_new lpa_srs_trumedia_controls_hdmi[] = {
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SRS TruMedia HDMI",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_soc_info_volsw,
+ .get = msm_dts_srs_trumedia_control_get,
+ .put = msm_dts_srs_trumedia_control_hdmi_set,
+ .private_value = ((unsigned long)&(struct soc_mixer_control)
+ {.reg = SND_SOC_NOPM,
+ .rreg = SND_SOC_NOPM,
+ .shift = 0,
+ .rshift = 0,
+ .max = 0xFFFFFFFF,
+ .platform_max = 0xFFFFFFFF,
+ .invert = 0
+ })
+ }
+};
+
+static const struct snd_kcontrol_new lpa_srs_trumedia_controls_i2s[] = {
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SRS TruMedia I2S",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_soc_info_volsw,
+ .get = msm_dts_srs_trumedia_control_get,
+ .put = msm_dts_srs_trumedia_control_i2s_set,
+ .private_value = ((unsigned long)&(struct soc_mixer_control)
+ {.reg = SND_SOC_NOPM,
+ .rreg = SND_SOC_NOPM,
+ .shift = 0,
+ .rshift = 0,
+ .max = 0xFFFFFFFF,
+ .platform_max = 0xFFFFFFFF,
+ .invert = 0
+ })
+ }
+};
+
+static const struct snd_kcontrol_new lpa_srs_trumedia_controls_mi2s[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SRS TruMedia MI2S",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_soc_info_volsw,
+ .get = msm_dts_srs_trumedia_control_get,
+ .put = msm_dts_srs_trumedia_control_mi2s_set,
+ .private_value = ((unsigned long)&(struct soc_mixer_control)
+ {
+ .reg = SND_SOC_NOPM,
+ .rreg = SND_SOC_NOPM,
+ .shift = 0,
+ .rshift = 0,
+ .max = 0xFFFFFFFF,
+ .platform_max = 0xFFFFFFFF,
+ .invert = 0
+ })
+ }
+};
+
+void msm_dts_srs_tm_add_controls(struct snd_soc_platform *platform)
+{
+ snd_soc_add_platform_controls(platform,
+ lpa_srs_trumedia_controls,
+ ARRAY_SIZE(lpa_srs_trumedia_controls));
+
+ snd_soc_add_platform_controls(platform,
+ lpa_srs_trumedia_controls_hdmi,
+ ARRAY_SIZE(lpa_srs_trumedia_controls_hdmi));
+
+ snd_soc_add_platform_controls(platform,
+ lpa_srs_trumedia_controls_i2s,
+ ARRAY_SIZE(lpa_srs_trumedia_controls_i2s));
+ snd_soc_add_platform_controls(platform,
+ lpa_srs_trumedia_controls_mi2s,
+ ARRAY_SIZE(lpa_srs_trumedia_controls_mi2s));
+}
+
+static int reg_ion_mem(void)
+{
+ int rc;
+ rc = msm_audio_ion_alloc("SRS_TRUMEDIA", &ion_client, &ion_handle,
+ ION_MEM_SIZE, &po.paddr, (size_t *)&po.size,
+ &po.kvaddr);
+ if (rc != 0)
+ pr_err("%s: failed to allocate memory.\n", __func__);
+ pr_debug("%s: exited ion_client = %pK, ion_handle = %pK, phys_addr = %lu, length = %d, vaddr = %pK, rc = 0x%x\n",
+ __func__, ion_client, ion_handle, (long)po.paddr,
+ (unsigned int)po.size, po.kvaddr, rc);
+ return rc;
+}
+
+void msm_dts_srs_tm_ion_memmap(struct param_outband *po_)
+{
+ if (po.kvaddr == NULL) {
+ pr_debug("%s: callingreg_ion_mem()\n", __func__);
+ reg_ion_mem();
+ }
+ po_->size = ION_MEM_SIZE;
+ po_->kvaddr = po.kvaddr;
+ po_->paddr = po.paddr;
+}
+
+static void unreg_ion_mem(void)
+{
+ msm_audio_ion_free(ion_client, ion_handle);
+ po.kvaddr = NULL;
+ po.paddr = 0;
+ po.size = 0;
+}
+
+void msm_dts_srs_tm_deinit(int port_id)
+{
+ set_port_id(port_id, -1);
+ atomic_dec(&ref_cnt);
+ if (po.kvaddr != NULL) {
+ if (!atomic_read(&ref_cnt)) {
+ pr_debug("%s: calling unreg_ion_mem()\n", __func__);
+ unreg_ion_mem();
+ }
+ }
+ return;
+}
+
+void msm_dts_srs_tm_init(int port_id, int copp_idx)
+{
+ int cur_ref_cnt = 0;
+
+ if (set_port_id(port_id, copp_idx) < 0) {
+ pr_err("%s: Invalid port_id: %d\n", __func__, port_id);
+ return;
+ }
+
+ cur_ref_cnt = atomic_read(&ref_cnt);
+ atomic_inc(&ref_cnt);
+ if (!cur_ref_cnt && po.kvaddr == NULL) {
+ pr_debug("%s: calling reg_ion_mem()\n", __func__);
+ if (reg_ion_mem() != 0) {
+ atomic_dec(&ref_cnt);
+ po.kvaddr = NULL;
+ return;
+ }
+ }
+ msm_dts_srs_tm_send_params(port_id, 1);
+ return;
+}
diff --git a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h
new file mode 100644
index 000000000000..7a45896b28ba
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_DTS_SRS_TM_CONFIG_H_
+#define _MSM_DTS_SRS_TM_CONFIG_H_
+
+#include <sound/soc.h>
+
+struct param_outband;
+
+#ifdef CONFIG_DTS_SRS_TM
+
+union srs_trumedia_params_u {
+ struct srs_trumedia_params srs_params;
+ __u16 raw_params[1];
+};
+
+void msm_dts_srs_tm_ion_memmap(struct param_outband *po_);
+void msm_dts_srs_tm_init(int port_id, int copp_idx);
+void msm_dts_srs_tm_deinit(int port_id);
+void msm_dts_srs_tm_add_controls(struct snd_soc_platform *platform);
+#else
+static inline void msm_dts_srs_tm_ion_memmap(struct param_outband *po_) { }
+static inline void msm_dts_srs_tm_init(int port_id, int copp_idx) { }
+static inline void msm_dts_srs_tm_deinit(int port_id) { }
+static inline void msm_dts_srs_tm_add_controls(
+ struct snd_soc_platform *platform) { }
+
+#endif
+
+#endif
+
diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
new file mode 100644
index 000000000000..b7e69771e991
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
@@ -0,0 +1,2454 @@
+/*
+ * Copyright (c) 2013-2017, 2019 Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/freezer.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/timer.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6lsm.h>
+#include <sound/lsm_params.h>
+#include <sound/pcm_params.h>
+#include "msm-pcm-routing-v2.h"
+
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 61440
+#define CAPTURE_MIN_PERIOD_SIZE 320
+#define LISTEN_MAX_STATUS_PAYLOAD_SIZE 256
+
+#define LAB_BUFFER_ALLOC 1
+#define LAB_BUFFER_DEALLOC 0
+
+static struct snd_pcm_hardware msm_pcm_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ .rates = (SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 16000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 4,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS *
+ CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 16000, 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+struct lsm_priv {
+ struct snd_pcm_substream *substream;
+ struct lsm_client *lsm_client;
+ struct snd_lsm_event_status_v3 *event_status;
+ spinlock_t event_lock;
+ wait_queue_head_t event_wait;
+ unsigned long event_avail;
+ atomic_t event_wait_stop;
+ atomic_t buf_count;
+ atomic_t read_abort;
+ wait_queue_head_t period_wait;
+ struct mutex lsm_api_lock;
+ int appl_cnt;
+ int dma_write;
+};
+
+enum { /* lsm session states */
+ IDLE = 0,
+ RUNNING,
+};
+
+static int msm_lsm_queue_lab_buffer(struct lsm_priv *prtd, int i)
+{
+ int rc = 0;
+ struct lsm_cmd_read cmd_read;
+ struct snd_soc_pcm_runtime *rtd;
+
+ if (!prtd || !prtd->lsm_client) {
+ pr_err("%s: Invalid params prtd %pK lsm client %pK\n",
+ __func__, prtd, ((!prtd) ? NULL : prtd->lsm_client));
+ return -EINVAL;
+ }
+ if (!prtd->substream || !prtd->substream->private_data) {
+ pr_err("%s: Invalid %s\n", __func__,
+ (!prtd->substream) ? "substream" : "private_data");
+ return -EINVAL;
+ }
+ rtd = prtd->substream->private_data;
+
+ if (!prtd->lsm_client->lab_buffer ||
+ i >= prtd->lsm_client->hw_params.period_count) {
+ dev_err(rtd->dev,
+ "%s: Lab buffer not setup %pK incorrect index %d period count %d\n",
+ __func__, prtd->lsm_client->lab_buffer, i,
+ prtd->lsm_client->hw_params.period_count);
+ return -EINVAL;
+ }
+ cmd_read.buf_addr_lsw =
+ lower_32_bits(prtd->lsm_client->lab_buffer[i].phys);
+ cmd_read.buf_addr_msw =
+ msm_audio_populate_upper_32_bits(
+ prtd->lsm_client->lab_buffer[i].phys);
+ cmd_read.buf_size = prtd->lsm_client->lab_buffer[i].size;
+ cmd_read.mem_map_handle =
+ prtd->lsm_client->lab_buffer[i].mem_map_handle;
+ rc = q6lsm_read(prtd->lsm_client, &cmd_read);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: error in queuing the lab buffer rc %d\n",
+ __func__, rc);
+ return rc;
+}
+
+static int lsm_lab_buffer_sanity(struct lsm_priv *prtd,
+ struct lsm_cmd_read_done *read_done, int *index)
+{
+ int i = 0, rc = -EINVAL;
+ struct snd_soc_pcm_runtime *rtd;
+
+ if (!prtd || !read_done || !index) {
+ pr_err("%s: Invalid params prtd %pK read_done %pK index %pK\n",
+ __func__, prtd, read_done, index);
+ return -EINVAL;
+ }
+
+ if (!prtd->substream || !prtd->substream->private_data) {
+ pr_err("%s: Invalid %s\n", __func__,
+ (!prtd->substream) ? "substream" : "private_data");
+ return -EINVAL;
+ }
+ rtd = prtd->substream->private_data;
+
+ if (!prtd->lsm_client->lab_enable || !prtd->lsm_client->lab_buffer) {
+ dev_err(rtd->dev,
+ "%s: Lab not enabled %d invalid lab buffer %pK\n",
+ __func__, prtd->lsm_client->lab_enable,
+ prtd->lsm_client->lab_buffer);
+ return -EINVAL;
+ }
+ for (i = 0; i < prtd->lsm_client->hw_params.period_count; i++) {
+ if ((lower_32_bits(prtd->lsm_client->lab_buffer[i].phys) ==
+ read_done->buf_addr_lsw) &&
+ (msm_audio_populate_upper_32_bits
+ (prtd->lsm_client->lab_buffer[i].phys) ==
+ read_done->buf_addr_msw) &&
+ (prtd->lsm_client->lab_buffer[i].mem_map_handle ==
+ read_done->mem_map_handle)) {
+ dev_dbg(rtd->dev,
+ "%s: Buffer found %pK memmap handle %d\n",
+ __func__, &prtd->lsm_client->lab_buffer[i].phys,
+ prtd->lsm_client->lab_buffer[i].mem_map_handle);
+ if (read_done->total_size >
+ prtd->lsm_client->lab_buffer[i].size) {
+ dev_err(rtd->dev,
+ "%s: Size mismatch call back size %d actual size %zd\n",
+ __func__, read_done->total_size,
+ prtd->lsm_client->lab_buffer[i].size);
+ rc = -EINVAL;
+ break;
+ } else {
+ *index = i;
+ rc = 0;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static void lsm_event_handler(uint32_t opcode, uint32_t token,
+ void *payload, uint16_t client_size,
+ void *priv)
+{
+ unsigned long flags;
+ struct lsm_priv *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_soc_pcm_runtime *rtd;
+ uint16_t status = 0;
+ uint16_t payload_size = 0;
+ uint16_t index = 0;
+ uint32_t event_ts_lsw = 0;
+ uint32_t event_ts_msw = 0;
+
+ if (!substream || !substream->private_data) {
+ pr_err("%s: Invalid %s\n", __func__,
+ (!substream) ? "substream" : "private_data");
+ return;
+ }
+ rtd = substream->private_data;
+
+ switch (opcode) {
+ case LSM_DATA_EVENT_READ_DONE: {
+ int rc;
+ struct lsm_cmd_read_done *read_done = payload;
+ int buf_index = 0;
+ if (prtd->lsm_client->session != token ||
+ !read_done) {
+ dev_err(rtd->dev,
+ "%s: EVENT_READ_DONE invalid callback, session %d callback %d payload %pK",
+ __func__, prtd->lsm_client->session,
+ token, read_done);
+ return;
+ }
+ if (atomic_read(&prtd->read_abort)) {
+ dev_dbg(rtd->dev,
+ "%s: read abort set skip data\n", __func__);
+ return;
+ }
+ if (!lsm_lab_buffer_sanity(prtd, read_done, &buf_index)) {
+ dev_dbg(rtd->dev,
+ "%s: process read done index %d\n",
+ __func__, buf_index);
+ if (buf_index >=
+ prtd->lsm_client->hw_params.period_count) {
+ dev_err(rtd->dev,
+ "%s: Invalid index %d buf_index max cnt %d\n",
+ __func__, buf_index,
+ prtd->lsm_client->hw_params.period_count);
+ return;
+ }
+ prtd->dma_write += read_done->total_size;
+ atomic_inc(&prtd->buf_count);
+ snd_pcm_period_elapsed(substream);
+ wake_up(&prtd->period_wait);
+ /* queue the next period buffer */
+ buf_index = (buf_index + 1) %
+ prtd->lsm_client->hw_params.period_count;
+ rc = msm_lsm_queue_lab_buffer(prtd, buf_index);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: error in queuing the lab buffer rc %d\n",
+ __func__, rc);
+ } else
+ dev_err(rtd->dev, "%s: Invalid lab buffer returned by dsp\n",
+ __func__);
+ break;
+ }
+
+ case LSM_SESSION_EVENT_DETECTION_STATUS:
+ if (client_size < 3 * sizeof(uint8_t)) {
+ dev_err(rtd->dev,
+ "%s: client_size has invalid size[%d]\n",
+ __func__, client_size);
+ return;
+ }
+ status = (uint16_t)((uint8_t *)payload)[0];
+ payload_size = (uint16_t)((uint8_t *)payload)[2];
+ index = 4;
+ dev_dbg(rtd->dev,
+ "%s: event detect status = %d payload size = %d\n",
+ __func__, status , payload_size);
+ break;
+
+ case LSM_SESSION_EVENT_DETECTION_STATUS_V2:
+ if (client_size < 2 * sizeof(uint8_t)) {
+ dev_err(rtd->dev,
+ "%s: client_size has invalid size[%d]\n",
+ __func__, client_size);
+ return;
+ }
+ status = (uint16_t)((uint8_t *)payload)[0];
+ payload_size = (uint16_t)((uint8_t *)payload)[1];
+ index = 2;
+ dev_dbg(rtd->dev,
+ "%s: event detect status = %d payload size = %d\n",
+ __func__, status , payload_size);
+ break;
+
+ case LSM_SESSION_EVENT_DETECTION_STATUS_V3:
+ if (client_size < 2 * (sizeof(uint32_t) + sizeof(uint8_t))) {
+ dev_err(rtd->dev,
+ "%s: client_size has invalid size[%d]\n",
+ __func__, client_size);
+ return;
+ }
+ event_ts_lsw = ((uint32_t *)payload)[0];
+ event_ts_msw = ((uint32_t *)payload)[1];
+ status = (uint16_t)((uint8_t *)payload)[8];
+ payload_size = (uint16_t)((uint8_t *)payload)[9];
+ index = 10;
+ dev_dbg(rtd->dev,
+ "%s: ts_msw = %u, ts_lsw = %u, event detect status = %d payload size = %d\n",
+ __func__, event_ts_msw, event_ts_lsw, status,
+ payload_size);
+ break;
+
+ default:
+ break;
+ }
+
+ if (opcode == LSM_SESSION_EVENT_DETECTION_STATUS ||
+ opcode == LSM_SESSION_EVENT_DETECTION_STATUS_V2 ||
+ opcode == LSM_SESSION_EVENT_DETECTION_STATUS_V3) {
+ spin_lock_irqsave(&prtd->event_lock, flags);
+ prtd->event_status = krealloc(prtd->event_status,
+ sizeof(struct snd_lsm_event_status_v3) +
+ payload_size, GFP_ATOMIC);
+ if (!prtd->event_status) {
+ dev_err(rtd->dev, "%s: no memory for event status\n",
+ __func__);
+ spin_unlock_irqrestore(&prtd->event_lock, flags);
+ return;
+ }
+ /*
+ * event status timestamp will be non-zero and valid if
+ * opcode is LSM_SESSION_EVENT_DETECTION_STATUS_V3
+ */
+ prtd->event_status->timestamp_lsw = event_ts_lsw;
+ prtd->event_status->timestamp_msw = event_ts_msw;
+ prtd->event_status->status = status;
+ prtd->event_status->payload_size = payload_size;
+
+ if (likely(prtd->event_status)) {
+ if (client_size >= (payload_size + index)) {
+ memcpy(prtd->event_status->payload,
+ &((uint8_t *)payload)[index],
+ payload_size);
+ prtd->event_avail = 1;
+ spin_unlock_irqrestore(&prtd->event_lock,
+ flags);
+ wake_up(&prtd->event_wait);
+ } else {
+ spin_unlock_irqrestore(&prtd->event_lock,
+ flags);
+ dev_err(rtd->dev,
+ "%s: Failed to copy memory with invalid size = %d\n",
+ __func__, payload_size);
+ return;
+ }
+ } else {
+ spin_unlock_irqrestore(&prtd->event_lock, flags);
+ dev_err(rtd->dev,
+ "%s: Couldn't allocate %d bytes of memory\n",
+ __func__, payload_size);
+ }
+ if (substream->timer_running)
+ snd_timer_interrupt(substream->timer, 1);
+ }
+}
+
+static int msm_lsm_lab_buffer_alloc(struct lsm_priv *lsm, int alloc)
+{
+ int ret = 0;
+ struct snd_dma_buffer *dma_buf = NULL;
+ if (!lsm) {
+ pr_err("%s: Invalid param lsm %pK\n", __func__, lsm);
+ return -EINVAL;
+ }
+ if (alloc) {
+ if (!lsm->substream) {
+ pr_err("%s: substream is NULL\n", __func__);
+ return -EINVAL;
+ }
+ ret = q6lsm_lab_buffer_alloc(lsm->lsm_client, alloc);
+ if (ret) {
+ pr_err("%s: alloc lab buffer failed ret %d\n",
+ __func__, ret);
+ goto exit;
+ }
+ dma_buf = &lsm->substream->dma_buffer;
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = lsm->substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = lsm->lsm_client->lab_buffer[0].data;
+ dma_buf->addr = lsm->lsm_client->lab_buffer[0].phys;
+ dma_buf->bytes = lsm->lsm_client->hw_params.buf_sz *
+ lsm->lsm_client->hw_params.period_count;
+ snd_pcm_set_runtime_buffer(lsm->substream, dma_buf);
+ } else {
+ ret = q6lsm_lab_buffer_alloc(lsm->lsm_client, alloc);
+ if (ret)
+ pr_err("%s: free lab buffer failed ret %d\n",
+ __func__, ret);
+ kfree(lsm->lsm_client->lab_buffer);
+ lsm->lsm_client->lab_buffer = NULL;
+ }
+exit:
+ return ret;
+}
+
+static int msm_lsm_get_conf_levels(struct lsm_client *client,
+ u8 *conf_levels_ptr)
+{
+ int rc = 0;
+
+ if (client->num_confidence_levels == 0) {
+ pr_debug("%s: no confidence levels provided\n",
+ __func__);
+ client->confidence_levels = NULL;
+ goto done;
+ }
+
+ client->confidence_levels =
+ kzalloc((sizeof(uint8_t) * client->num_confidence_levels),
+ GFP_KERNEL);
+ if (!client->confidence_levels) {
+ pr_err("%s: No memory for confidence\n"
+ "levels num of level from user = %d\n",
+ __func__, client->num_confidence_levels);
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(client->confidence_levels,
+ conf_levels_ptr,
+ client->num_confidence_levels)) {
+ pr_err("%s: copy from user failed, size = %d\n",
+ __func__, client->num_confidence_levels);
+ rc = -EFAULT;
+ goto copy_err;
+ }
+
+ return rc;
+
+copy_err:
+ kfree(client->confidence_levels);
+ client->confidence_levels = NULL;
+done:
+ return rc;
+
+}
+
+static int msm_lsm_set_epd(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int rc = 0;
+ struct snd_lsm_ep_det_thres epd_th;
+
+ if (p_info->param_size != sizeof(epd_th)) {
+ dev_err(rtd->dev,
+ "%s: Invalid param_size %d\n",
+ __func__, p_info->param_size);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&epd_th, p_info->param_data,
+ p_info->param_size)) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed, size = %d\n",
+ __func__, p_info->param_size);
+ rc = -EFAULT;
+ goto done;
+ }
+
+ rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+ &epd_th, LSM_ENDPOINT_DETECT_THRESHOLD);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: Failed to set epd param, err = %d\n",
+ __func__, rc);
+done:
+ return rc;
+}
+
+static int msm_lsm_set_mode(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_lsm_detect_mode mode;
+ int rc = 0;
+
+ if (p_info->param_size != sizeof(mode)) {
+ dev_err(rtd->dev,
+ "%s: Invalid param_size %d\n",
+ __func__, p_info->param_size);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&mode, p_info->param_data,
+ sizeof(mode))) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed, size = %zd\n",
+ __func__, sizeof(mode));
+ rc = -EFAULT;
+ goto done;
+ }
+
+ rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+ &mode, LSM_OPERATION_MODE);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: Failed to set det_mode param, err = %d\n",
+ __func__, rc);
+done:
+ return rc;
+}
+
+static int msm_lsm_set_gain(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_lsm_gain gain;
+ int rc = 0;
+
+ if (p_info->param_size != sizeof(gain)) {
+ dev_err(rtd->dev,
+ "%s: Invalid param_size %d\n",
+ __func__, p_info->param_size);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&gain, p_info->param_data,
+ sizeof(gain))) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed, size = %zd\n",
+ __func__, sizeof(gain));
+ rc = -EFAULT;
+ goto done;
+ }
+
+ rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+ &gain, LSM_GAIN);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: Failed to set det_mode param, err = %d\n",
+ __func__, rc);
+done:
+ return rc;
+}
+
+static int msm_lsm_set_conf(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int rc = 0;
+
+ if (p_info->param_size > MAX_NUM_CONFIDENCE) {
+ dev_err(rtd->dev,
+ "%s: invalid confidence levels %d\n",
+ __func__, p_info->param_size);
+ return -EINVAL;
+ }
+
+ prtd->lsm_client->num_confidence_levels =
+ p_info->param_size;
+ rc = msm_lsm_get_conf_levels(prtd->lsm_client,
+ p_info->param_data);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: get_conf_levels failed, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+ prtd->lsm_client->confidence_levels,
+ LSM_MIN_CONFIDENCE_LEVELS);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: Failed to set min_conf_levels, err = %d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+static int msm_lsm_reg_model(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int rc = 0;
+ u8 *snd_model_ptr;
+ size_t offset = 0;
+
+ rc = q6lsm_snd_model_buf_alloc(prtd->lsm_client,
+ p_info->param_size,
+ true);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: snd_model buf alloc failed, size = %d\n",
+ __func__, p_info->param_size);
+ return rc;
+ }
+
+ q6lsm_sm_set_param_data(prtd->lsm_client, p_info, &offset);
+
+ /*
+ * For set_param, advance the sound model data with the
+ * number of bytes required by param_data.
+ */
+ snd_model_ptr = ((u8 *) prtd->lsm_client->sound_model.data) + offset;
+
+ if (copy_from_user(snd_model_ptr,
+ p_info->param_data, p_info->param_size)) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user for snd_model failed, size = %d\n",
+ __func__, p_info->param_size);
+ rc = -EFAULT;
+ goto err_copy;
+ }
+ rc = q6lsm_set_one_param(prtd->lsm_client, p_info, NULL,
+ LSM_REG_SND_MODEL);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: Failed to set sound_model, err = %d\n",
+ __func__, rc);
+ goto err_copy;
+ }
+ return rc;
+
+err_copy:
+ q6lsm_snd_model_buf_free(prtd->lsm_client);
+ return rc;
+}
+
+static int msm_lsm_dereg_model(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int rc = 0;
+
+ rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+ NULL, LSM_DEREG_SND_MODEL);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: Failed to set det_mode param, err = %d\n",
+ __func__, rc);
+
+ q6lsm_snd_model_buf_free(prtd->lsm_client);
+
+ return rc;
+}
+
+static int msm_lsm_set_custom(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ u8 *data;
+ int rc = 0;
+
+ data = kzalloc(p_info->param_size, GFP_KERNEL);
+ if (!data) {
+ dev_err(rtd->dev,
+ "%s: no memory for custom data, size = %d\n",
+ __func__, p_info->param_size);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(data, p_info->param_data,
+ p_info->param_size)) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed for custom params, size = %d\n",
+ __func__, p_info->param_size);
+ rc = -EFAULT;
+ goto err_ret;
+ }
+
+ rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+ data, LSM_CUSTOM_PARAMS);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: Failed to set custom param, err = %d\n",
+ __func__, rc);
+
+err_ret:
+ kfree(data);
+ return rc;
+}
+
+static int msm_lsm_set_poll_enable(struct snd_pcm_substream *substream,
+ struct lsm_params_info *p_info)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_lsm_poll_enable poll_enable;
+ int rc = 0;
+
+ if (p_info->param_size != sizeof(poll_enable)) {
+ dev_err(rtd->dev,
+ "%s: Invalid param_size %d\n",
+ __func__, p_info->param_size);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&poll_enable, p_info->param_data,
+ sizeof(poll_enable))) {
+ dev_err(rtd->dev,
+ "%s: copy_from_user failed, size = %zd\n",
+ __func__, sizeof(poll_enable));
+ rc = -EFAULT;
+ goto done;
+ }
+
+ if (prtd->lsm_client->poll_enable == poll_enable.poll_en) {
+ dev_dbg(rtd->dev,
+ "%s: Polling for session %d already %s\n",
+ __func__, prtd->lsm_client->session,
+ (poll_enable.poll_en ? "enabled" : "disabled"));
+ rc = 0;
+ goto done;
+ }
+
+ rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+ &poll_enable, LSM_POLLING_ENABLE);
+ if (!rc) {
+ prtd->lsm_client->poll_enable = poll_enable.poll_en;
+ } else {
+ dev_err(rtd->dev,
+ "%s: Failed to set poll enable, err = %d\n",
+ __func__, rc);
+ }
+done:
+ return rc;
+}
+
+static int msm_lsm_process_params(struct snd_pcm_substream *substream,
+ struct snd_lsm_module_params *p_data,
+ void *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct lsm_params_info *p_info;
+ int i;
+ int rc = 0;
+
+ p_info = (struct lsm_params_info *) params;
+
+ for (i = 0; i < p_data->num_params; i++) {
+ dev_dbg(rtd->dev,
+ "%s: param (%d), module_id = 0x%x, param_id = 0x%x, param_size = 0x%x, param_type = 0x%x\n",
+ __func__, i, p_info->module_id,
+ p_info->param_id, p_info->param_size,
+ p_info->param_type);
+
+ switch (p_info->param_type) {
+ case LSM_ENDPOINT_DETECT_THRESHOLD:
+ rc = msm_lsm_set_epd(substream, p_info);
+ break;
+ case LSM_OPERATION_MODE:
+ rc = msm_lsm_set_mode(substream, p_info);
+ break;
+ case LSM_GAIN:
+ rc = msm_lsm_set_gain(substream, p_info);
+ break;
+ case LSM_MIN_CONFIDENCE_LEVELS:
+ rc = msm_lsm_set_conf(substream, p_info);
+ break;
+ case LSM_REG_SND_MODEL:
+ rc = msm_lsm_reg_model(substream, p_info);
+ break;
+ case LSM_DEREG_SND_MODEL:
+ rc = msm_lsm_dereg_model(substream, p_info);
+ break;
+ case LSM_CUSTOM_PARAMS:
+ rc = msm_lsm_set_custom(substream, p_info);
+ break;
+ case LSM_POLLING_ENABLE:
+ rc = msm_lsm_set_poll_enable(substream, p_info);
+ break;
+ default:
+ dev_err(rtd->dev,
+ "%s: Invalid param_type %d\n",
+ __func__, p_info->param_type);
+ rc = -EINVAL;
+ break;
+ }
+ if (rc) {
+ pr_err("%s: set_param fail for param_type %d\n",
+ __func__, p_info->param_type);
+ return rc;
+ }
+
+ p_info++;
+ }
+
+ return rc;
+}
+
+static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ unsigned long flags;
+ int ret;
+ struct snd_lsm_sound_model_v2 snd_model_v2;
+ struct snd_lsm_session_data session_data;
+ int rc = 0;
+ int xchg = 0;
+ struct snd_pcm_runtime *runtime;
+ struct lsm_priv *prtd;
+ struct snd_lsm_detection_params det_params;
+ uint8_t *confidence_level = NULL;
+
+ if (!substream || !substream->private_data) {
+ pr_err("%s: Invalid %s\n", __func__,
+ (!substream) ? "substream" : "private_data");
+ return -EINVAL;
+ }
+
+ runtime = substream->runtime;
+ prtd = runtime->private_data;
+ rtd = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_LSM_SET_SESSION_DATA:
+ dev_dbg(rtd->dev, "%s: set session data\n", __func__);
+ if (copy_from_user(&session_data, arg,
+ sizeof(session_data))) {
+ dev_err(rtd->dev, "%s: %s: copy_from_user failed\n",
+ __func__, "LSM_SET_SESSION_DATA");
+ return -EFAULT;
+ }
+
+ if (session_data.app_id != LSM_VOICE_WAKEUP_APP_ID_V2) {
+ dev_err(rtd->dev,
+ "%s:Invalid App id %d for Listen client\n",
+ __func__, session_data.app_id);
+ rc = -EINVAL;
+ break;
+ }
+
+ prtd->lsm_client->app_id = session_data.app_id;
+ ret = q6lsm_open(prtd->lsm_client,
+ prtd->lsm_client->app_id);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "%s: lsm open failed, %d\n",
+ __func__, ret);
+ return ret;
+ }
+ prtd->lsm_client->opened = true;
+ dev_dbg(rtd->dev, "%s: Session_ID = %d, APP ID = %d\n",
+ __func__,
+ prtd->lsm_client->session,
+ prtd->lsm_client->app_id);
+ break;
+ case SNDRV_LSM_REG_SND_MODEL_V2:
+ dev_dbg(rtd->dev, "%s: Registering sound model V2\n",
+ __func__);
+ memcpy(&snd_model_v2, arg,
+ sizeof(struct snd_lsm_sound_model_v2));
+ if (snd_model_v2.num_confidence_levels >
+ MAX_NUM_CONFIDENCE) {
+ dev_err(rtd->dev,
+ "%s: Invalid conf_levels = %d, maximum allowed = %d\n",
+ __func__, snd_model_v2.num_confidence_levels,
+ MAX_NUM_CONFIDENCE);
+ rc = -EINVAL;
+ break;
+ }
+ rc = q6lsm_snd_model_buf_alloc(prtd->lsm_client,
+ snd_model_v2.data_size, false);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: q6lsm buffer alloc failed V2, size %d\n",
+ __func__, snd_model_v2.data_size);
+ break;
+ }
+ if (copy_from_user(prtd->lsm_client->sound_model.data,
+ snd_model_v2.data, snd_model_v2.data_size)) {
+ dev_err(rtd->dev,
+ "%s: copy from user data failed\n"
+ "data %pK size %d\n", __func__,
+ snd_model_v2.data, snd_model_v2.data_size);
+ q6lsm_snd_model_buf_free(prtd->lsm_client);
+ rc = -EFAULT;
+ break;
+ }
+
+ dev_dbg(rtd->dev, "SND Model Magic no byte[0] %x,\n"
+ "byte[1] %x, byte[2] %x byte[3] %x\n",
+ snd_model_v2.data[0], snd_model_v2.data[1],
+ snd_model_v2.data[2], snd_model_v2.data[3]);
+ prtd->lsm_client->num_confidence_levels =
+ snd_model_v2.num_confidence_levels;
+
+ rc = msm_lsm_get_conf_levels(prtd->lsm_client,
+ snd_model_v2.confidence_level);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: get_conf_levels failed, err = %d\n",
+ __func__, rc);
+ break;
+ }
+
+ rc = q6lsm_register_sound_model(prtd->lsm_client,
+ snd_model_v2.detection_mode,
+ snd_model_v2.detect_failure);
+ if (rc < 0) {
+ dev_err(rtd->dev,
+ "%s: Register snd Model v2 failed =%d\n",
+ __func__, rc);
+ kfree(confidence_level);
+ q6lsm_snd_model_buf_free(prtd->lsm_client);
+ }
+
+ kfree(prtd->lsm_client->confidence_levels);
+ prtd->lsm_client->confidence_levels = NULL;
+ break;
+
+ case SNDRV_LSM_SET_PARAMS:
+ dev_dbg(rtd->dev, "%s: set_params\n", __func__);
+ memcpy(&det_params, arg,
+ sizeof(det_params));
+ if (det_params.num_confidence_levels >
+ MAX_NUM_CONFIDENCE) {
+ rc = -EINVAL;
+ break;
+ }
+
+ prtd->lsm_client->num_confidence_levels =
+ det_params.num_confidence_levels;
+
+ rc = msm_lsm_get_conf_levels(prtd->lsm_client,
+ det_params.conf_level);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: Failed to get conf_levels, err = %d\n",
+ __func__, rc);
+ break;
+ }
+
+ rc = q6lsm_set_data(prtd->lsm_client,
+ det_params.detect_mode,
+ det_params.detect_failure);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: Failed to set params, err = %d\n",
+ __func__, rc);
+
+ kfree(prtd->lsm_client->confidence_levels);
+ prtd->lsm_client->confidence_levels = NULL;
+
+ break;
+
+ case SNDRV_LSM_DEREG_SND_MODEL:
+ dev_dbg(rtd->dev, "%s: Deregistering sound model\n",
+ __func__);
+ rc = q6lsm_deregister_sound_model(prtd->lsm_client);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: Sound model de-register failed, err = %d\n",
+ __func__, rc);
+ break;
+
+ case SNDRV_LSM_EVENT_STATUS:
+ case SNDRV_LSM_EVENT_STATUS_V3: {
+ uint32_t ts_lsw, ts_msw;
+ uint16_t status = 0, payload_size = 0;
+
+ dev_dbg(rtd->dev, "%s: Get event status\n", __func__);
+ atomic_set(&prtd->event_wait_stop, 0);
+
+ /*
+ * Release the api lock before wait to allow
+ * other IOCTLs to be invoked while waiting
+ * for event
+ */
+ mutex_unlock(&prtd->lsm_api_lock);
+ rc = wait_event_freezable(prtd->event_wait,
+ (cmpxchg(&prtd->event_avail, 1, 0) ||
+ (xchg = atomic_cmpxchg(&prtd->event_wait_stop,
+ 1, 0))));
+ mutex_lock(&prtd->lsm_api_lock);
+ dev_dbg(rtd->dev, "%s: wait_event_freezable %d event_wait_stop %d\n",
+ __func__, rc, xchg);
+ if (!rc && !xchg) {
+ dev_dbg(rtd->dev, "%s: New event available %ld\n",
+ __func__, prtd->event_avail);
+ spin_lock_irqsave(&prtd->event_lock, flags);
+
+ if (prtd->event_status) {
+ payload_size = prtd->event_status->payload_size;
+ ts_lsw = prtd->event_status->timestamp_lsw;
+ ts_msw = prtd->event_status->timestamp_msw;
+ status = prtd->event_status->status;
+ spin_unlock_irqrestore(&prtd->event_lock,
+ flags);
+ } else {
+ spin_unlock_irqrestore(&prtd->event_lock,
+ flags);
+ rc = -EINVAL;
+ dev_err(rtd->dev,
+ "%s: prtd->event_status is NULL\n",
+ __func__);
+ break;
+ }
+
+ if (cmd == SNDRV_LSM_EVENT_STATUS) {
+ struct snd_lsm_event_status *user = arg;
+
+ if (user->payload_size < payload_size) {
+ dev_dbg(rtd->dev,
+ "%s: provided %d bytes isn't enough, needs %d bytes\n",
+ __func__, user->payload_size,
+ payload_size);
+ rc = -ENOMEM;
+ } else {
+ user->status = status;
+ user->payload_size = payload_size;
+ memcpy(user->payload,
+ prtd->event_status->payload,
+ payload_size);
+ }
+ } else {
+ struct snd_lsm_event_status_v3 *user_v3 = arg;
+
+ if (user_v3->payload_size < payload_size) {
+ dev_dbg(rtd->dev,
+ "%s: provided %d bytes isn't enough, needs %d bytes\n",
+ __func__, user_v3->payload_size,
+ payload_size);
+ rc = -ENOMEM;
+ } else {
+ user_v3->timestamp_lsw = ts_lsw;
+ user_v3->timestamp_msw = ts_msw;
+ user_v3->status = status;
+ user_v3->payload_size = payload_size;
+ memcpy(user_v3->payload,
+ prtd->event_status->payload,
+ payload_size);
+ }
+ }
+ if (!rc) {
+ if (prtd->lsm_client->lab_enable
+ && !prtd->lsm_client->lab_started
+ && prtd->event_status->status ==
+ LSM_VOICE_WAKEUP_STATUS_DETECTED) {
+ atomic_set(&prtd->read_abort, 0);
+ atomic_set(&prtd->buf_count, 0);
+ prtd->appl_cnt = 0;
+ prtd->dma_write = 0;
+ rc = msm_lsm_queue_lab_buffer(prtd,
+ 0);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: Queue buffer failed for lab rc = %d\n",
+ __func__, rc);
+ else
+ prtd->lsm_client->lab_started
+ = true;
+ }
+ }
+ } else if (xchg) {
+ dev_dbg(rtd->dev, "%s: Wait aborted\n", __func__);
+ rc = 0;
+ }
+ break;
+ }
+
+ case SNDRV_LSM_ABORT_EVENT:
+ dev_dbg(rtd->dev, "%s: Aborting event status wait\n",
+ __func__);
+ atomic_set(&prtd->event_wait_stop, 1);
+ wake_up(&prtd->event_wait);
+ break;
+
+ case SNDRV_LSM_START:
+ dev_dbg(rtd->dev, "%s: Starting LSM client session\n",
+ __func__);
+ if (!prtd->lsm_client->started) {
+ ret = q6lsm_start(prtd->lsm_client, true);
+ if (!ret) {
+ prtd->lsm_client->started = true;
+ dev_dbg(rtd->dev, "%s: LSM client session started\n",
+ __func__);
+ }
+ }
+ break;
+
+ case SNDRV_LSM_STOP: {
+ dev_dbg(rtd->dev,
+ "%s: Stopping LSM client session\n",
+ __func__);
+ if (prtd->lsm_client->started) {
+ if (prtd->lsm_client->lab_enable) {
+ atomic_set(&prtd->read_abort, 1);
+ if (prtd->lsm_client->lab_started) {
+ ret = q6lsm_stop_lab(prtd->lsm_client);
+ if (ret)
+ dev_err(rtd->dev,
+ "%s: stop lab failed ret %d\n",
+ __func__, ret);
+ prtd->lsm_client->lab_started = false;
+ }
+ }
+ ret = q6lsm_stop(prtd->lsm_client, true);
+ if (!ret)
+ dev_dbg(rtd->dev,
+ "%s: LSM client session stopped %d\n",
+ __func__, ret);
+ prtd->lsm_client->started = false;
+ }
+ break;
+ }
+ case SNDRV_LSM_LAB_CONTROL: {
+ u32 enable;
+
+ if (copy_from_user(&enable, arg, sizeof(enable))) {
+ dev_err(rtd->dev, "%s: %s: copy_frm_user failed\n",
+ __func__, "LSM_LAB_CONTROL");
+ return -EFAULT;
+ }
+
+ dev_dbg(rtd->dev, "%s: ioctl %s, enable = %d\n",
+ __func__, "SNDRV_LSM_LAB_CONTROL", enable);
+ if (!prtd->lsm_client->started) {
+ if (prtd->lsm_client->lab_enable == enable) {
+ dev_dbg(rtd->dev,
+ "%s: Lab for session %d already %s\n",
+ __func__, prtd->lsm_client->session,
+ enable ? "enabled" : "disabled");
+ rc = 0;
+ break;
+ }
+ rc = q6lsm_lab_control(prtd->lsm_client, enable);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: ioctl %s failed rc %d to %s lab for session %d\n",
+ __func__, "SNDRV_LAB_CONTROL", rc,
+ enable ? "enable" : "disable",
+ prtd->lsm_client->session);
+ } else {
+ rc = msm_lsm_lab_buffer_alloc(prtd,
+ enable ? LAB_BUFFER_ALLOC
+ : LAB_BUFFER_DEALLOC);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: msm_lsm_lab_buffer_alloc failed rc %d for %s",
+ __func__, rc,
+ enable ? "ALLOC" : "DEALLOC");
+ if (!rc)
+ prtd->lsm_client->lab_enable = enable;
+ }
+ } else {
+ dev_err(rtd->dev, "%s: ioctl %s issued after start",
+ __func__, "SNDRV_LSM_LAB_CONTROL");
+ rc = -EINVAL;
+ }
+ break;
+ }
+ case SNDRV_LSM_STOP_LAB:
+ dev_dbg(rtd->dev, "%s: stopping LAB\n", __func__);
+ if (prtd->lsm_client->lab_enable &&
+ prtd->lsm_client->lab_started) {
+ atomic_set(&prtd->read_abort, 1);
+ rc = q6lsm_stop_lab(prtd->lsm_client);
+ if (rc)
+ dev_err(rtd->dev,
+ "%s: Lab stop failed for session %d rc %d\n",
+ __func__,
+ prtd->lsm_client->session, rc);
+ prtd->lsm_client->lab_started = false;
+ }
+ break;
+
+ case SNDRV_LSM_SET_PORT:
+ dev_dbg(rtd->dev, "%s: set LSM port\n", __func__);
+ rc = q6lsm_set_port_connected(prtd->lsm_client);
+ break;
+
+ case SNDRV_LSM_SET_FWK_MODE_CONFIG: {
+ u32 mode;
+
+ if (copy_from_user(&mode, (void __user *) arg, sizeof(mode))) {
+ dev_err(rtd->dev, "%s: %s: copy_frm_user failed\n",
+ __func__, "LSM_SET_FWK_MODE_CONFIG");
+ return -EFAULT;
+ }
+
+ dev_dbg(rtd->dev, "%s: ioctl %s, enable = %d\n",
+ __func__, "SNDRV_LSM_SET_FWK_MODE_CONFIG", mode);
+ if (prtd->lsm_client->event_mode == mode) {
+ dev_dbg(rtd->dev,
+ "%s: mode for %d already set to %d\n",
+ __func__, prtd->lsm_client->session, mode);
+ rc = 0;
+ } else {
+ dev_dbg(rtd->dev, "%s: Event mode = %d\n",
+ __func__, mode);
+ rc = q6lsm_set_fwk_mode_cfg(prtd->lsm_client, mode);
+ if (!rc)
+ prtd->lsm_client->event_mode = mode;
+ else
+ dev_err(rtd->dev,
+ "%s: set event mode failed %d\n",
+ __func__, rc);
+ }
+ break;
+ }
+
+ default:
+ dev_dbg(rtd->dev,
+ "%s: Falling into default snd_lib_ioctl cmd 0x%x\n",
+ __func__, cmd);
+ rc = snd_pcm_lib_ioctl(substream, cmd, arg);
+ break;
+ }
+
+ if (!rc)
+ dev_dbg(rtd->dev, "%s: leave (%d)\n",
+ __func__, rc);
+ else
+ dev_err(rtd->dev, "%s: cmd 0x%x failed %d\n",
+ __func__, cmd, rc);
+
+ return rc;
+}
+#ifdef CONFIG_COMPAT
+
+struct snd_lsm_event_status32 {
+ u16 status;
+ u16 payload_size;
+ u8 payload[0];
+};
+
+struct snd_lsm_event_status_v3_32 {
+ u32 timestamp_lsw;
+ u32 timestamp_msw;
+ u16 status;
+ u16 payload_size;
+ u8 payload[0];
+};
+
+struct snd_lsm_sound_model_v2_32 {
+ compat_uptr_t data;
+ compat_uptr_t confidence_level;
+ u32 data_size;
+ enum lsm_detection_mode detection_mode;
+ u8 num_confidence_levels;
+ bool detect_failure;
+};
+
+struct snd_lsm_detection_params_32 {
+ compat_uptr_t conf_level;
+ enum lsm_detection_mode detect_mode;
+ u8 num_confidence_levels;
+ bool detect_failure;
+};
+
+struct lsm_params_info_32 {
+ u32 module_id;
+ u32 param_id;
+ u32 param_size;
+ compat_uptr_t param_data;
+ uint32_t param_type;
+};
+
+struct snd_lsm_module_params_32 {
+ compat_uptr_t params;
+ u32 num_params;
+ u32 data_size;
+};
+
+enum {
+ SNDRV_LSM_REG_SND_MODEL_V2_32 =
+ _IOW('U', 0x07, struct snd_lsm_sound_model_v2_32),
+ SNDRV_LSM_SET_PARAMS_32 =
+ _IOW('U', 0x0A, struct snd_lsm_detection_params_32),
+ SNDRV_LSM_SET_MODULE_PARAMS_32 =
+ _IOW('U', 0x0B, struct snd_lsm_module_params_32),
+ SNDRV_LSM_EVENT_STATUS_V3_32 =
+ _IOW('U', 0x0F, struct snd_lsm_event_status_v3_32),
+};
+
+static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream,
+ unsigned int cmd, void __user *arg)
+{
+ struct snd_pcm_runtime *runtime;
+ struct lsm_priv *prtd;
+ struct snd_soc_pcm_runtime *rtd;
+ int err = 0;
+ u32 size = 0;
+
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
+
+ if (!substream || !substream->private_data) {
+ pr_err("%s: Invalid %s\n", __func__,
+ (!substream) ? "substream" : "private_data");
+ return -EINVAL;
+ }
+ runtime = substream->runtime;
+ rtd = substream->private_data;
+ prtd = runtime->private_data;
+
+ mutex_lock(&prtd->lsm_api_lock);
+
+ switch (cmd) {
+ case SNDRV_LSM_EVENT_STATUS: {
+ struct snd_lsm_event_status *user = NULL, userarg32;
+ struct snd_lsm_event_status *user32 = NULL;
+ if (copy_from_user(&userarg32, arg, sizeof(userarg32))) {
+ dev_err(rtd->dev, "%s: err copyuser ioctl %s\n",
+ __func__, "SNDRV_LSM_EVENT_STATUS");
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (userarg32.payload_size >
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+ pr_err("%s: payload_size %d is invalid, max allowed = %d\n",
+ __func__, userarg32.payload_size,
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+ err = -EINVAL;
+ goto done;
+ }
+
+ size = sizeof(*user) + userarg32.payload_size;
+ user = kzalloc(size, GFP_KERNEL);
+ if (!user) {
+ dev_err(rtd->dev,
+ "%s: Allocation failed event status size %d\n",
+ __func__, size);
+ err = -EFAULT;
+ goto done;
+ } else {
+ cmd = SNDRV_LSM_EVENT_STATUS;
+ user->payload_size = userarg32.payload_size;
+ err = msm_lsm_ioctl_shared(substream, cmd, user);
+ }
+ /* Update size with actual payload size */
+ size = sizeof(userarg32) + user->payload_size;
+ if (!err && !access_ok(VERIFY_WRITE, arg, size)) {
+ dev_err(rtd->dev,
+ "%s: write verify failed size %d\n",
+ __func__, size);
+ err = -EFAULT;
+ }
+ if (!err) {
+ user32 = kzalloc(size, GFP_KERNEL);
+ if (!user32) {
+ dev_err(rtd->dev,
+ "%s: Allocation event user status size %d\n",
+ __func__, size);
+ err = -EFAULT;
+ } else {
+ user32->status = user->status;
+ user32->payload_size = user->payload_size;
+ memcpy(user32->payload,
+ user->payload, user32->payload_size);
+ }
+ }
+ if (!err && (copy_to_user(arg, user32, size))) {
+ dev_err(rtd->dev, "%s: failed to copy payload %d",
+ __func__, size);
+ err = -EFAULT;
+ }
+ kfree(user);
+ kfree(user32);
+ if (err)
+ dev_err(rtd->dev, "%s: lsmevent failed %d",
+ __func__, err);
+ break;
+ }
+
+ case SNDRV_LSM_EVENT_STATUS_V3_32: {
+ struct snd_lsm_event_status_v3_32 userarg32, *user32 = NULL;
+ struct snd_lsm_event_status_v3 *user = NULL;
+
+ if (copy_from_user(&userarg32, arg, sizeof(userarg32))) {
+ dev_err(rtd->dev, "%s: err copyuser ioctl %s\n",
+ __func__, "SNDRV_LSM_EVENT_STATUS_V3_32");
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (userarg32.payload_size >
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+ pr_err("%s: payload_size %d is invalid, max allowed = %d\n",
+ __func__, userarg32.payload_size,
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+ err = -EINVAL;
+ goto done;
+ }
+
+ size = sizeof(*user) + userarg32.payload_size;
+ user = kzalloc(size, GFP_KERNEL);
+ if (!user) {
+ dev_err(rtd->dev,
+ "%s: Allocation failed event status size %d\n",
+ __func__, size);
+ err = -EFAULT;
+ goto done;
+ }
+ cmd = SNDRV_LSM_EVENT_STATUS_V3;
+ user->payload_size = userarg32.payload_size;
+ err = msm_lsm_ioctl_shared(substream, cmd, user);
+
+ /* Update size with actual payload size */
+ size = sizeof(userarg32) + user->payload_size;
+ if (!err && !access_ok(VERIFY_WRITE, arg, size)) {
+ dev_err(rtd->dev,
+ "%s: write verify failed size %d\n",
+ __func__, size);
+ err = -EFAULT;
+ }
+ if (!err) {
+ user32 = kzalloc(size, GFP_KERNEL);
+ if (!user32) {
+ dev_err(rtd->dev,
+ "%s: Allocation event user status size %d\n",
+ __func__, size);
+ err = -EFAULT;
+ } else {
+ user32->timestamp_lsw = user->timestamp_lsw;
+ user32->timestamp_msw = user->timestamp_msw;
+ user32->status = user->status;
+ user32->payload_size = user->payload_size;
+ memcpy(user32->payload,
+ user->payload, user32->payload_size);
+ }
+ }
+ if (!err && (copy_to_user(arg, user32, size))) {
+ dev_err(rtd->dev, "%s: failed to copy payload %d",
+ __func__, size);
+ err = -EFAULT;
+ }
+ kfree(user);
+ kfree(user32);
+ if (err)
+ dev_err(rtd->dev, "%s: lsmevent failed %d",
+ __func__, err);
+ break;
+ }
+
+ case SNDRV_LSM_REG_SND_MODEL_V2_32: {
+ struct snd_lsm_sound_model_v2_32 snd_modelv232;
+ struct snd_lsm_sound_model_v2 snd_modelv2;
+
+ if (prtd->lsm_client->use_topology) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if using topology\n",
+ __func__, "REG_SND_MODEL_V2");
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&snd_modelv232, arg,
+ sizeof(snd_modelv232))) {
+ err = -EFAULT;
+ dev_err(rtd->dev,
+ "%s: copy user failed, size %zd %s\n",
+ __func__,
+ sizeof(struct snd_lsm_sound_model_v2_32),
+ "SNDRV_LSM_REG_SND_MODEL_V2_32");
+ } else {
+ snd_modelv2.confidence_level =
+ compat_ptr(snd_modelv232.confidence_level);
+ snd_modelv2.data = compat_ptr(snd_modelv232.data);
+ snd_modelv2.data_size = snd_modelv232.data_size;
+ snd_modelv2.detect_failure =
+ snd_modelv232.detect_failure;
+ snd_modelv2.detection_mode =
+ snd_modelv232.detection_mode;
+ snd_modelv2.num_confidence_levels =
+ snd_modelv232.num_confidence_levels;
+ cmd = SNDRV_LSM_REG_SND_MODEL_V2;
+ err = msm_lsm_ioctl_shared(substream, cmd,
+ &snd_modelv2);
+ if (err)
+ dev_err(rtd->dev,
+ "%s: ioctl %s failed\n", __func__,
+ "SNDDRV_LSM_REG_SND_MODEL_V2_32");
+ }
+ break;
+ }
+
+ case SNDRV_LSM_SET_PARAMS_32:{
+ struct snd_lsm_detection_params_32 det_params32;
+ struct snd_lsm_detection_params det_params;
+
+ if (prtd->lsm_client->use_topology) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if using topology\n",
+ __func__, "SET_PARAMS_32");
+ err = -EINVAL;
+ }
+
+ if (copy_from_user(&det_params32, arg,
+ sizeof(det_params32))) {
+ err = -EFAULT;
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size = %zd\n",
+ __func__, "SNDRV_LSM_SET_PARAMS_32",
+ sizeof(det_params32));
+ } else {
+ det_params.conf_level =
+ compat_ptr(det_params32.conf_level);
+ det_params.detect_mode =
+ det_params32.detect_mode;
+ det_params.num_confidence_levels =
+ det_params32.num_confidence_levels;
+ det_params.detect_failure =
+ det_params32.detect_failure;
+ cmd = SNDRV_LSM_SET_PARAMS;
+ err = msm_lsm_ioctl_shared(substream, cmd,
+ &det_params);
+ if (err)
+ dev_err(rtd->dev,
+ "%s: ioctl %s failed\n", __func__,
+ "SNDRV_LSM_SET_PARAMS");
+ }
+ break;
+ }
+
+ case SNDRV_LSM_SET_MODULE_PARAMS_32: {
+ struct snd_lsm_module_params_32 p_data_32;
+ struct snd_lsm_module_params p_data;
+ u8 *params, *params32;
+ size_t p_size;
+ struct lsm_params_info_32 *p_info_32;
+ struct lsm_params_info *p_info;
+ int i;
+
+ if (!prtd->lsm_client->use_topology) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if not using topology\n",
+ __func__, "SET_MODULE_PARAMS_32");
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&p_data_32, arg,
+ sizeof(p_data_32))) {
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size = %zd\n",
+ __func__, "SET_MODULE_PARAMS_32",
+ sizeof(p_data_32));
+ err = -EFAULT;
+ goto done;
+ }
+
+ p_data.params = compat_ptr(p_data_32.params);
+ p_data.num_params = p_data_32.num_params;
+ p_data.data_size = p_data_32.data_size;
+
+ if (p_data.num_params > LSM_PARAMS_MAX) {
+ dev_err(rtd->dev,
+ "%s: %s: Invalid num_params %d\n",
+ __func__, "SET_MODULE_PARAMS_32",
+ p_data.num_params);
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (p_data.data_size !=
+ (p_data.num_params * sizeof(struct lsm_params_info_32))) {
+ dev_err(rtd->dev,
+ "%s: %s: Invalid size %d\n",
+ __func__, "SET_MODULE_PARAMS_32",
+ p_data.data_size);
+ err = -EINVAL;
+ goto done;
+ }
+
+ p_size = sizeof(struct lsm_params_info_32) *
+ p_data.num_params;
+
+ params32 = kzalloc(p_size, GFP_KERNEL);
+ if (!params32) {
+ dev_err(rtd->dev,
+ "%s: no memory for params32, size = %zd\n",
+ __func__, p_size);
+ err = -ENOMEM;
+ goto done;
+ }
+
+ p_size = sizeof(struct lsm_params_info) * p_data.num_params;
+ params = kzalloc(p_size, GFP_KERNEL);
+ if (!params) {
+ dev_err(rtd->dev,
+ "%s: no memory for params, size = %zd\n",
+ __func__, p_size);
+ kfree(params32);
+ err = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(params32, p_data.params,
+ p_data.data_size)) {
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size = %d\n",
+ __func__, "params32", p_data.data_size);
+ kfree(params32);
+ kfree(params);
+ err = -EFAULT;
+ goto done;
+ }
+
+ p_info_32 = (struct lsm_params_info_32 *) params32;
+ p_info = (struct lsm_params_info *) params;
+ for (i = 0; i < p_data.num_params; i++) {
+ p_info->module_id = p_info_32->module_id;
+ p_info->param_id = p_info_32->param_id;
+ p_info->param_size = p_info_32->param_size;
+ p_info->param_data = compat_ptr(p_info_32->param_data);
+ p_info->param_type = p_info_32->param_type;
+
+ p_info_32++;
+ p_info++;
+ }
+
+ err = msm_lsm_process_params(substream,
+ &p_data, params);
+ if (err)
+ dev_err(rtd->dev,
+ "%s: Failed to process params, err = %d\n",
+ __func__, err);
+ kfree(params);
+ kfree(params32);
+ break;
+ }
+ case SNDRV_LSM_REG_SND_MODEL_V2:
+ case SNDRV_LSM_SET_PARAMS:
+ case SNDRV_LSM_SET_MODULE_PARAMS:
+ /*
+ * In ideal cases, the compat_ioctl should never be called
+ * with the above unlocked ioctl commands. Print error
+ * and return error if it does.
+ */
+ dev_err(rtd->dev,
+ "%s: Invalid cmd for compat_ioctl\n",
+ __func__);
+ err = -EINVAL;
+ break;
+ default:
+ err = msm_lsm_ioctl_shared(substream, cmd, arg);
+ break;
+ }
+done:
+ mutex_unlock(&prtd->lsm_api_lock);
+ return err;
+}
+#else
+#define msm_lsm_ioctl_compat NULL
+#endif
+
+static int msm_lsm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ int err = 0;
+ u32 size = 0;
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *rtd;
+ struct lsm_priv *prtd;
+
+ if (!substream || !substream->private_data) {
+ pr_err("%s: Invalid %s\n", __func__,
+ (!substream) ? "substream" : "private_data");
+ return -EINVAL;
+ }
+ runtime = substream->runtime;
+ prtd = runtime->private_data;
+ rtd = substream->private_data;
+
+ mutex_lock(&prtd->lsm_api_lock);
+ switch (cmd) {
+ case SNDRV_LSM_REG_SND_MODEL_V2: {
+ struct snd_lsm_sound_model_v2 snd_model_v2;
+
+ if (prtd->lsm_client->use_topology) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if using topology\n",
+ __func__, "REG_SND_MODEL_V2");
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&snd_model_v2, arg, sizeof(snd_model_v2))) {
+ err = -EFAULT;
+ dev_err(rtd->dev,
+ "%s: copy from user failed, size %zd\n",
+ __func__,
+ sizeof(struct snd_lsm_sound_model_v2));
+ }
+ if (!err)
+ err = msm_lsm_ioctl_shared(substream, cmd,
+ &snd_model_v2);
+ if (err)
+ dev_err(rtd->dev,
+ "%s REG_SND_MODEL failed err %d\n",
+ __func__, err);
+ goto done;
+ }
+ break;
+ case SNDRV_LSM_SET_PARAMS: {
+ struct snd_lsm_detection_params det_params;
+
+ if (prtd->lsm_client->use_topology) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if using topology\n",
+ __func__, "SET_PARAMS");
+ err = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: SNDRV_LSM_SET_PARAMS\n", __func__);
+
+ if (copy_from_user(&det_params, arg,
+ sizeof(det_params))) {
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size %zd\n",
+ __func__, "SNDRV_LSM_SET_PARAMS",
+ sizeof(det_params));
+ err = -EFAULT;
+ }
+
+ if (!err)
+ err = msm_lsm_ioctl_shared(substream, cmd,
+ &det_params);
+ else
+ dev_err(rtd->dev,
+ "%s: LSM_SET_PARAMS failed, err %d\n",
+ __func__, err);
+
+ goto done;
+ }
+
+ case SNDRV_LSM_SET_MODULE_PARAMS: {
+ struct snd_lsm_module_params p_data;
+ size_t p_size;
+ u8 *params;
+
+ if (!prtd->lsm_client->use_topology) {
+ dev_err(rtd->dev,
+ "%s: %s: not supported if not using topology\n",
+ __func__, "SET_MODULE_PARAMS");
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(&p_data, arg,
+ sizeof(p_data))) {
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size = %zd\n",
+ __func__, "p_data", sizeof(p_data));
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (p_data.num_params > LSM_PARAMS_MAX) {
+ dev_err(rtd->dev,
+ "%s: %s: Invalid num_params %d\n",
+ __func__, "SET_MODULE_PARAMS",
+ p_data.num_params);
+ err = -EINVAL;
+ goto done;
+ }
+
+ p_size = p_data.num_params *
+ sizeof(struct lsm_params_info);
+
+ if (p_data.data_size != p_size) {
+ dev_err(rtd->dev,
+ "%s: %s: Invalid size %zd\n",
+ __func__, "SET_MODULE_PARAMS", p_size);
+
+ err = -EFAULT;
+ goto done;
+ }
+
+ params = kzalloc(p_size, GFP_KERNEL);
+ if (!params) {
+ dev_err(rtd->dev,
+ "%s: no memory for params\n",
+ __func__);
+ err = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(params, p_data.params,
+ p_data.data_size)) {
+ dev_err(rtd->dev,
+ "%s: %s: copy_from_user failed, size = %d\n",
+ __func__, "params", p_data.data_size);
+ kfree(params);
+ err = -EFAULT;
+ goto done;
+ }
+
+ err = msm_lsm_process_params(substream, &p_data, params);
+ if (err)
+ dev_err(rtd->dev,
+ "%s: %s: Failed to set params, err = %d\n",
+ __func__, "SET_MODULE_PARAMS", err);
+ kfree(params);
+ break;
+ }
+
+ case SNDRV_LSM_EVENT_STATUS: {
+ struct snd_lsm_event_status *user = NULL, userarg;
+ dev_dbg(rtd->dev,
+ "%s: SNDRV_LSM_EVENT_STATUS\n", __func__);
+ if (copy_from_user(&userarg, arg, sizeof(userarg))) {
+ dev_err(rtd->dev,
+ "%s: err copyuser event_status\n",
+ __func__);
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (userarg.payload_size >
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+ pr_err("%s: payload_size %d is invalid, max allowed = %d\n",
+ __func__, userarg.payload_size,
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+ err = -EINVAL;
+ goto done;
+ }
+
+ size = sizeof(struct snd_lsm_event_status) +
+ userarg.payload_size;
+ user = kzalloc(size, GFP_KERNEL);
+ if (!user) {
+ dev_err(rtd->dev,
+ "%s: Allocation failed event status size %d\n",
+ __func__, size);
+ err = -EFAULT;
+ goto done;
+ } else {
+ user->payload_size = userarg.payload_size;
+ err = msm_lsm_ioctl_shared(substream, cmd, user);
+ }
+ /* Update size with actual payload size */
+ size = sizeof(*user) + user->payload_size;
+ if (!err && !access_ok(VERIFY_WRITE, arg, size)) {
+ dev_err(rtd->dev,
+ "%s: write verify failed size %d\n",
+ __func__, size);
+ err = -EFAULT;
+ }
+ if (!err && (copy_to_user(arg, user, size))) {
+ dev_err(rtd->dev,
+ "%s: failed to copy payload %d",
+ __func__, size);
+ err = -EFAULT;
+ }
+ kfree(user);
+ if (err)
+ dev_err(rtd->dev,
+ "%s: lsmevent failed %d", __func__, err);
+ goto done;
+ }
+
+ case SNDRV_LSM_EVENT_STATUS_V3: {
+ struct snd_lsm_event_status_v3 *user = NULL;
+ struct snd_lsm_event_status_v3 userarg;
+
+ dev_dbg(rtd->dev,
+ "%s: SNDRV_LSM_EVENT_STATUS_V3\n", __func__);
+ if (!arg) {
+ dev_err(rtd->dev,
+ "%s: Invalid params event_status_v3\n",
+ __func__);
+ err = -EINVAL;
+ goto done;
+ }
+ if (copy_from_user(&userarg, arg, sizeof(userarg))) {
+ dev_err(rtd->dev,
+ "%s: err copyuser event_status_v3\n",
+ __func__);
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (userarg.payload_size >
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+ pr_err("%s: payload_size %d is invalid, max allowed = %d\n",
+ __func__, userarg.payload_size,
+ LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+ err = -EINVAL;
+ goto done;
+ }
+
+ size = sizeof(struct snd_lsm_event_status_v3) +
+ userarg.payload_size;
+ user = kzalloc(size, GFP_KERNEL);
+ if (!user) {
+ dev_err(rtd->dev,
+ "%s: Allocation failed event status size %d\n",
+ __func__, size);
+ err = -EFAULT;
+ goto done;
+ }
+ user->payload_size = userarg.payload_size;
+ err = msm_lsm_ioctl_shared(substream, cmd, user);
+
+ /* Update size with actual payload size */
+ size = sizeof(*user) + user->payload_size;
+ if (!err && !access_ok(VERIFY_WRITE, arg, size)) {
+ dev_err(rtd->dev,
+ "%s: write verify failed size %d\n",
+ __func__, size);
+ err = -EFAULT;
+ }
+ if (!err && (copy_to_user(arg, user, size))) {
+ dev_err(rtd->dev,
+ "%s: failed to copy payload %d",
+ __func__, size);
+ err = -EFAULT;
+ }
+ kfree(user);
+ if (err)
+ dev_err(rtd->dev,
+ "%s: lsm_event_v3 failed %d", __func__, err);
+ break;
+ }
+
+ default:
+ err = msm_lsm_ioctl_shared(substream, cmd, arg);
+ break;
+ }
+done:
+ mutex_unlock(&prtd->lsm_api_lock);
+ return err;
+}
+
+static int msm_lsm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct lsm_priv), GFP_KERNEL);
+ if (!prtd) {
+ pr_err("%s: Failed to allocate memory for lsm_priv\n",
+ __func__);
+ return -ENOMEM;
+ }
+ mutex_init(&prtd->lsm_api_lock);
+ spin_lock_init(&prtd->event_lock);
+ init_waitqueue_head(&prtd->event_wait);
+ init_waitqueue_head(&prtd->period_wait);
+ prtd->substream = substream;
+ runtime->private_data = prtd;
+ runtime->hw = msm_pcm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_info("%s: snd_pcm_hw_constraint_list failed ret %d\n",
+ __func__, ret);
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_info("%s: snd_pcm_hw_constraint_integer failed ret %d\n",
+ __func__, ret);
+
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ CAPTURE_MIN_NUM_PERIODS * CAPTURE_MIN_PERIOD_SIZE,
+ CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE);
+ if (ret < 0)
+ pr_info("%s: constraint for buffer bytes min max ret = %d\n",
+ __func__, ret);
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ if (ret < 0) {
+ pr_info("%s: constraint for period bytes step ret = %d\n",
+ __func__, ret);
+ }
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+ if (ret < 0)
+ pr_info("%s: constraint for buffer bytes step ret = %d\n",
+ __func__, ret);
+ prtd->lsm_client = q6lsm_client_alloc(
+ (lsm_app_cb)lsm_event_handler, prtd);
+ if (!prtd->lsm_client) {
+ pr_err("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ runtime->private_data = NULL;
+ return -ENOMEM;
+ }
+ prtd->lsm_client->opened = false;
+ prtd->lsm_client->session_state = IDLE;
+ prtd->lsm_client->poll_enable = true;
+ prtd->lsm_client->perf_mode = 0;
+ prtd->lsm_client->event_mode = LSM_EVENT_NON_TIME_STAMP_MODE;
+
+ return 0;
+}
+
+static int msm_lsm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret = 0;
+
+ if (!substream->private_data) {
+ pr_err("%s: Invalid private_data", __func__);
+ return -EINVAL;
+ }
+
+ rtd = prtd->substream->private_data;
+
+ if (!prtd->lsm_client) {
+ dev_err(rtd->dev,
+ "%s: LSM client data ptr is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (q6lsm_set_media_fmt_params(prtd->lsm_client))
+ dev_dbg(rtd->dev,
+ "%s: failed to set lsm media fmt params\n", __func__);
+
+ if (prtd->lsm_client->session_state == IDLE) {
+ ret = msm_pcm_routing_reg_phy_compr_stream(
+ rtd->dai_link->be_id,
+ prtd->lsm_client->perf_mode,
+ prtd->lsm_client->session,
+ SNDRV_PCM_STREAM_CAPTURE,
+ LISTEN);
+ if (ret) {
+ dev_err(rtd->dev,
+ "%s: register phy compr stream failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ prtd->lsm_client->session_state = RUNNING;
+ prtd->lsm_client->started = false;
+ runtime->private_data = prtd;
+ return ret;
+}
+
+static int msm_lsm_close(struct snd_pcm_substream *substream)
+{
+ unsigned long flags;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret = 0;
+
+ if (!substream->private_data) {
+ pr_err("%s: Invalid private_data", __func__);
+ return -EINVAL;
+ }
+ if (!prtd || !prtd->lsm_client) {
+ pr_err("%s: No LSM session active\n", __func__);
+ return -EINVAL;
+ }
+ rtd = substream->private_data;
+
+ dev_dbg(rtd->dev, "%s\n", __func__);
+ if (prtd->lsm_client->started) {
+ ret = q6lsm_stop(prtd->lsm_client, true);
+ if (ret)
+ dev_err(rtd->dev,
+ "%s: session stop failed, err = %d\n",
+ __func__, ret);
+ else
+ dev_dbg(rtd->dev,
+ "%s: LSM client session stopped %d\n",
+ __func__, ret);
+
+ /*
+ * Go Ahead and try de-register sound model,
+ * even if stop failed
+ */
+ prtd->lsm_client->started = false;
+
+ ret = q6lsm_deregister_sound_model(prtd->lsm_client);
+ if (ret)
+ dev_err(rtd->dev,
+ "%s: dereg_snd_model failed, err = %d\n",
+ __func__, ret);
+ else
+ dev_dbg(rtd->dev, "%s: dereg_snd_model succesful\n",
+ __func__);
+ }
+
+ msm_pcm_routing_dereg_phy_stream(rtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+
+ if (prtd->lsm_client->opened) {
+ q6lsm_close(prtd->lsm_client);
+ prtd->lsm_client->opened = false;
+ }
+ q6lsm_client_free(prtd->lsm_client);
+
+ spin_lock_irqsave(&prtd->event_lock, flags);
+ kfree(prtd->event_status);
+ prtd->event_status = NULL;
+ spin_unlock_irqrestore(&prtd->event_lock, flags);
+ mutex_destroy(&prtd->lsm_api_lock);
+ kfree(prtd);
+ runtime->private_data = NULL;
+
+ return 0;
+}
+
+static int msm_lsm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct lsm_hw_params *hw_params = NULL;
+ struct snd_soc_pcm_runtime *rtd;
+
+ if (!substream->private_data) {
+ pr_err("%s: Invalid private_data", __func__);
+ return -EINVAL;
+ }
+ rtd = substream->private_data;
+
+ if (!prtd || !params) {
+ dev_err(rtd->dev,
+ "%s: invalid params prtd %pK params %pK",
+ __func__, prtd, params);
+ return -EINVAL;
+ }
+ hw_params = &prtd->lsm_client->hw_params;
+ hw_params->num_chs = params_channels(params);
+ hw_params->period_count = params_periods(params);
+ hw_params->sample_rate = params_rate(params);
+ if (((hw_params->sample_rate != 16000) &&
+ (hw_params->sample_rate != 48000)) ||
+ (hw_params->period_count == 0)) {
+ dev_err(rtd->dev,
+ "%s: Invalid Params sample rate %d period count %d\n",
+ __func__, hw_params->sample_rate,
+ hw_params->period_count);
+ return -EINVAL;
+ }
+
+ if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) {
+ hw_params->sample_size = 16;
+ } else if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE) {
+ hw_params->sample_size = 24;
+ } else {
+ dev_err(rtd->dev, "%s: Invalid Format 0x%x\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ hw_params->buf_sz = params_buffer_bytes(params) /
+ hw_params->period_count;
+ dev_dbg(rtd->dev,
+ "%s: channels %d sample rate %d sample size %d buffer size %d period count %d\n",
+ __func__, hw_params->num_chs, hw_params->sample_rate,
+ hw_params->sample_size, hw_params->buf_sz,
+ hw_params->period_count);
+ return 0;
+}
+
+static snd_pcm_uframes_t msm_lsm_pcm_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd;
+
+ if (!substream->private_data) {
+ pr_err("%s: Invalid private_data", __func__);
+ return -EINVAL;
+ }
+ rtd = substream->private_data;
+
+ if (!prtd) {
+ dev_err(rtd->dev,
+ "%s: Invalid param %pK\n", __func__, prtd);
+ return 0;
+ }
+
+ if (prtd->dma_write >= snd_pcm_lib_buffer_bytes(substream))
+ prtd->dma_write = 0;
+ dev_dbg(rtd->dev,
+ "%s: dma post = %d\n", __func__, prtd->dma_write);
+ return bytes_to_frames(runtime, prtd->dma_write);
+}
+
+static int msm_lsm_pcm_copy(struct snd_pcm_substream *substream, int ch,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ char *pcm_buf = NULL;
+ int fbytes = 0, rc = 0;
+ struct snd_soc_pcm_runtime *rtd;
+
+ if (!substream->private_data) {
+ pr_err("%s: Invalid private_data", __func__);
+ return -EINVAL;
+ }
+ rtd = substream->private_data;
+
+ if (!prtd) {
+ dev_err(rtd->dev,
+ "%s: Invalid param %pK\n", __func__, prtd);
+ return -EINVAL;
+ }
+
+ fbytes = frames_to_bytes(runtime, frames);
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->status->state == SNDRV_PCM_STATE_PREPARED) {
+ dev_err(rtd->dev,
+ "%s: runtime state incorrect %d", __func__,
+ runtime->status->state);
+ return 0;
+ }
+ rc = wait_event_timeout(prtd->period_wait,
+ (atomic_read(&prtd->buf_count) |
+ atomic_read(&prtd->read_abort)), (2 * HZ));
+ if (!rc) {
+ dev_err(rtd->dev,
+ "%s: timeout for read retry\n", __func__);
+ return -EAGAIN;
+ }
+ if (atomic_read(&prtd->read_abort)) {
+ dev_err(rtd->dev,
+ "%s: Read abort recieved\n", __func__);
+ return -EIO;
+ }
+ prtd->appl_cnt = prtd->appl_cnt %
+ prtd->lsm_client->hw_params.period_count;
+ pcm_buf = prtd->lsm_client->lab_buffer[prtd->appl_cnt].data;
+ dev_dbg(rtd->dev,
+ "%s: copy the pcm data size %d\n",
+ __func__, fbytes);
+ if (pcm_buf) {
+ if (copy_to_user(buf, pcm_buf, fbytes)) {
+ dev_err(rtd->dev,
+ "%s: failed to copy bytes %d\n",
+ __func__, fbytes);
+ return -EINVAL;
+ }
+ } else {
+ dev_err(rtd->dev,
+ "%s: Invalid pcm buffer\n", __func__);
+ return -EINVAL;
+ }
+ prtd->appl_cnt = (prtd->appl_cnt + 1) %
+ prtd->lsm_client->hw_params.period_count;
+ atomic_dec(&prtd->buf_count);
+ return 0;
+}
+
+static int msm_lsm_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = ucontrol->value.integer.value[3];
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ cfg_data.app_type = ucontrol->value.integer.value[0];
+ cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
+ cfg_data.sample_rate = ucontrol->value.integer.value[2];
+
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, &cfg_data);
+ if (ret < 0)
+ pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+
+ return 0;
+}
+
+static int msm_lsm_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = 0;
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ &be_id, &cfg_data);
+ if (ret < 0) {
+ pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[0] = cfg_data.app_type;
+ ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
+ ucontrol->value.integer.value[2] = cfg_data.sample_rate;
+ ucontrol->value.integer.value[3] = be_id;
+ pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+done:
+ return ret;
+}
+
+static int msm_lsm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_pcm_usr *app_type_info;
+ struct snd_kcontrol *kctl;
+ const char *mixer_ctl_name = "Listen Stream";
+ const char *deviceNo = "NN";
+ const char *suffix = "App Type Cfg";
+ int ctl_len, ret = 0;
+
+ ctl_len = strlen(mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ pr_debug("%s: Listen app type cntrl add\n", __func__);
+ ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ NULL, 1, ctl_len, rtd->dai_link->be_id,
+ &app_type_info);
+ if (ret < 0) {
+ pr_err("%s: Listen app type cntrl add failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ kctl = app_type_info->kctl;
+ snprintf(kctl->id.name, ctl_len, "%s %d %s",
+ mixer_ctl_name, rtd->pcm->device, suffix);
+ kctl->put = msm_lsm_app_type_cfg_ctl_put;
+ kctl->get = msm_lsm_app_type_cfg_ctl_get;
+ return 0;
+}
+
+static int msm_lsm_add_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+
+ ret = msm_lsm_add_app_type_controls(rtd);
+ if (ret)
+ pr_err("%s, add app type controls failed:%d\n", __func__, ret);
+
+ return ret;
+}
+
+static struct snd_pcm_ops msm_lsm_ops = {
+ .open = msm_lsm_open,
+ .close = msm_lsm_close,
+ .ioctl = msm_lsm_ioctl,
+ .prepare = msm_lsm_prepare,
+ .compat_ioctl = msm_lsm_ioctl_compat,
+ .hw_params = msm_lsm_hw_params,
+ .copy = msm_lsm_pcm_copy,
+ .pointer = msm_lsm_pcm_pointer,
+};
+
+static int msm_asoc_lsm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = msm_lsm_add_controls(rtd);
+ if (ret)
+ pr_err("%s, kctl add failed:%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int msm_asoc_lsm_probe(struct snd_soc_platform *platform)
+{
+ pr_debug("enter %s\n", __func__);
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_lsm_ops,
+ .pcm_new = msm_asoc_lsm_new,
+ .probe = msm_asoc_lsm_probe,
+};
+
+static int msm_lsm_probe(struct platform_device *pdev)
+{
+
+ return snd_soc_register_platform(&pdev->dev, &msm_soc_platform);
+}
+
+static int msm_lsm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id msm_lsm_client_dt_match[] = {
+ {.compatible = "qcom,msm-lsm-client" },
+ { }
+};
+
+static struct platform_driver msm_lsm_driver = {
+ .driver = {
+ .name = "msm-lsm-client",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(msm_lsm_client_dt_match),
+ },
+ .probe = msm_lsm_probe,
+ .remove = msm_lsm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_lsm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_lsm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("LSM client platform driver");
+MODULE_DEVICE_TABLE(of, msm_lsm_client_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
new file mode 100644
index 000000000000..b1a1ea54a73e
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
@@ -0,0 +1,924 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <asm/dma.h>
+#include "msm-pcm-afe-v2.h"
+
+#define MIN_PLAYBACK_PERIOD_SIZE (128 * 2)
+#define MAX_PLAYBACK_PERIOD_SIZE (128 * 2 * 2 * 6)
+#define MIN_PLAYBACK_NUM_PERIODS (4)
+#define MAX_PLAYBACK_NUM_PERIODS (384)
+
+#define MIN_CAPTURE_PERIOD_SIZE (128 * 2)
+#define MAX_CAPTURE_PERIOD_SIZE (192 * 2 * 2 * 8 * 4)
+#define MIN_CAPTURE_NUM_PERIODS (4)
+#define MAX_CAPTURE_NUM_PERIODS (384)
+
+static struct snd_pcm_hardware msm_afe_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE|
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = (SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 6,
+ .buffer_bytes_max = MAX_PLAYBACK_PERIOD_SIZE *
+ MAX_PLAYBACK_NUM_PERIODS,
+ .period_bytes_min = MIN_PLAYBACK_PERIOD_SIZE,
+ .period_bytes_max = MAX_PLAYBACK_PERIOD_SIZE,
+ .periods_min = MIN_PLAYBACK_NUM_PERIODS,
+ .periods_max = MAX_PLAYBACK_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware msm_afe_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE|
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = (SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 6,
+ .buffer_bytes_max = MAX_CAPTURE_PERIOD_SIZE *
+ MAX_CAPTURE_NUM_PERIODS,
+ .period_bytes_min = MIN_CAPTURE_PERIOD_SIZE,
+ .period_bytes_max = MAX_CAPTURE_PERIOD_SIZE,
+ .periods_min = MIN_CAPTURE_NUM_PERIODS,
+ .periods_max = MAX_CAPTURE_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+
+static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt);
+static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt);
+
+static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt)
+{
+ struct pcm_afe_info *prtd =
+ container_of(hrt, struct pcm_afe_info, hrt);
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u32 mem_map_handle = 0;
+
+ mem_map_handle = afe_req_mmap_handle(prtd->audio_client);
+ if (!mem_map_handle)
+ pr_err("%s: mem_map_handle is NULL\n", __func__);
+
+ if (prtd->start) {
+ pr_debug("sending frame to DSP: poll_time: %d\n",
+ prtd->poll_time);
+ if (prtd->dsp_cnt == runtime->periods)
+ prtd->dsp_cnt = 0;
+ pr_debug("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle);
+ afe_rt_proxy_port_write(
+ (prtd->dma_addr +
+ (prtd->dsp_cnt *
+ snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle,
+ snd_pcm_lib_period_bytes(prtd->substream));
+ prtd->dsp_cnt++;
+ hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
+ * 1000));
+
+ return HRTIMER_RESTART;
+ } else
+ return HRTIMER_NORESTART;
+}
+static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt)
+{
+ struct pcm_afe_info *prtd =
+ container_of(hrt, struct pcm_afe_info, hrt);
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u32 mem_map_handle = 0;
+ int ret;
+
+ mem_map_handle = afe_req_mmap_handle(prtd->audio_client);
+ if (!mem_map_handle)
+ pr_err("%s: mem_map_handle is NULL\n", __func__);
+
+ if (prtd->start) {
+ if (prtd->dsp_cnt == runtime->periods)
+ prtd->dsp_cnt = 0;
+ pr_debug("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle);
+ ret = afe_rt_proxy_port_read(
+ (prtd->dma_addr + (prtd->dsp_cnt
+ * snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle,
+ snd_pcm_lib_period_bytes(prtd->substream));
+ if (ret < 0) {
+ pr_err("%s: AFE port read fails: %d\n", __func__, ret);
+ prtd->start = 0;
+ return HRTIMER_NORESTART;
+ }
+ prtd->dsp_cnt++;
+ pr_debug("sending frame rec to DSP: poll_time: %d\n",
+ prtd->poll_time);
+ hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
+ * 1000));
+
+ return HRTIMER_RESTART;
+ } else
+ return HRTIMER_NORESTART;
+}
+static void pcm_afe_process_tx_pkt(uint32_t opcode,
+ uint32_t token, uint32_t *payload,
+ void *priv)
+{
+ struct pcm_afe_info *prtd = priv;
+ unsigned long dsp_flags;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_pcm_runtime *runtime = NULL;
+ uint16_t event;
+ uint64_t period_bytes;
+ uint64_t bytes_one_sec;
+
+ if (prtd == NULL)
+ return;
+ substream = prtd->substream;
+ runtime = substream->runtime;
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ switch (opcode) {
+ case AFE_EVENT_RT_PROXY_PORT_STATUS: {
+ event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
+ switch (event) {
+ case AFE_EVENT_RTPORT_START: {
+ prtd->dsp_cnt = 0;
+ /* Calculate poll time.
+ * Split steps to avoid overflow.
+ * Poll time-time corresponding to one period
+ * in bytes.
+ * (Samplerate * channelcount * format) =
+ * bytes in 1 sec.
+ * Poll time =
+ * (period bytes / bytes in one sec) *
+ * 1000000 micro seconds.
+ * Multiplication by 1000000 is done in two
+ * steps to keep the accuracy of poll time.
+ */
+ if (prtd->mmap_flag) {
+ period_bytes = ((uint64_t)(
+ (snd_pcm_lib_period_bytes(
+ prtd->substream)) *
+ 1000));
+ bytes_one_sec = (runtime->rate
+ * runtime->channels * 2);
+ bytes_one_sec =
+ div_u64(bytes_one_sec, 1000);
+ prtd->poll_time =
+ div_u64(period_bytes,
+ bytes_one_sec);
+ pr_debug("prtd->poll_time: %d",
+ prtd->poll_time);
+ }
+ break;
+ }
+ case AFE_EVENT_RTPORT_STOP:
+ pr_debug("%s: event!=0\n", __func__);
+ prtd->start = 0;
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ break;
+ case AFE_EVENT_RTPORT_LOW_WM:
+ pr_debug("%s: Underrun\n", __func__);
+ break;
+ case AFE_EVENT_RTPORT_HI_WM:
+ pr_debug("%s: Overrun\n", __func__);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2:
+ pr_debug("write done\n");
+ prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+ (prtd->substream);
+ snd_pcm_period_elapsed(prtd->substream);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case RESET_EVENTS:
+ prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+ (prtd->substream);
+ prtd->reset_event = true;
+ snd_pcm_period_elapsed(prtd->substream);
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+}
+
+static void pcm_afe_process_rx_pkt(uint32_t opcode,
+ uint32_t token, uint32_t *payload,
+ void *priv)
+{
+ struct pcm_afe_info *prtd = priv;
+ unsigned long dsp_flags;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_pcm_runtime *runtime = NULL;
+ uint16_t event;
+ uint64_t period_bytes;
+ uint64_t bytes_one_sec;
+ uint32_t mem_map_handle = 0;
+
+ if (prtd == NULL)
+ return;
+ substream = prtd->substream;
+ runtime = substream->runtime;
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ switch (opcode) {
+ case AFE_EVENT_RT_PROXY_PORT_STATUS: {
+ event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
+ switch (event) {
+ case AFE_EVENT_RTPORT_START: {
+ prtd->dsp_cnt = 0;
+ /* Calculate poll time. Split steps to avoid overflow.
+ * Poll time-time corresponding to one period in bytes.
+ * (Samplerate * channelcount * format)=bytes in 1 sec.
+ * Poll time = (period bytes / bytes in one sec) *
+ * 1000000 micro seconds.
+ * Multiplication by 1000000 is done in two steps to
+ * keep the accuracy of poll time.
+ */
+ if (prtd->mmap_flag) {
+ period_bytes = ((uint64_t)(
+ (snd_pcm_lib_period_bytes(
+ prtd->substream)) * 1000));
+ bytes_one_sec = (runtime->rate *
+ runtime->channels * 2);
+ bytes_one_sec = div_u64(bytes_one_sec , 1000);
+ prtd->poll_time =
+ div_u64(period_bytes, bytes_one_sec);
+ pr_debug("prtd->poll_time : %d\n",
+ prtd->poll_time);
+ } else {
+ mem_map_handle =
+ afe_req_mmap_handle(prtd->audio_client);
+ if (!mem_map_handle)
+ pr_err("%s:mem_map_handle is NULL\n",
+ __func__);
+ /* Do initial read to start transfer */
+ afe_rt_proxy_port_read((prtd->dma_addr +
+ (prtd->dsp_cnt *
+ snd_pcm_lib_period_bytes(
+ prtd->substream))),
+ mem_map_handle,
+ snd_pcm_lib_period_bytes(
+ prtd->substream));
+ prtd->dsp_cnt++;
+ }
+ break;
+ }
+ case AFE_EVENT_RTPORT_STOP:
+ pr_debug("%s: event!=0\n", __func__);
+ prtd->start = 0;
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ break;
+ case AFE_EVENT_RTPORT_LOW_WM:
+ pr_debug("%s: Underrun\n", __func__);
+ break;
+ case AFE_EVENT_RTPORT_HI_WM:
+ pr_debug("%s: Overrun\n", __func__);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2:
+ pr_debug("%s :Read done\n", __func__);
+ prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+ (prtd->substream);
+ if (!prtd->mmap_flag) {
+ atomic_set(&prtd->rec_bytes_avail, 1);
+ wake_up(&prtd->read_wait);
+ }
+ snd_pcm_period_elapsed(prtd->substream);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case RESET_EVENTS:
+ prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+ (prtd->substream);
+ prtd->reset_event = true;
+ if (!prtd->mmap_flag) {
+ atomic_set(&prtd->rec_bytes_avail, 1);
+ wake_up(&prtd->read_wait);
+ }
+ snd_pcm_period_elapsed(prtd->substream);
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+}
+
+static int msm_afe_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s: sample_rate=%d\n", __func__, runtime->rate);
+
+ pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+ ret = afe_register_get_events(dai->id,
+ pcm_afe_process_tx_pkt, prtd);
+ if (ret < 0) {
+ pr_err("afe-pcm:register for events failed\n");
+ return ret;
+ }
+ pr_debug("%s:success\n", __func__);
+ prtd->prepared++;
+ return ret;
+}
+
+static int msm_afe_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+ ret = afe_register_get_events(dai->id,
+ pcm_afe_process_rx_pkt, prtd);
+ if (ret < 0) {
+ pr_err("afe-pcm:register for events failed\n");
+ return ret;
+ }
+ pr_debug("%s:success\n", __func__);
+ prtd->prepared++;
+ return 0;
+}
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 16000, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static int msm_afe_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = NULL;
+ int ret = 0;
+
+ prtd = kzalloc(sizeof(struct pcm_afe_info), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ } else
+ pr_debug("prtd %pK\n", prtd);
+
+ mutex_init(&prtd->lock);
+ spin_lock_init(&prtd->dsp_lock);
+ prtd->dsp_cnt = 0;
+
+ mutex_lock(&prtd->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = msm_afe_hardware_playback;
+ else
+ runtime->hw = msm_afe_hardware_capture;
+
+ prtd->substream = substream;
+ runtime->private_data = prtd;
+ prtd->audio_client = q6afe_audio_client_alloc(prtd);
+ if (!prtd->audio_client) {
+ pr_debug("%s: Could not allocate memory\n", __func__);
+ mutex_unlock(&prtd->lock);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+
+ atomic_set(&prtd->rec_bytes_avail, 0);
+ init_waitqueue_head(&prtd->read_wait);
+
+ hrtimer_init(&prtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->hrt.function = afe_hrtimer_callback;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->hrt.function = afe_hrtimer_rec_callback;
+
+ mutex_unlock(&prtd->lock);
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_integer failed\n");
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ MIN_CAPTURE_NUM_PERIODS * MIN_CAPTURE_PERIOD_SIZE,
+ MAX_CAPTURE_NUM_PERIODS * MAX_CAPTURE_PERIOD_SIZE);
+
+ if (ret < 0) {
+ pr_err("constraint for buffer bytes min max ret = %d\n",
+ ret);
+ }
+ }
+
+ prtd->reset_event = false;
+ return 0;
+}
+
+static int msm_afe_playback_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff,
+ void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+ u32 mem_map_handle = 0;
+
+ pr_debug("%s : appl_ptr 0x%lx hw_ptr 0x%lx dest_to_copy 0x%pK\n",
+ __func__,
+ runtime->control->appl_ptr, runtime->status->hw_ptr, hwbuf);
+
+ if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) {
+ pr_err("%s :Failed to copy audio from user buffer\n",
+ __func__);
+
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ if (!prtd->mmap_flag) {
+ mem_map_handle = afe_req_mmap_handle(prtd->audio_client);
+ if (!mem_map_handle) {
+ pr_err("%s: mem_map_handle is NULL\n", __func__);
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ pr_debug("%s : prtd-> dma_addr 0x%lx dsp_cnt %d\n", __func__,
+ prtd->dma_addr, prtd->dsp_cnt);
+
+ if (prtd->dsp_cnt == runtime->periods)
+ prtd->dsp_cnt = 0;
+
+ ret = afe_rt_proxy_port_write(
+ (prtd->dma_addr + (prtd->dsp_cnt *
+ snd_pcm_lib_period_bytes(prtd->substream))),
+ mem_map_handle,
+ snd_pcm_lib_period_bytes(prtd->substream));
+
+ if (ret) {
+ pr_err("%s: AFE proxy port write failed %d\n",
+ __func__, ret);
+ goto fail;
+ }
+ prtd->dsp_cnt++;
+ }
+fail:
+ return ret;
+}
+
+static int msm_afe_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff,
+ void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+ u32 mem_map_handle = 0;
+
+ if (!prtd->mmap_flag) {
+ mem_map_handle = afe_req_mmap_handle(prtd->audio_client);
+
+ if (!mem_map_handle) {
+ pr_err("%s: mem_map_handle is NULL\n", __func__);
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ if (prtd->dsp_cnt == runtime->periods)
+ prtd->dsp_cnt = 0;
+
+ ret = afe_rt_proxy_port_read((prtd->dma_addr +
+ (prtd->dsp_cnt *
+ snd_pcm_lib_period_bytes(prtd->substream))),
+ mem_map_handle,
+ snd_pcm_lib_period_bytes(prtd->substream));
+
+ if (ret) {
+ pr_err("%s: AFE proxy port read failed %d\n",
+ __func__, ret);
+ goto fail;
+ }
+
+ prtd->dsp_cnt++;
+ ret = wait_event_timeout(prtd->read_wait,
+ atomic_read(&prtd->rec_bytes_avail), 5 * HZ);
+ if (ret < 0) {
+ pr_err("%s: wait_event_timeout failed\n", __func__);
+
+ ret = -ETIMEDOUT;
+ goto fail;
+ }
+ atomic_set(&prtd->rec_bytes_avail, 0);
+ }
+ pr_debug("%s:appl_ptr 0x%lx hw_ptr 0x%lx src_to_copy 0x%pK\n",
+ __func__, runtime->control->appl_ptr,
+ runtime->status->hw_ptr, hwbuf);
+
+ if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) {
+ pr_err("%s: copy to user failed\n", __func__);
+
+ goto fail;
+ ret = -EFAULT;
+ }
+
+fail:
+ return ret;
+}
+
+static int msm_afe_copy(struct snd_pcm_substream *substream, int channel,
+ snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ int ret = 0;
+
+ if (prtd->reset_event) {
+ pr_debug("%s: reset events received from ADSP, return error\n",
+ __func__);
+ return -ENETRESET;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_afe_playback_copy(substream, channel, hwoff,
+ buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_afe_capture_copy(substream, channel, hwoff,
+ buf, frames);
+ return ret;
+}
+
+static int msm_afe_close(struct snd_pcm_substream *substream)
+{
+ int rc = 0;
+ struct snd_dma_buffer *dma_buf;
+ struct snd_pcm_runtime *runtime;
+ struct pcm_afe_info *prtd;
+ struct snd_soc_pcm_runtime *rtd = NULL;
+ struct snd_soc_dai *dai = NULL;
+ int dir = IN;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ if (substream == NULL) {
+ pr_err("substream is NULL\n");
+ return -EINVAL;
+ }
+ rtd = substream->private_data;
+ dai = rtd->cpu_dai;
+ runtime = substream->runtime;
+ prtd = runtime->private_data;
+
+ mutex_lock(&prtd->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dir = IN;
+ ret = afe_unregister_get_events(dai->id);
+ if (ret < 0)
+ pr_err("AFE unregister for events failed\n");
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ dir = OUT;
+ ret = afe_unregister_get_events(dai->id);
+ if (ret < 0)
+ pr_err("AFE unregister for events failed\n");
+ }
+ if (prtd->mmap_flag)
+ hrtimer_cancel(&prtd->hrt);
+
+ rc = afe_cmd_memory_unmap(afe_req_mmap_handle(prtd->audio_client));
+ if (rc < 0)
+ pr_err("AFE memory unmap failed\n");
+
+ pr_debug("release all buffer\n");
+ dma_buf = &substream->dma_buffer;
+ if (dma_buf == NULL) {
+ pr_debug("dma_buf is NULL\n");
+ goto done;
+ }
+
+ if (dma_buf->area)
+ dma_buf->area = NULL;
+ q6afe_audio_client_buf_free_contiguous(dir, prtd->audio_client);
+done:
+ pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+ q6afe_audio_client_free(prtd->audio_client);
+ mutex_unlock(&prtd->lock);
+ prtd->prepared--;
+ kfree(prtd);
+ runtime->private_data = NULL;
+ return 0;
+}
+static int msm_afe_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ prtd->pcm_irq_pos = 0;
+ if (prtd->prepared)
+ return 0;
+ mutex_lock(&prtd->lock);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_afe_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_afe_capture_prepare(substream);
+ mutex_unlock(&prtd->lock);
+ return ret;
+}
+static int msm_afe_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ struct afe_audio_client *ac = prtd->audio_client;
+ struct afe_audio_port_data *apd = ac->port;
+ struct afe_audio_buffer *ab;
+ int dir = -1;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+ ab = &(apd[dir].buf[0]);
+
+ return msm_audio_ion_mmap((struct audio_buffer *)ab, vma);
+}
+static int msm_afe_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__);
+ prtd->start = 1;
+ if (prtd->mmap_flag)
+ hrtimer_start(&prtd->hrt, ns_to_ktime(0),
+ HRTIMER_MODE_REL);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
+ prtd->start = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+static int msm_afe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ struct afe_audio_buffer *buf;
+ int dir, rc;
+
+ pr_debug("%s:\n", __func__);
+
+ mutex_lock(&prtd->lock);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+
+ rc = q6afe_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ (params_buffer_bytes(params) / params_periods(params)),
+ params_periods(params));
+ pr_debug("params_buffer_bytes(params) = %d\n",
+ (params_buffer_bytes(params)));
+ pr_debug("params_periods(params) = %d\n",
+ (params_periods(params)));
+ pr_debug("params_periodsize(params) = %d\n",
+ (params_buffer_bytes(params) / params_periods(params)));
+
+ if (rc < 0) {
+ pr_err("Audio Start: Buffer Allocation failed rc = %d\n", rc);
+ mutex_unlock(&prtd->lock);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+
+ if (buf == NULL || buf[0].data == NULL) {
+ mutex_unlock(&prtd->lock);
+ return -ENOMEM;
+ }
+
+ pr_debug("%s:buf = %pK\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+
+ dma_buf->bytes = params_buffer_bytes(params);
+
+ if (!dma_buf->area) {
+ pr_err("%s:MSM AFE physical memory allocation failed\n",
+ __func__);
+ mutex_unlock(&prtd->lock);
+ return -ENOMEM;
+ }
+
+ memset(dma_buf->area, 0, params_buffer_bytes(params));
+
+ prtd->dma_addr = (phys_addr_t) dma_buf->addr;
+
+ mutex_unlock(&prtd->lock);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ rc = afe_memory_map(dma_buf->addr, dma_buf->bytes, prtd->audio_client);
+ if (rc < 0)
+ pr_err("fail to map memory to DSP\n");
+
+ return rc;
+}
+static snd_pcm_uframes_t msm_afe_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= snd_pcm_lib_buffer_bytes(substream))
+ prtd->pcm_irq_pos = 0;
+
+ if (prtd->reset_event) {
+ pr_debug("%s: reset events received from ADSP, return XRUN\n",
+ __func__);
+ return SNDRV_PCM_POS_XRUN;
+ }
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static struct snd_pcm_ops msm_afe_ops = {
+ .open = msm_afe_open,
+ .copy = msm_afe_copy,
+ .hw_params = msm_afe_hw_params,
+ .trigger = msm_afe_trigger,
+ .close = msm_afe_close,
+ .prepare = msm_afe_prepare,
+ .mmap = msm_afe_mmap,
+ .pointer = msm_afe_pointer,
+};
+
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static int msm_afe_afe_probe(struct snd_soc_platform *platform)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_afe_ops,
+ .pcm_new = msm_asoc_pcm_new,
+ .probe = msm_afe_afe_probe,
+};
+
+static int msm_afe_probe(struct platform_device *pdev)
+{
+
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_afe_remove(struct platform_device *pdev)
+{
+ pr_debug("%s\n", __func__);
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+static const struct of_device_id msm_pcm_afe_dt_match[] = {
+ {.compatible = "qcom,msm-pcm-afe"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_pcm_afe_dt_match);
+
+static struct platform_driver msm_afe_driver = {
+ .driver = {
+ .name = "msm-pcm-afe",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_pcm_afe_dt_match,
+ },
+ .probe = msm_afe_probe,
+ .remove = msm_afe_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ pr_debug("%s\n", __func__);
+ return platform_driver_register(&msm_afe_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ pr_debug("%s\n", __func__);
+ platform_driver_unregister(&msm_afe_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("AFE PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h
new file mode 100644
index 000000000000..84a4c3c6c275
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2012,2015-2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_PCM_AFE_H
+#define _MSM_PCM_AFE_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+
+
+struct pcm_afe_info {
+ unsigned long dma_addr;
+ struct snd_pcm_substream *substream;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ struct mutex lock;
+ spinlock_t dsp_lock;
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint8_t start;
+ uint32_t dsp_cnt;
+ uint32_t buf_phys;
+ int32_t mmap_flag;
+ int prepared;
+ struct hrtimer hrt;
+ int poll_time;
+ struct afe_audio_client *audio_client;
+ wait_queue_head_t read_wait;
+ atomic_t rec_bytes_avail;
+ bool reset_event;
+};
+
+
+#define MSM_EXT(xname, fp_info, fp_get, fp_put, addr) \
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .name = xname, \
+ .info = fp_info,\
+ .get = fp_get, .put = fp_put, \
+ .private_value = addr, \
+ }
+
+#endif /*_MSM_PCM_AFE_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c
new file mode 100644
index 000000000000..abbcfed39152
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c
@@ -0,0 +1,596 @@
+/* Copyright (c) 2013-2014, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/q6afe-v2.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+#include "q6voice.h"
+
+enum {
+ DTMF_IN_RX,
+ DTMF_IN_TX,
+};
+
+enum format {
+ FORMAT_S16_LE = 2
+};
+
+struct dtmf_det_info {
+ char session[MAX_SESSION_NAME_LEN];
+ uint8_t dir;
+ uint16_t high_freq;
+ uint16_t low_freq;
+};
+
+struct dtmf_buf_node {
+ struct list_head list;
+ struct dtmf_det_info dtmf_det_pkt;
+};
+
+enum dtmf_state {
+ DTMF_GEN_RX_STOPPED,
+ DTMF_GEN_RX_STARTED,
+};
+
+#define DTMF_MAX_Q_LEN 10
+#define DTMF_PKT_SIZE sizeof(struct dtmf_det_info)
+
+struct dtmf_drv_info {
+ enum dtmf_state state;
+ struct snd_pcm_substream *capture_substream;
+
+ struct list_head out_queue;
+ struct list_head free_out_queue;
+
+ wait_queue_head_t out_wait;
+
+ struct mutex lock;
+ spinlock_t dsp_lock;
+
+ uint8_t capture_start;
+ uint8_t capture_instance;
+
+ unsigned int pcm_capture_size;
+ unsigned int pcm_capture_count;
+ unsigned int pcm_capture_irq_pos;
+ unsigned int pcm_capture_buf_pos;
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = (sizeof(struct dtmf_buf_node) * DTMF_MAX_Q_LEN),
+ .period_bytes_min = DTMF_PKT_SIZE,
+ .period_bytes_max = DTMF_PKT_SIZE,
+ .periods_min = DTMF_MAX_Q_LEN,
+ .periods_max = DTMF_MAX_Q_LEN,
+ .fifo_size = 0,
+};
+
+static int msm_dtmf_rx_generate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint16_t low_freq = ucontrol->value.integer.value[0];
+ uint16_t high_freq = ucontrol->value.integer.value[1];
+ int16_t duration = ucontrol->value.integer.value[2];
+ uint16_t gain = ucontrol->value.integer.value[3];
+
+ pr_debug("%s: low_freq=%d high_freq=%d duration=%d gain=%d\n",
+ __func__, low_freq, high_freq, (int)duration, gain);
+ afe_dtmf_generate_rx((int64_t) duration, high_freq, low_freq, gain);
+ return 0;
+}
+
+static int msm_dtmf_rx_generate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s:\n", __func__);
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_dtmf_detect_voice_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int enable = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: enable=%d\n", __func__, enable);
+ voc_enable_dtmf_rx_detection(voc_get_session_id(VOICE_SESSION_NAME),
+ enable);
+
+ return 0;
+}
+
+static int msm_dtmf_detect_voice_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_dtmf_detect_volte_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int enable = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: enable=%d\n", __func__, enable);
+ voc_enable_dtmf_rx_detection(voc_get_session_id(VOLTE_SESSION_NAME),
+ enable);
+
+ return 0;
+}
+
+static int msm_dtmf_detect_volte_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static struct snd_kcontrol_new msm_dtmf_controls[] = {
+ SOC_SINGLE_MULTI_EXT("DTMF_Generate Rx Low High Duration Gain",
+ SND_SOC_NOPM, 0, 65535, 0, 4,
+ msm_dtmf_rx_generate_get,
+ msm_dtmf_rx_generate_put),
+ SOC_SINGLE_EXT("DTMF_Detect Rx Voice enable", SND_SOC_NOPM, 0, 1, 0,
+ msm_dtmf_detect_voice_rx_get,
+ msm_dtmf_detect_voice_rx_put),
+ SOC_SINGLE_EXT("DTMF_Detect Rx VoLTE enable", SND_SOC_NOPM, 0, 1, 0,
+ msm_dtmf_detect_volte_rx_get,
+ msm_dtmf_detect_volte_rx_put),
+};
+
+static int msm_pcm_dtmf_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_add_platform_controls(platform, msm_dtmf_controls,
+ ARRAY_SIZE(msm_dtmf_controls));
+ return 0;
+}
+
+static void dtmf_rx_detected_cb(uint8_t *pkt,
+ char *session,
+ void *private_data)
+{
+ struct dtmf_buf_node *buf_node = NULL;
+ struct vss_istream_evt_rx_dtmf_detected *dtmf_det_pkt =
+ (struct vss_istream_evt_rx_dtmf_detected *)pkt;
+ struct dtmf_drv_info *prtd = private_data;
+ unsigned long dsp_flags;
+
+ pr_debug("%s\n", __func__);
+ if (prtd->capture_substream == NULL)
+ return;
+
+ /* Copy dtmf detected info into out_queue. */
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ /* discarding dtmf detection info till start is received */
+ if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) {
+ buf_node = list_first_entry(&prtd->free_out_queue,
+ struct dtmf_buf_node, list);
+ list_del(&buf_node->list);
+ buf_node->dtmf_det_pkt.high_freq = dtmf_det_pkt->high_freq;
+ buf_node->dtmf_det_pkt.low_freq = dtmf_det_pkt->low_freq;
+ if (session != NULL)
+ strlcpy(buf_node->dtmf_det_pkt.session,
+ session, MAX_SESSION_NAME_LEN);
+
+ buf_node->dtmf_det_pkt.dir = DTMF_IN_RX;
+ pr_debug("high =%d, low=%d session=%s\n",
+ buf_node->dtmf_det_pkt.high_freq,
+ buf_node->dtmf_det_pkt.low_freq,
+ buf_node->dtmf_det_pkt.session);
+ list_add_tail(&buf_node->list, &prtd->out_queue);
+ prtd->pcm_capture_irq_pos += prtd->pcm_capture_count;
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ snd_pcm_period_elapsed(prtd->capture_substream);
+ } else {
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ pr_err("DTMF detection pkt in Rx dropped, no free node available\n");
+ }
+
+ wake_up(&prtd->out_wait);
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff,
+ void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int count = 0;
+ struct dtmf_buf_node *buf_node = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+ unsigned long dsp_flags;
+
+ count = frames_to_bytes(runtime, frames);
+
+ ret = wait_event_interruptible_timeout(prtd->out_wait,
+ (!list_empty(&prtd->out_queue)),
+ 1 * HZ);
+
+ if (ret > 0) {
+ if (count <= DTMF_PKT_SIZE) {
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ buf_node = list_first_entry(&prtd->out_queue,
+ struct dtmf_buf_node, list);
+ list_del(&buf_node->list);
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ ret = copy_to_user(buf,
+ &buf_node->dtmf_det_pkt,
+ count);
+ if (ret) {
+ pr_err("%s: Copy to user retuned %d\n",
+ __func__, ret);
+ ret = -EFAULT;
+ }
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ list_add_tail(&buf_node->list,
+ &prtd->free_out_queue);
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+
+ } else {
+ pr_err("%s: Read count %d > DTMF_PKT_SIZE\n",
+ __func__, count);
+ ret = -ENOMEM;
+ }
+ } else if (ret == 0) {
+ pr_err("%s: No UL data available\n", __func__);
+ ret = -ETIMEDOUT;
+ } else {
+ pr_err("%s: Read was interrupted\n", __func__);
+ ret = -ERESTARTSYS;
+ }
+ return ret;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ pr_debug("%s() DTMF\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = NULL;
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ prtd = kzalloc(sizeof(struct dtmf_drv_info), GFP_KERNEL);
+
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ mutex_init(&prtd->lock);
+ spin_lock_init(&prtd->dsp_lock);
+ init_waitqueue_head(&prtd->out_wait);
+ INIT_LIST_HEAD(&prtd->out_queue);
+ INIT_LIST_HEAD(&prtd->free_out_queue);
+
+ runtime->hw = msm_pcm_hardware;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->capture_substream = substream;
+ prtd->capture_instance++;
+ runtime->private_data = prtd;
+ }
+
+done:
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct list_head *ptr = NULL;
+ struct list_head *next = NULL;
+ struct dtmf_buf_node *buf_node = NULL;
+ struct snd_dma_buffer *c_dma_buf;
+ struct snd_pcm_substream *c_substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+ unsigned long dsp_flags;
+
+ pr_debug("%s() DTMF\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mutex_lock(&prtd->lock);
+ wake_up(&prtd->out_wait);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->capture_instance--;
+
+ if (!prtd->capture_instance) {
+ if (prtd->state == DTMF_GEN_RX_STARTED) {
+ prtd->state = DTMF_GEN_RX_STOPPED;
+ voc_disable_dtmf_det_on_active_sessions();
+ voc_register_dtmf_rx_detection_cb(NULL, NULL);
+ }
+ /* release all buffer */
+ /* release out_queue and free_out_queue */
+ pr_debug("release all buffer\n");
+ c_substream = prtd->capture_substream;
+ if (c_substream == NULL) {
+ pr_debug("c_substream is NULL\n");
+ mutex_unlock(&prtd->lock);
+ return -EINVAL;
+ }
+
+ c_dma_buf = &c_substream->dma_buffer;
+ if (c_dma_buf == NULL) {
+ pr_debug("c_dma_buf is NULL.\n");
+ mutex_unlock(&prtd->lock);
+ return -EINVAL;
+ }
+
+ if (c_dma_buf->area != NULL) {
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ list_for_each_safe(ptr, next,
+ &prtd->out_queue) {
+ buf_node = list_entry(ptr,
+ struct dtmf_buf_node, list);
+ list_del(&buf_node->list);
+ }
+
+ list_for_each_safe(ptr, next,
+ &prtd->free_out_queue) {
+ buf_node = list_entry(ptr,
+ struct dtmf_buf_node, list);
+ list_del(&buf_node->list);
+ }
+
+ spin_unlock_irqrestore(&prtd->dsp_lock,
+ dsp_flags);
+ dma_free_coherent(c_substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max,
+ c_dma_buf->area,
+ c_dma_buf->addr);
+ c_dma_buf->area = NULL;
+ }
+ }
+ prtd->capture_substream = NULL;
+ mutex_unlock(&prtd->lock);
+ }
+
+ return ret;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct dtmf_buf_node *buf_node = NULL;
+ int i = 0, offset = 0;
+ int ret = 0;
+
+ pr_debug("%s: DTMF\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mutex_lock(&prtd->lock);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+
+ dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max,
+ &dma_buf->addr, GFP_KERNEL);
+ if (!dma_buf->area) {
+ pr_err("%s:MSM DTMF dma_alloc failed\n", __func__);
+ mutex_unlock(&prtd->lock);
+ return -ENOMEM;
+ }
+
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+
+ for (i = 0; i < DTMF_MAX_Q_LEN; i++) {
+ pr_debug("node =%d\n", i);
+ buf_node = (void *) dma_buf->area + offset;
+ list_add_tail(&buf_node->list,
+ &prtd->free_out_queue);
+ offset = offset + sizeof(struct dtmf_buf_node);
+ }
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ mutex_unlock(&prtd->lock);
+ }
+
+ return ret;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+
+ pr_debug("%s: DTMF\n", __func__);
+ prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_capture_irq_pos = 0;
+ prtd->pcm_capture_buf_pos = 0;
+ return 0;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+
+ pr_debug("%s: DTMF\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mutex_lock(&prtd->lock);
+
+ msm_pcm_capture_prepare(substream);
+
+ if (runtime->format != FORMAT_S16_LE) {
+ pr_err("format:%u doesnt match %d\n",
+ (uint32_t)runtime->format, FORMAT_S16_LE);
+ mutex_unlock(&prtd->lock);
+ return -EINVAL;
+ }
+
+ if (prtd->capture_instance &&
+ (prtd->state != DTMF_GEN_RX_STARTED)) {
+ voc_register_dtmf_rx_detection_cb(dtmf_rx_detected_cb,
+ prtd);
+ prtd->state = DTMF_GEN_RX_STARTED;
+ }
+ mutex_unlock(&prtd->lock);
+ }
+
+ return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ pr_debug("%s: Trigger start\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->capture_start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->capture_start = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ snd_pcm_uframes_t ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size)
+ prtd->pcm_capture_irq_pos = 0;
+ ret = bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos));
+ }
+
+ return ret;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+ .probe = msm_pcm_dtmf_probe,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_pcm_dtmf_dt_match[] = {
+ {.compatible = "qcom,msm-pcm-dtmf"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_pcm_dtmf_dt_match);
+
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-dtmf",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_pcm_dtmf_dt_match,
+ },
+ .probe = msm_pcm_probe,
+ .remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("DTMF platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c
new file mode 100644
index 000000000000..b02ab78684fb
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c
@@ -0,0 +1,1552 @@
+/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/msm_audio_ion.h>
+#include "q6voice.h"
+
+#define HPCM_MAX_Q_LEN 2
+#define HPCM_MIN_VOC_PKT_SIZE 320
+#define HPCM_MAX_VOC_PKT_SIZE 640
+#define VHPCM_BLOCK_SIZE 4096
+#define CACHE_ALIGNMENT_SIZE 128
+#define CACHE_ALIGNMENT_MASK 0xFFFFFF80
+
+#define VOICE_TX_CAPTURE_DAI_ID "CS-VOICE HOST TX CAPTURE"
+#define VOICE_TX_PLAYBACK_DAI_ID "CS-VOICE HOST TX PLAYBACK"
+#define VOICE_RX_CAPTURE_DAI_ID "CS-VOICE HOST RX CAPTURE"
+#define VOICE_RX_PLAYBACK_DAI_ID "CS-VOICE HOST RX PLAYBACK"
+
+#define VOLTE_TX_CAPTURE_DAI_ID "VOLTE HOST TX CAPTURE"
+#define VOLTE_TX_PLAYBACK_DAI_ID "VOLTE HOST TX PLAYBACK"
+#define VOLTE_RX_CAPTURE_DAI_ID "VOLTE HOST RX CAPTURE"
+#define VOLTE_RX_PLAYBACK_DAI_ID "VOLTE HOST RX PLAYBACK"
+
+
+#define VoMMode1_TX_CAPTURE_DAI_ID "VoiceMMode1 HOST TX CAPTURE"
+#define VoMMode1_TX_PLAYBACK_DAI_ID "VoiceMMode1 HOST TX PLAYBACK"
+#define VoMMode1_RX_CAPTURE_DAI_ID "VoiceMMode1 HOST RX CAPTURE"
+#define VoMMode1_RX_PLAYBACK_DAI_ID "VoiceMMode1 HOST RX PLAYBACK"
+
+#define VoMMode2_TX_CAPTURE_DAI_ID "VoiceMMode2 HOST TX CAPTURE"
+#define VoMMode2_TX_PLAYBACK_DAI_ID "VoiceMMode2 HOST TX PLAYBACK"
+#define VoMMode2_RX_CAPTURE_DAI_ID "VoiceMMode2 HOST RX CAPTURE"
+#define VoMMode2_RX_PLAYBACK_DAI_ID "VoiceMMode2 HOST RX PLAYBACK"
+
+enum {
+ RX = 1,
+ TX,
+};
+
+enum {
+ VOICE_INDEX = 0,
+ VOLTE_INDEX,
+ VOMMODE1_INDEX,
+ VOMMODE2_INDEX,
+ MAX_SESSION
+};
+
+enum hpcm_state {
+ HPCM_STOPPED = 1,
+ HPCM_CLOSED,
+ HPCM_PREPARED,
+ HPCM_STARTED,
+};
+
+struct hpcm_frame {
+ uint32_t len;
+ uint8_t voc_pkt[HPCM_MAX_VOC_PKT_SIZE];
+};
+
+struct hpcm_buf_node {
+ struct list_head list;
+ struct hpcm_frame frame;
+};
+
+struct vocpcm_ion_buffer {
+ /* Physical address */
+ phys_addr_t paddr;
+ /* Kernel virtual address */
+ void *kvaddr;
+};
+
+struct dai_data {
+ enum hpcm_state state;
+ struct snd_pcm_substream *substream;
+ struct list_head filled_queue;
+ struct list_head free_queue;
+ wait_queue_head_t queue_wait;
+ spinlock_t dsp_lock;
+ uint32_t pcm_size;
+ uint32_t pcm_count;
+ /* IRQ position */
+ uint32_t pcm_irq_pos;
+ /* Position in buffer */
+ uint32_t pcm_buf_pos;
+ struct vocpcm_ion_buffer vocpcm_ion_buffer;
+};
+
+struct tap_point {
+ struct dai_data playback_dai_data;
+ struct dai_data capture_dai_data;
+};
+
+struct session {
+ struct tap_point tx_tap_point;
+ struct tap_point rx_tap_point;
+ phys_addr_t sess_paddr;
+ void *sess_kvaddr;
+ struct ion_handle *ion_handle;
+ struct mem_map_table tp_mem_table;
+};
+
+struct tappnt_mxr_data {
+ bool enable;
+ uint16_t direction;
+ uint16_t sample_rate;
+};
+
+/* Values from mixer ctl are cached in this structure */
+struct mixer_conf {
+ int8_t sess_indx;
+ struct tappnt_mxr_data rx;
+ struct tappnt_mxr_data tx;
+};
+
+struct start_cmd {
+ struct vss_ivpcm_tap_point tap_pnt[2];
+ uint32_t no_of_tapoints;
+};
+
+struct hpcm_drv {
+ struct mutex lock;
+ struct session session[MAX_SESSION];
+ struct mixer_conf mixer_conf;
+ struct ion_client *ion_client;
+ struct start_cmd start_cmd;
+};
+
+static struct hpcm_drv hpcm_drv;
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_SPECIAL,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = sizeof(struct hpcm_buf_node) * HPCM_MAX_Q_LEN,
+ .period_bytes_min = HPCM_MIN_VOC_PKT_SIZE,
+ .period_bytes_max = HPCM_MAX_VOC_PKT_SIZE,
+ .periods_min = HPCM_MAX_Q_LEN,
+ .periods_max = HPCM_MAX_Q_LEN,
+ .fifo_size = 0,
+};
+
+static char *hpcm_get_sess_name(int sess_indx)
+{
+ char *sess_name = NULL;
+
+ if (sess_indx == VOICE_INDEX)
+ sess_name = VOICE_SESSION_NAME;
+ else if (sess_indx == VOLTE_INDEX)
+ sess_name = VOLTE_SESSION_NAME;
+ else if (sess_indx == VOMMODE1_INDEX)
+ sess_name = VOICEMMODE1_NAME;
+ else if (sess_indx == VOMMODE2_INDEX)
+ sess_name = VOICEMMODE2_NAME;
+ else
+ pr_err("%s:, Invalid sess_index\n", __func__);
+
+ return sess_name;
+}
+
+static void hpcm_reset_mixer_config(struct hpcm_drv *prtd)
+{
+ prtd->mixer_conf.sess_indx = -1;
+ prtd->mixer_conf.rx.enable = false;
+ prtd->mixer_conf.rx.direction = -1;
+ prtd->mixer_conf.rx.sample_rate = 0;
+
+ prtd->mixer_conf.tx.enable = false;
+ prtd->mixer_conf.tx.direction = -1;
+ prtd->mixer_conf.tx.sample_rate = 0;
+}
+
+/* Check for valid mixer control values */
+static bool hpcm_is_valid_config(int sess_indx, int tap_point,
+ uint16_t direction, uint16_t samplerate)
+{
+ if (sess_indx < VOICE_INDEX || sess_indx > VOMMODE2_INDEX) {
+ pr_err("%s: invalid sess_indx :%d\n", __func__, sess_indx);
+ goto error;
+ }
+
+ if (samplerate != VSS_IVPCM_SAMPLING_RATE_8K &&
+ samplerate != VSS_IVPCM_SAMPLING_RATE_16K) {
+ pr_err("%s: invalid sample rate :%d\n", __func__, samplerate);
+ goto error;
+ }
+
+ if ((tap_point != RX) && (tap_point != TX)) {
+ pr_err("%s: invalid tappoint :%d\n", __func__, tap_point);
+ goto error;
+ }
+
+ if ((direction != VSS_IVPCM_TAP_POINT_DIR_IN) &&
+ (direction != VSS_IVPCM_TAP_POINT_DIR_OUT) &&
+ (direction != VSS_IVPCM_TAP_POINT_DIR_OUT_IN)) {
+ pr_err("%s: invalid direction :%d\n", __func__, direction);
+ goto error;
+ }
+
+ return true;
+
+error:
+ return false;
+}
+
+
+static struct dai_data *hpcm_get_dai_data(char *pcm_id, struct hpcm_drv *prtd)
+{
+ struct dai_data *dai_data = NULL;
+ size_t size = 0;
+
+ if (pcm_id) {
+ size = strlen(pcm_id);
+ /* Check for Voice DAI */
+ if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOICE_INDEX].tx_tap_point.capture_dai_data;
+ } else if (strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOICE_INDEX].tx_tap_point.playback_dai_data;
+ } else if (strnstr(pcm_id, VOICE_RX_CAPTURE_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOICE_INDEX].rx_tap_point.capture_dai_data;
+ } else if (strnstr(pcm_id, VOICE_RX_PLAYBACK_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOICE_INDEX].rx_tap_point.playback_dai_data;
+ /* Check for VoLTE DAI */
+ } else if (strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOLTE_INDEX].tx_tap_point.capture_dai_data;
+ } else if (strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOLTE_INDEX].tx_tap_point.playback_dai_data;
+ } else if (strnstr(pcm_id, VOLTE_RX_CAPTURE_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOLTE_INDEX].rx_tap_point.capture_dai_data;
+ } else if (strnstr(pcm_id, VOLTE_RX_PLAYBACK_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOLTE_INDEX].rx_tap_point.playback_dai_data;
+ /* check for VoiceMMode1 DAI */
+ } else if (strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOMMODE1_INDEX].tx_tap_point.capture_dai_data;
+ } else if (strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOMMODE1_INDEX].tx_tap_point.playback_dai_data;
+ } else if (strnstr(pcm_id, VoMMode1_RX_CAPTURE_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOMMODE1_INDEX].rx_tap_point.capture_dai_data;
+ } else if (strnstr(pcm_id, VoMMode1_RX_PLAYBACK_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOMMODE1_INDEX].rx_tap_point.playback_dai_data;
+ /* check for VOiceMMode2 DAI */
+ } else if (strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOMMODE2_INDEX].tx_tap_point.capture_dai_data;
+ } else if (strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOMMODE2_INDEX].tx_tap_point.playback_dai_data;
+ } else if (strnstr(pcm_id, VoMMode2_RX_CAPTURE_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOMMODE2_INDEX].rx_tap_point.capture_dai_data;
+ } else if (strnstr(pcm_id, VoMMode2_RX_PLAYBACK_DAI_ID, size)) {
+ dai_data =
+ &prtd->session[VOMMODE2_INDEX].rx_tap_point.playback_dai_data;
+
+ } else {
+ pr_err("%s: Wrong dai id\n", __func__);
+ }
+ }
+
+ return dai_data;
+}
+
+static struct tap_point *hpcm_get_tappoint_data(char *pcm_id,
+ struct hpcm_drv *prtd)
+{
+ struct tap_point *tp = NULL;
+ size_t size = 0;
+
+ if (pcm_id) {
+ size = strlen(pcm_id);
+ /* Check for Voice DAI */
+ if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, size)) {
+ tp = &prtd->session[VOICE_INDEX].tx_tap_point;
+ } else if (strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, size)) {
+ tp = &prtd->session[VOICE_INDEX].tx_tap_point;
+ } else if (strnstr(pcm_id, VOICE_RX_CAPTURE_DAI_ID, size)) {
+ tp = &prtd->session[VOICE_INDEX].rx_tap_point;
+ } else if (strnstr(pcm_id, VOICE_RX_PLAYBACK_DAI_ID, size)) {
+ tp = &prtd->session[VOICE_INDEX].rx_tap_point;
+ /* Check for VoLTE DAI */
+ } else if (strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, size)) {
+ tp = &prtd->session[VOLTE_INDEX].tx_tap_point;
+ } else if (strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, size)) {
+ tp = &prtd->session[VOLTE_INDEX].tx_tap_point;
+ } else if (strnstr(pcm_id, VOLTE_RX_CAPTURE_DAI_ID, size)) {
+ tp = &prtd->session[VOLTE_INDEX].rx_tap_point;
+ } else if (strnstr(pcm_id, VOLTE_RX_PLAYBACK_DAI_ID, size)) {
+ tp = &prtd->session[VOLTE_INDEX].rx_tap_point;
+ /* check for VoiceMMode1 */
+ } else if (strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, size)) {
+ tp = &prtd->session[VOMMODE1_INDEX].tx_tap_point;
+ } else if (strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, size)) {
+ tp = &prtd->session[VOMMODE1_INDEX].tx_tap_point;
+ } else if (strnstr(pcm_id, VoMMode1_RX_CAPTURE_DAI_ID, size)) {
+ tp = &prtd->session[VOMMODE1_INDEX].rx_tap_point;
+ } else if (strnstr(pcm_id, VoMMode1_RX_PLAYBACK_DAI_ID, size)) {
+ tp = &prtd->session[VOMMODE1_INDEX].rx_tap_point;
+ /* check for VoiceMMode2 */
+ } else if (strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, size)) {
+ tp = &prtd->session[VOMMODE2_INDEX].tx_tap_point;
+ } else if (strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, size)) {
+ tp = &prtd->session[VOMMODE2_INDEX].tx_tap_point;
+ } else if (strnstr(pcm_id, VoMMode2_RX_CAPTURE_DAI_ID, size)) {
+ tp = &prtd->session[VOMMODE2_INDEX].rx_tap_point;
+ } else if (strnstr(pcm_id, VoMMode2_RX_PLAYBACK_DAI_ID, size)) {
+ tp = &prtd->session[VOMMODE2_INDEX].rx_tap_point;
+ } else {
+ pr_err("%s: wrong dai id\n", __func__);
+ }
+ }
+
+ return tp;
+}
+
+static struct tappnt_mxr_data *hpcm_get_tappnt_mixer_data(char *pcm_id,
+ struct hpcm_drv *prtd)
+{
+
+ if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, strlen(pcm_id))) {
+ return &prtd->mixer_conf.tx;
+ } else {
+ return &prtd->mixer_conf.rx;
+ }
+}
+
+static int get_tappnt_value(char *pcm_id)
+{
+
+ if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+ strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, strlen(pcm_id))) {
+ return TX;
+ } else {
+ return RX;
+ }
+}
+
+static bool hpcm_all_dais_are_ready(uint16_t direction, struct tap_point *tp,
+ enum hpcm_state state)
+{
+ bool dais_started = false;
+
+ /*
+ * Based on the direction set per tap point in the mixer control,
+ * all the dais per tap point should meet the required state for the
+ * commands such as vpcm_map_memory/vpcm_start to be executed.
+ */
+ switch (direction) {
+ case VSS_IVPCM_TAP_POINT_DIR_OUT_IN:
+ if ((tp->playback_dai_data.state >= state) &&
+ (tp->capture_dai_data.state >= state)) {
+ dais_started = true;
+ }
+ break;
+
+ case VSS_IVPCM_TAP_POINT_DIR_IN:
+ if (tp->playback_dai_data.state >= state)
+ dais_started = true;
+ break;
+
+ case VSS_IVPCM_TAP_POINT_DIR_OUT:
+ if (tp->capture_dai_data.state >= state)
+ dais_started = true;
+ break;
+
+ default:
+ pr_err("invalid direction\n");
+ }
+
+ return dais_started;
+}
+
+static void hpcm_create_free_queue(struct snd_dma_buffer *dma_buf,
+ struct dai_data *dai_data)
+{
+ struct hpcm_buf_node *buf_node = NULL;
+ int i = 0, offset = 0;
+
+ for (i = 0; i < HPCM_MAX_Q_LEN; i++) {
+ buf_node = (void *)dma_buf->area + offset;
+ list_add_tail(&buf_node->list,
+ &dai_data->free_queue);
+ offset = offset + sizeof(struct hpcm_buf_node);
+ }
+}
+
+static void hpcm_free_allocated_mem(struct hpcm_drv *prtd)
+{
+ phys_addr_t paddr = 0;
+ struct tap_point *txtp = NULL;
+ struct tap_point *rxtp = NULL;
+ struct session *sess = NULL;
+
+ sess = &prtd->session[prtd->mixer_conf.sess_indx];
+ txtp = &sess->tx_tap_point;
+ rxtp = &sess->rx_tap_point;
+ paddr = sess->sess_paddr;
+
+ if (paddr) {
+ msm_audio_ion_free(prtd->ion_client, sess->ion_handle);
+ prtd->ion_client = NULL;
+ sess->ion_handle = NULL;
+ msm_audio_ion_free(sess->tp_mem_table.client,
+ sess->tp_mem_table.handle);
+ sess->tp_mem_table.client = NULL;
+ sess->tp_mem_table.handle = NULL;
+ sess->sess_paddr = 0;
+ sess->sess_kvaddr = 0;
+ sess->ion_handle = 0;
+ prtd->ion_client = 0;
+ sess->tp_mem_table.client = 0;
+ sess->tp_mem_table.handle = 0;
+
+ txtp->capture_dai_data.vocpcm_ion_buffer.paddr = 0;
+ txtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = 0;
+
+ txtp->playback_dai_data.vocpcm_ion_buffer.paddr = 0;
+ txtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = 0;
+
+ rxtp->capture_dai_data.vocpcm_ion_buffer.paddr = 0;
+ rxtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = 0;
+
+ rxtp->playback_dai_data.vocpcm_ion_buffer.paddr = 0;
+ rxtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = 0;
+ } else {
+ pr_debug("%s, paddr = 0, nothing to free\n", __func__);
+ }
+}
+
+static void hpcm_unmap_and_free_shared_memory(struct hpcm_drv *prtd)
+
+{
+ phys_addr_t paddr = 0;
+ char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx);
+
+ if (prtd->mixer_conf.sess_indx >= 0)
+ paddr = prtd->session[prtd->mixer_conf.sess_indx].sess_paddr;
+ else
+ paddr = 0;
+
+ if (paddr) {
+ voc_send_cvp_unmap_vocpcm_memory(voc_get_session_id(sess_name));
+ hpcm_free_allocated_mem(prtd);
+ } else {
+ pr_debug("%s, paddr = 0, nothing to unmap/free\n", __func__);
+ }
+}
+
+static int hpcm_map_vocpcm_memory(struct hpcm_drv *prtd)
+{
+ int ret = 0;
+ char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx);
+ struct session *sess = NULL;
+ sess = &prtd->session[prtd->mixer_conf.sess_indx];
+
+ ret = voc_send_cvp_map_vocpcm_memory(voc_get_session_id(sess_name),
+ &sess->tp_mem_table,
+ sess->sess_paddr,
+ VHPCM_BLOCK_SIZE);
+
+ return ret;
+}
+
+static int hpcm_allocate_shared_memory(struct hpcm_drv *prtd)
+{
+ int result;
+ int ret = 0;
+ size_t mem_len;
+ size_t len;
+ struct tap_point *txtp = NULL;
+ struct tap_point *rxtp = NULL;
+ struct session *sess = NULL;
+
+ sess = &prtd->session[prtd->mixer_conf.sess_indx];
+ txtp = &sess->tx_tap_point;
+ rxtp = &sess->rx_tap_point;
+
+ result = msm_audio_ion_alloc("host_pcm_buffer",
+ &prtd->ion_client,
+ &sess->ion_handle,
+ VHPCM_BLOCK_SIZE,
+ &sess->sess_paddr,
+ &mem_len,
+ &sess->sess_kvaddr);
+ if (result) {
+ pr_err("%s: msm_audio_ion_alloc error, rc = %d\n",
+ __func__, result);
+ sess->sess_paddr = 0;
+ sess->sess_kvaddr = 0;
+ ret = -ENOMEM;
+ goto done;
+ }
+ pr_debug("%s: Host PCM memory block allocated\n", __func__);
+
+ /* Allocate mem_map_table for tap point */
+ result = msm_audio_ion_alloc("host_pcm_table",
+ &sess->tp_mem_table.client,
+ &sess->tp_mem_table.handle,
+ sizeof(struct vss_imemory_table_t),
+ &sess->tp_mem_table.phys,
+ &len,
+ &sess->tp_mem_table.data);
+
+ if (result) {
+ pr_err("%s: msm_audio_ion_alloc error, rc = %d\n",
+ __func__, result);
+ msm_audio_ion_free(prtd->ion_client, sess->ion_handle);
+ prtd->ion_client = NULL;
+ sess->ion_handle = NULL;
+ sess->sess_paddr = 0;
+ sess->sess_kvaddr = 0;
+ ret = -ENOMEM;
+ goto done;
+ }
+ pr_debug("%s: Host PCM memory table allocated\n", __func__);
+
+ memset(sess->tp_mem_table.data, 0,
+ sizeof(struct vss_imemory_table_t));
+
+ sess->tp_mem_table.size = sizeof(struct vss_imemory_table_t);
+
+ pr_debug("%s: data %pK phys %pK\n", __func__,
+ sess->tp_mem_table.data, &sess->tp_mem_table.phys);
+
+ /* Split 4096 block into four 1024 byte blocks for each dai */
+ txtp->capture_dai_data.vocpcm_ion_buffer.paddr =
+ sess->sess_paddr;
+ txtp->capture_dai_data.vocpcm_ion_buffer.kvaddr =
+ sess->sess_kvaddr;
+
+ txtp->playback_dai_data.vocpcm_ion_buffer.paddr =
+ sess->sess_paddr + VHPCM_BLOCK_SIZE/4;
+ txtp->playback_dai_data.vocpcm_ion_buffer.kvaddr =
+ sess->sess_kvaddr + VHPCM_BLOCK_SIZE/4;
+
+ rxtp->capture_dai_data.vocpcm_ion_buffer.paddr =
+ sess->sess_paddr + (VHPCM_BLOCK_SIZE/4) * 2;
+ rxtp->capture_dai_data.vocpcm_ion_buffer.kvaddr =
+ sess->sess_kvaddr + (VHPCM_BLOCK_SIZE/4) * 2;
+
+ rxtp->playback_dai_data.vocpcm_ion_buffer.paddr =
+ sess->sess_paddr + (VHPCM_BLOCK_SIZE/4) * 3;
+ rxtp->playback_dai_data.vocpcm_ion_buffer.kvaddr =
+ sess->sess_kvaddr + (VHPCM_BLOCK_SIZE/4) * 3;
+
+done:
+ return ret;
+}
+
+static int hpcm_start_vocpcm(char *pcm_id, struct hpcm_drv *prtd,
+ struct tap_point *tp)
+{
+ int indx = prtd->mixer_conf.sess_indx;
+ uint32_t *no_of_tp = &prtd->start_cmd.no_of_tapoints;
+ struct vss_ivpcm_tap_point *tap_pnt = &prtd->start_cmd.tap_pnt[0];
+ uint32_t no_of_tp_req = 0;
+ char *sess_name = hpcm_get_sess_name(indx);
+
+ if (prtd->mixer_conf.rx.enable)
+ no_of_tp_req++;
+ if (prtd->mixer_conf.tx.enable)
+ no_of_tp_req++;
+
+ if (prtd->mixer_conf.rx.enable && (get_tappnt_value(pcm_id) == RX)) {
+ if (hpcm_all_dais_are_ready(prtd->mixer_conf.rx.direction,
+ tp, HPCM_PREPARED)) {
+ pr_debug("%s: RX conditions met\n", __func__);
+ tap_pnt[*no_of_tp].tap_point =
+ VSS_IVPCM_TAP_POINT_RX_DEFAULT;
+ tap_pnt[*no_of_tp].direction =
+ prtd->mixer_conf.rx.direction;
+ tap_pnt[*no_of_tp].sampling_rate =
+ prtd->mixer_conf.rx.sample_rate;
+ (*no_of_tp)++;
+ }
+ }
+
+ if (prtd->mixer_conf.tx.enable && (get_tappnt_value(pcm_id) == TX)) {
+ if (hpcm_all_dais_are_ready(prtd->mixer_conf.tx.direction,
+ tp, HPCM_PREPARED)) {
+ pr_debug("%s: TX conditions met\n", __func__);
+ tap_pnt[*no_of_tp].tap_point =
+ VSS_IVPCM_TAP_POINT_TX_DEFAULT;
+ tap_pnt[*no_of_tp].direction =
+ prtd->mixer_conf.tx.direction;
+ tap_pnt[*no_of_tp].sampling_rate =
+ prtd->mixer_conf.tx.sample_rate;
+ (*no_of_tp)++;
+ }
+ }
+
+ if ((prtd->mixer_conf.tx.enable || prtd->mixer_conf.rx.enable) &&
+ *no_of_tp == no_of_tp_req) {
+ voc_send_cvp_start_vocpcm(voc_get_session_id(sess_name),
+ tap_pnt, *no_of_tp);
+ /* Reset the start command so that it is not called twice */
+ memset(&prtd->start_cmd, 0, sizeof(struct start_cmd));
+ } else {
+ pr_debug("%s: required pcm handles not opened yet\n", __func__);
+ }
+
+ return 0;
+}
+
+/* Playback path*/
+static void hpcm_copy_playback_data_from_queue(struct dai_data *dai_data,
+ uint32_t *len)
+{
+ struct hpcm_buf_node *buf_node = NULL;
+ unsigned long dsp_flags;
+
+ if (dai_data->substream == NULL)
+ return;
+
+ spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+
+ if (!list_empty(&dai_data->filled_queue)) {
+ buf_node = list_first_entry(&dai_data->filled_queue,
+ struct hpcm_buf_node, list);
+ list_del(&buf_node->list);
+ *len = buf_node->frame.len;
+ memcpy((u8 *)dai_data->vocpcm_ion_buffer.kvaddr,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.len);
+
+ list_add_tail(&buf_node->list, &dai_data->free_queue);
+ dai_data->pcm_irq_pos += dai_data->pcm_count;
+ spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+ snd_pcm_period_elapsed(dai_data->substream);
+ } else {
+ *len = 0;
+ spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+ pr_err("IN data not available\n");
+ }
+
+ wake_up(&dai_data->queue_wait);
+}
+
+/* Capture path*/
+static void hpcm_copy_capture_data_to_queue(struct dai_data *dai_data,
+ uint32_t len)
+{
+ struct hpcm_buf_node *buf_node = NULL;
+ unsigned long dsp_flags;
+
+ if (dai_data->substream == NULL)
+ return;
+
+ /* Copy out buffer packet into free_queue */
+ spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+
+ if (!list_empty(&dai_data->free_queue)) {
+ buf_node = list_first_entry(&dai_data->free_queue,
+ struct hpcm_buf_node, list);
+ list_del(&buf_node->list);
+ buf_node->frame.len = len;
+ memcpy(&buf_node->frame.voc_pkt[0],
+ (uint8_t *)dai_data->vocpcm_ion_buffer.kvaddr,
+ buf_node->frame.len);
+ list_add_tail(&buf_node->list, &dai_data->filled_queue);
+ dai_data->pcm_irq_pos += dai_data->pcm_count;
+ spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+ snd_pcm_period_elapsed(dai_data->substream);
+ } else {
+ spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+ pr_err("OUTPUT data dropped\n");
+ }
+
+ wake_up(&dai_data->queue_wait);
+}
+
+void hpcm_notify_evt_processing(uint8_t *data, char *session,
+ void *private_data)
+{
+ struct hpcm_drv *prtd = (struct hpcm_drv *)private_data;
+ struct vss_ivpcm_evt_notify_v2_t *notify_evt =
+ (struct vss_ivpcm_evt_notify_v2_t *)data;
+ struct vss_ivpcm_evt_push_buffer_v2_t push_buff_event;
+ struct tap_point *tp = NULL;
+ int in_buf_len = 0;
+ struct tappnt_mxr_data *tmd = NULL;
+ char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx);
+
+ /* If it's not a timetick, it's a error notification, drop the event */
+ if ((notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_TIMETICK) == 0) {
+ pr_err("%s: Error notification. mask=%d\n", __func__,
+ notify_evt->notify_mask);
+ return;
+ }
+
+ if (notify_evt->tap_point == VSS_IVPCM_TAP_POINT_TX_DEFAULT) {
+ tp = &prtd->session[prtd->mixer_conf.sess_indx].tx_tap_point;
+ tmd = &prtd->mixer_conf.tx;
+ } else if (notify_evt->tap_point == VSS_IVPCM_TAP_POINT_RX_DEFAULT) {
+ tp = &prtd->session[prtd->mixer_conf.sess_indx].rx_tap_point;
+ tmd = &prtd->mixer_conf.rx;
+ }
+
+ if (tp == NULL || tmd == NULL) {
+ pr_err("%s: tp = %pK or tmd = %pK is null\n", __func__,
+ tp, tmd);
+
+ return;
+ }
+
+ if (notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_OUTPUT_BUFFER) {
+ hpcm_copy_capture_data_to_queue(&tp->capture_dai_data,
+ notify_evt->filled_out_size);
+ }
+
+ if (notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_INPUT_BUFFER) {
+ hpcm_copy_playback_data_from_queue(&tp->playback_dai_data,
+ &in_buf_len);
+ }
+
+ switch (tmd->direction) {
+ /*
+ * When the dir is OUT_IN, for the first notify mask, pushbuf mask
+ * should be set to VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER since we
+ * atleast need one buffer's worth data before we can send IN buffer.
+ * For the consecutive notify evts, the push buf mask will set for both
+ * VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER and
+ * VSS_IVPCM_PUSH_BUFFER_MASK_IN_BUFFER.
+ */
+ case VSS_IVPCM_TAP_POINT_DIR_OUT_IN:
+ if (notify_evt->notify_mask ==
+ VSS_IVPCM_NOTIFY_MASK_TIMETICK) {
+ push_buff_event.push_buf_mask =
+ VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER;
+ } else {
+ push_buff_event.push_buf_mask =
+ VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER |
+ VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER;
+ }
+ break;
+
+ case VSS_IVPCM_TAP_POINT_DIR_IN:
+ push_buff_event.push_buf_mask =
+ VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER;
+ break;
+
+ case VSS_IVPCM_TAP_POINT_DIR_OUT:
+ push_buff_event.push_buf_mask =
+ VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER;
+ break;
+ }
+
+ push_buff_event.tap_point = notify_evt->tap_point;
+ push_buff_event.out_buf_mem_address =
+ tp->capture_dai_data.vocpcm_ion_buffer.paddr;
+ push_buff_event.in_buf_mem_address =
+ tp->playback_dai_data.vocpcm_ion_buffer.paddr;
+ push_buff_event.sampling_rate = notify_evt->sampling_rate;
+ push_buff_event.num_in_channels = 1;
+
+ /*
+ * ADSP must read and write from a cache aligned (128 byte) location,
+ * and in blocks of the cache alignment size. The 128 byte cache
+ * alignment requirement is guaranteed due to 4096 byte memory
+ * alignment requirement during memory allocation/mapping. The output
+ * buffer (ADSP write) size mask ensures that a 128 byte multiple
+ * worth of will be written. Internally, the input buffer (ADSP read)
+ * size will also be a multiple of 128 bytes. However it is the
+ * application's responsibility to ensure no other data is written in
+ * the specified length of memory.
+ */
+ push_buff_event.out_buf_mem_size = ((notify_evt->request_buf_size) +
+ CACHE_ALIGNMENT_SIZE) & CACHE_ALIGNMENT_MASK;
+ push_buff_event.in_buf_mem_size = in_buf_len;
+
+ voc_send_cvp_vocpcm_push_buf_evt(voc_get_session_id(sess_name),
+ &push_buff_event);
+}
+
+static int msm_hpcm_configure_voice_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ int tap_point = ucontrol->value.integer.value[0];
+ uint16_t direction = ucontrol->value.integer.value[1];
+ uint16_t sample_rate = ucontrol->value.integer.value[2];
+ struct tappnt_mxr_data *tmd = NULL;
+ int ret = 0;
+
+ mutex_lock(&hpcm_drv.lock);
+ pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n",
+ __func__, tap_point, direction, sample_rate);
+
+ if (!hpcm_is_valid_config(VOICE_INDEX, tap_point, direction,
+ sample_rate)) {
+ pr_err("Invalid vpcm mixer control voice values\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (tap_point == RX)
+ tmd = &hpcm_drv.mixer_conf.rx;
+ else
+ tmd = &hpcm_drv.mixer_conf.tx;
+
+ tmd->enable = true;
+ tmd->direction = direction;
+ tmd->sample_rate = sample_rate;
+ hpcm_drv.mixer_conf.sess_indx = VOICE_INDEX;
+
+done:
+ mutex_unlock(&hpcm_drv.lock);
+ return ret;
+}
+
+static int msm_hpcm_configure_vmmode1_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ int tap_point = ucontrol->value.integer.value[0];
+ uint16_t direction = ucontrol->value.integer.value[1];
+ uint16_t sample_rate = ucontrol->value.integer.value[2];
+ struct tappnt_mxr_data *tmd = NULL;
+ int ret = 0;
+
+ mutex_lock(&hpcm_drv.lock);
+ pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n",
+ __func__, tap_point, direction, sample_rate);
+
+ if (!hpcm_is_valid_config(VOMMODE1_INDEX, tap_point, direction,
+ sample_rate)) {
+ pr_err("Invalid vpcm mixer control voice values\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (tap_point == RX)
+ tmd = &hpcm_drv.mixer_conf.rx;
+ else
+ tmd = &hpcm_drv.mixer_conf.tx;
+
+ tmd->enable = true;
+ tmd->direction = direction;
+ tmd->sample_rate = sample_rate;
+ hpcm_drv.mixer_conf.sess_indx = VOMMODE1_INDEX;
+
+done:
+ mutex_unlock(&hpcm_drv.lock);
+ return ret;
+}
+
+static int msm_hpcm_configure_vmmode2_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ int tap_point = ucontrol->value.integer.value[0];
+ uint16_t direction = ucontrol->value.integer.value[1];
+ uint16_t sample_rate = ucontrol->value.integer.value[2];
+ struct tappnt_mxr_data *tmd = NULL;
+ int ret = 0;
+
+ mutex_lock(&hpcm_drv.lock);
+ pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n",
+ __func__, tap_point, direction, sample_rate);
+
+ if (!hpcm_is_valid_config(VOMMODE2_INDEX, tap_point, direction,
+ sample_rate)) {
+ pr_err("Invalid vpcm mixer control voice values\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (tap_point == RX)
+ tmd = &hpcm_drv.mixer_conf.rx;
+ else
+ tmd = &hpcm_drv.mixer_conf.tx;
+
+ tmd->enable = true;
+ tmd->direction = direction;
+ tmd->sample_rate = sample_rate;
+ hpcm_drv.mixer_conf.sess_indx = VOMMODE2_INDEX;
+
+done:
+ mutex_unlock(&hpcm_drv.lock);
+ return ret;
+}
+
+static int msm_hpcm_configure_volte_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ int tap_point = ucontrol->value.integer.value[0];
+ uint16_t direction = ucontrol->value.integer.value[1];
+ uint16_t sample_rate = ucontrol->value.integer.value[2];
+ struct tappnt_mxr_data *tmd = NULL;
+ int ret = 0;
+
+ mutex_lock(&hpcm_drv.lock);
+ pr_debug("%s: tap_point=%d direction=%d sample_rate=%d\n",
+ __func__, tap_point, direction, sample_rate);
+
+ if (!hpcm_is_valid_config(VOLTE_INDEX, tap_point, direction,
+ sample_rate)) {
+ pr_err("Invalid vpcm mixer control volte values\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (tap_point == RX)
+ tmd = &hpcm_drv.mixer_conf.rx;
+ else
+ tmd = &hpcm_drv.mixer_conf.tx;
+
+ tmd->enable = true;
+ tmd->direction = direction;
+ tmd->sample_rate = sample_rate;
+ hpcm_drv.mixer_conf.sess_indx = VOLTE_INDEX;
+
+done:
+ mutex_unlock(&hpcm_drv.lock);
+ return ret;
+
+}
+
+static struct snd_kcontrol_new msm_hpcm_controls[] = {
+ SOC_SINGLE_MULTI_EXT("HPCM_Voice tappoint direction samplerate",
+ SND_SOC_NOPM, 0, 16000, 0, 3,
+ NULL, msm_hpcm_configure_voice_put),
+ SOC_SINGLE_MULTI_EXT("HPCM_VoLTE tappoint direction samplerate",
+ SND_SOC_NOPM, 0, 16000, 0, 3,
+ NULL, msm_hpcm_configure_volte_put),
+ SOC_SINGLE_MULTI_EXT("HPCM_VMMode1 tappoint direction samplerate",
+ SND_SOC_NOPM, 0, 16000, 0, 3,
+ NULL, msm_hpcm_configure_vmmode1_put),
+ SOC_SINGLE_MULTI_EXT("HPCM_VMMode2 tappoint direction samplerate",
+ SND_SOC_NOPM, 0, 16000, 0, 3,
+ NULL, msm_hpcm_configure_vmmode2_put),
+};
+
+/* Sample rates supported */
+static unsigned int supported_sample_rates[] = {8000, 16000};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct list_head *ptr = NULL;
+ struct list_head *next = NULL;
+ struct hpcm_buf_node *buf_node = NULL;
+ struct snd_dma_buffer *dma_buf;
+ struct snd_pcm_runtime *runtime;
+ struct hpcm_drv *prtd;
+ unsigned long dsp_flags;
+ struct dai_data *dai_data = NULL;
+ struct tap_point *tp = NULL;
+ struct tappnt_mxr_data *tmd = NULL;
+ char *sess_name = NULL;
+
+ if (substream == NULL) {
+ pr_err("substream is NULL\n");
+ return -EINVAL;
+ }
+
+ pr_debug("%s, %s\n", __func__, substream->pcm->id);
+ runtime = substream->runtime;
+ prtd = runtime->private_data;
+ sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx);
+ dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+
+ if (dai_data == NULL) {
+ pr_err("%s, dai_data is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ wake_up(&dai_data->queue_wait);
+ mutex_lock(&prtd->lock);
+
+ tmd = hpcm_get_tappnt_mixer_data(substream->pcm->id, prtd);
+
+ tp = hpcm_get_tappoint_data(substream->pcm->id, prtd);
+ /* Send stop command */
+ voc_send_cvp_stop_vocpcm(voc_get_session_id(sess_name));
+ /* Memory unmap/free takes place only when called the first time */
+ hpcm_unmap_and_free_shared_memory(prtd);
+ /* Unregister host PCM event callback function */
+ voc_deregister_hpcm_evt_cb();
+ /* Reset the cached start cmd */
+ memset(&prtd->start_cmd, 0, sizeof(struct start_cmd));
+ /* Release all buffer */
+ pr_debug("%s: Release all buffer\n", __func__);
+ substream = dai_data->substream;
+ if (substream == NULL) {
+ pr_debug("%s: substream is NULL\n", __func__);
+ goto done;
+ }
+ dma_buf = &substream->dma_buffer;
+ if (dma_buf == NULL) {
+ pr_debug("%s: dma_buf is NULL\n", __func__);
+ goto done;
+ }
+ if (dma_buf->area != NULL) {
+ spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+ list_for_each_safe(ptr, next, &dai_data->filled_queue) {
+ buf_node = list_entry(ptr,
+ struct hpcm_buf_node, list);
+ list_del(&buf_node->list);
+ }
+ list_for_each_safe(ptr, next, &dai_data->free_queue) {
+ buf_node = list_entry(ptr,
+ struct hpcm_buf_node, list);
+ list_del(&buf_node->list);
+ }
+ spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+ dma_free_coherent(substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max, dma_buf->area,
+ dma_buf->addr);
+ dma_buf->area = NULL;
+ }
+ dai_data->substream = NULL;
+ dai_data->pcm_buf_pos = 0;
+ dai_data->pcm_count = 0;
+ dai_data->pcm_irq_pos = 0;
+ dai_data->pcm_size = 0;
+ dai_data->state = HPCM_CLOSED;
+ hpcm_reset_mixer_config(prtd);
+
+done:
+ mutex_unlock(&prtd->lock);
+ return ret;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ struct hpcm_buf_node *buf_node = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hpcm_drv *prtd = runtime->private_data;
+ struct dai_data *dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+ unsigned long dsp_flags;
+
+ int count = frames_to_bytes(runtime, frames);
+
+ if (dai_data == NULL) {
+ pr_err("%s, dai_data is null\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_interruptible_timeout(dai_data->queue_wait,
+ (!list_empty(&dai_data->free_queue) ||
+ dai_data->state == HPCM_STOPPED),
+ 1 * HZ);
+ if (ret > 0) {
+ if (count <= HPCM_MAX_VOC_PKT_SIZE) {
+ spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+ buf_node =
+ list_first_entry(&dai_data->free_queue,
+ struct hpcm_buf_node, list);
+ list_del(&buf_node->list);
+ spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+ ret = copy_from_user(&buf_node->frame.voc_pkt, buf,
+ count);
+ buf_node->frame.len = count;
+ spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+ list_add_tail(&buf_node->list, &dai_data->filled_queue);
+ spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+ } else {
+ pr_err("%s: Write cnt %d is > HPCM_MAX_VOC_PKT_SIZE\n",
+ __func__, count);
+ ret = -ENOMEM;
+ }
+ } else if (ret == 0) {
+ pr_err("%s: No free Playback buffer\n", __func__);
+ ret = -ETIMEDOUT;
+ } else {
+ pr_err("%s: playback copy was interrupted\n", __func__);
+ }
+
+done:
+ return ret;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff,
+ void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int count = 0;
+ struct hpcm_buf_node *buf_node = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hpcm_drv *prtd = runtime->private_data;
+ struct dai_data *dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+ unsigned long dsp_flags;
+
+ if (dai_data == NULL) {
+ pr_err("%s, dai_data is null\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ count = frames_to_bytes(runtime, frames);
+
+ ret = wait_event_interruptible_timeout(dai_data->queue_wait,
+ (!list_empty(&dai_data->filled_queue) ||
+ dai_data->state == HPCM_STOPPED),
+ 1 * HZ);
+
+ if (ret > 0) {
+ if (count <= HPCM_MAX_VOC_PKT_SIZE) {
+ spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+ buf_node = list_first_entry(&dai_data->filled_queue,
+ struct hpcm_buf_node, list);
+ list_del(&buf_node->list);
+ spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+ ret = copy_to_user(buf, &buf_node->frame.voc_pkt,
+ buf_node->frame.len);
+ if (ret) {
+ pr_err("%s: Copy to user retuned %d\n",
+ __func__, ret);
+ ret = -EFAULT;
+ }
+ spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+ list_add_tail(&buf_node->list, &dai_data->free_queue);
+ spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+
+ } else {
+ pr_err("%s: Read count %d > HPCM_MAX_VOC_PKT_SIZE\n",
+ __func__, count);
+ ret = -ENOMEM;
+ }
+
+ } else if (ret == 0) {
+ pr_err("%s: No Caputre data available\n", __func__);
+ ret = -ETIMEDOUT;
+ } else {
+ pr_err("%s: Read was interrupted\n", __func__);
+ ret = -ERESTARTSYS;
+ }
+
+done:
+ return ret;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int channel,
+ snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, channel,
+ hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, channel,
+ hwoff, buf, frames);
+
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct dai_data *dai_data = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hpcm_drv *prtd = runtime->private_data;
+ snd_pcm_uframes_t ret;
+
+ dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+
+ if (dai_data == NULL) {
+ pr_err("%s, dai_data is null\n", __func__);
+
+ ret = 0;
+ goto done;
+ }
+
+ if (dai_data->pcm_irq_pos >= dai_data->pcm_size)
+ dai_data->pcm_irq_pos = 0;
+
+ ret = bytes_to_frames(runtime, (dai_data->pcm_irq_pos));
+
+done:
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hpcm_drv *prtd = runtime->private_data;
+ struct dai_data *dai_data =
+ hpcm_get_dai_data(substream->pcm->id, prtd);
+
+ if (dai_data == NULL) {
+ pr_err("%s, dai_data is null\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s, %s\n", __func__, substream->pcm->id);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ pr_debug("SNDRV_PCM_TRIGGER_START\n");
+ dai_data->state = HPCM_STARTED;
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ dai_data->state = HPCM_STOPPED;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+done:
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hpcm_drv *prtd = runtime->private_data;
+ struct dai_data *dai_data = NULL;
+ struct tap_point *tp = NULL;
+
+ pr_debug("%s, %s\n", __func__, substream->pcm->id);
+ mutex_lock(&prtd->lock);
+
+ dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+
+ if (dai_data == NULL) {
+ pr_err("%s, dai_data is null\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ dai_data->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ dai_data->pcm_count = snd_pcm_lib_period_bytes(substream);
+ dai_data->pcm_irq_pos = 0;
+ dai_data->pcm_buf_pos = 0;
+ dai_data->state = HPCM_PREPARED;
+
+ /* Register event notify processing callback in prepare instead of
+ * init() as q6voice module's init() can be called at a later point
+ */
+ voc_register_hpcm_evt_cb(hpcm_notify_evt_processing, &hpcm_drv);
+
+ tp = hpcm_get_tappoint_data(substream->pcm->id, prtd);
+ if (tp != NULL) {
+ ret = hpcm_start_vocpcm(substream->pcm->id, prtd, tp);
+ if (ret) {
+ pr_err("error sending start cmd err=%d\n", ret);
+ goto done;
+ }
+ } else {
+ pr_err("%s tp is NULL\n", __func__);
+ }
+done:
+ mutex_unlock(&prtd->lock);
+ return ret;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct hpcm_drv *prtd = (struct hpcm_drv *)runtime->private_data;
+ int ret = 0;
+
+ pr_debug("%s: %s\n", __func__, substream->pcm->id);
+ mutex_lock(&prtd->lock);
+
+ /* Allocate and map voice host PCM ion buffer */
+ if (prtd->session[prtd->mixer_conf.sess_indx].sess_paddr == 0) {
+ ret = hpcm_allocate_shared_memory(prtd);
+ if (ret) {
+ pr_err("error creating shared memory err=%d\n", ret);
+ goto done;
+ }
+
+ ret = hpcm_map_vocpcm_memory(prtd);
+ if (ret) {
+ pr_err("error mapping shared memory err=%d\n", ret);
+ hpcm_free_allocated_mem(prtd);
+ goto done;
+ }
+ } else {
+ pr_debug("%s, VHPCM memory allocation/mapping not performed\n"
+ , __func__);
+ }
+
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+
+ dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max,
+ &dma_buf->addr, GFP_KERNEL);
+
+ if (!dma_buf->area) {
+ pr_err("%s:MSM dma_alloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+
+ hpcm_create_free_queue(dma_buf,
+ hpcm_get_dai_data(substream->pcm->id, prtd));
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+done:
+ mutex_unlock(&prtd->lock);
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hpcm_drv *prtd = &hpcm_drv;
+ struct tappnt_mxr_data *tmd = NULL;
+ struct dai_data *dai_data = NULL;
+ int ret = 0;
+ int tp_val = 0;
+
+ pr_debug("%s, %s\n", __func__, substream->pcm->id);
+ mutex_lock(&prtd->lock);
+
+ dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+
+ if (dai_data == NULL) {
+ pr_err("%s, dai_data is null\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ runtime->hw = msm_pcm_hardware;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_debug("snd_pcm_hw_constraint_list failed\n");
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ pr_debug("snd_pcm_hw_constraint_integer failed\n");
+ goto done;
+ }
+
+ tp_val = get_tappnt_value(substream->pcm->id);
+ tmd = hpcm_get_tappnt_mixer_data(substream->pcm->id, prtd);
+
+ /* Check wheather the kcontrol values set are valid */
+ if (!tmd ||
+ !(tmd->enable) ||
+ !hpcm_is_valid_config(prtd->mixer_conf.sess_indx,
+ tp_val, tmd->direction,
+ tmd->sample_rate)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ dai_data->substream = substream;
+ runtime->private_data = prtd;
+
+done:
+ mutex_unlock(&prtd->lock);
+ return ret;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .hw_params = msm_pcm_hw_params,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .copy = msm_pcm_copy,
+ .close = msm_pcm_close,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+
+ pr_debug("%s:\n", __func__);
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ return 0;
+}
+
+static int msm_pcm_hpcm_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_add_platform_controls(platform, msm_hpcm_controls,
+ ARRAY_SIZE(msm_hpcm_controls));
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+ .probe = msm_pcm_hpcm_probe,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+
+ pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev, &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_voice_host_pcm_dt_match[] = {
+ {.compatible = "qcom,msm-voice-host-pcm"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_voice_host_pcm_dt_match);
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-voice-host-pcm",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_voice_host_pcm_dt_match,
+ },
+ .probe = msm_pcm_probe,
+ .remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ int i = 0;
+ struct session *s = NULL;
+
+ memset(&hpcm_drv, 0, sizeof(hpcm_drv));
+ mutex_init(&hpcm_drv.lock);
+
+ for (i = 0; i < MAX_SESSION; i++) {
+ s = &hpcm_drv.session[i];
+ spin_lock_init(&s->rx_tap_point.capture_dai_data.dsp_lock);
+ spin_lock_init(&s->rx_tap_point.playback_dai_data.dsp_lock);
+ spin_lock_init(&s->tx_tap_point.capture_dai_data.dsp_lock);
+ spin_lock_init(&s->tx_tap_point.playback_dai_data.dsp_lock);
+
+ init_waitqueue_head(
+ &s->rx_tap_point.capture_dai_data.queue_wait);
+ init_waitqueue_head(
+ &s->rx_tap_point.playback_dai_data.queue_wait);
+ init_waitqueue_head(
+ &s->tx_tap_point.capture_dai_data.queue_wait);
+ init_waitqueue_head(
+ &s->tx_tap_point.playback_dai_data.queue_wait);
+
+ INIT_LIST_HEAD(&s->rx_tap_point.capture_dai_data.filled_queue);
+ INIT_LIST_HEAD(&s->rx_tap_point.capture_dai_data.free_queue);
+ INIT_LIST_HEAD(&s->rx_tap_point.playback_dai_data.filled_queue);
+ INIT_LIST_HEAD(&s->rx_tap_point.playback_dai_data.free_queue);
+
+ INIT_LIST_HEAD(&s->tx_tap_point.capture_dai_data.filled_queue);
+ INIT_LIST_HEAD(&s->tx_tap_point.capture_dai_data.free_queue);
+ INIT_LIST_HEAD(&s->tx_tap_point.playback_dai_data.filled_queue);
+ INIT_LIST_HEAD(&s->tx_tap_point.playback_dai_data.free_queue);
+ }
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
new file mode 100644
index 000000000000..24f9740a9511
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
@@ -0,0 +1,1839 @@
+/* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/q6asm-v2.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <asm/dma.h>
+#include <sound/q6audio-v2.h>
+
+#include "msm-pcm-routing-v2.h"
+#include "msm-qti-pp-config.h"
+
+#define LOOPBACK_VOL_MAX_STEPS 0x2000
+#define LOOPBACK_SESSION_MAX 4
+
+static DEFINE_MUTEX(loopback_session_lock);
+static const DECLARE_TLV_DB_LINEAR(loopback_rx_vol_gain, 0,
+ LOOPBACK_VOL_MAX_STEPS);
+
+struct msm_pcm_loopback {
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ int instance;
+
+ struct mutex lock;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+
+ int playback_start;
+ int capture_start;
+ int session_id;
+ struct audio_client *audio_client;
+ uint32_t volume;
+};
+
+struct fe_dai_session_map {
+ char stream_name[32];
+ struct msm_pcm_loopback *loopback_priv;
+};
+
+static struct fe_dai_session_map session_map[LOOPBACK_SESSION_MAX] = {
+ { {}, NULL},
+ { {}, NULL},
+ { {}, NULL},
+ { {}, NULL},
+};
+
+static u32 hfp_tx_mute;
+
+struct msm_pcm_pdata {
+ int perf_mode;
+ struct snd_pcm *pcm_device[MSM_FRONTEND_DAI_MM_SIZE];
+ struct msm_pcm_channel_mixer chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE][2];
+};
+
+static void stop_pcm(struct msm_pcm_loopback *pcm);
+static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd,
+ struct msm_pcm_loopback **pcm);
+
+static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
+ void *priv_data)
+{
+ struct msm_pcm_loopback *pcm = priv_data;
+
+ BUG_ON(!pcm);
+
+ pr_debug("%s: event 0x%x\n", __func__, event);
+
+ switch (event) {
+ case MSM_PCM_RT_EVT_DEVSWITCH:
+ q6asm_cmd(pcm->audio_client, CMD_PAUSE);
+ q6asm_cmd(pcm->audio_client, CMD_FLUSH);
+ q6asm_run(pcm->audio_client, 0, 0, 0);
+ default:
+ pr_err("%s: default event 0x%x\n", __func__, event);
+ break;
+ }
+}
+
+static void msm_pcm_loopback_event_handler(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct msm_pcm_loopback *pcm = priv;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ if (pcm->playback_substream != NULL)
+ substream = pcm->playback_substream;
+ else if (pcm->capture_substream != NULL)
+ substream = pcm->capture_substream;
+
+ switch (opcode) {
+ case ASM_STREAM_PP_EVENT: {
+ pr_debug("%s: ASM_STREAM_EVENT (0x%x)\n", __func__, opcode);
+ if (!substream) {
+ pr_err("%s: substream is NULL.\n", __func__);
+ return;
+ }
+
+ rtd = substream->private_data;
+ if (!rtd) {
+ pr_err("%s: rtd is NULL\n", __func__);
+ return;
+ }
+
+ ret = msm_adsp_inform_mixer_ctl(rtd, payload);
+ if (ret) {
+ pr_err("%s: failed to inform mixer ctl. err = %d\n",
+ __func__, ret);
+ return;
+ }
+
+ break;
+ }
+
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_STREAM_CMD_REGISTER_PP_EVENTS:
+ pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS:",
+ __func__);
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ pr_err("%s: Not Supported Event opcode[0x%x]\n",
+ __func__, opcode);
+ break;
+ }
+}
+
+static int msm_loopback_session_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hfp_tx_mute;
+ return 0;
+}
+
+static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0, n = 0;
+ int mute = ucontrol->value.integer.value[0];
+ struct msm_pcm_loopback *pcm = NULL;
+
+ if ((mute < 0) || (mute > 1)) {
+ pr_err(" %s Invalid arguments", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&loopback_session_lock);
+ pr_debug("%s: mute=%d\n", __func__, mute);
+ hfp_tx_mute = mute;
+ for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
+ if (!strcmp(session_map[n].stream_name, "MultiMedia6"))
+ pcm = session_map[n].loopback_priv;
+ }
+ if (pcm && pcm->audio_client) {
+ ret = q6asm_set_mute(pcm->audio_client, mute);
+ if (ret < 0)
+ pr_err("%s: Send mute command failed rc=%d\n",
+ __func__, ret);
+ }
+ mutex_unlock(&loopback_session_lock);
+done:
+ return ret;
+}
+
+static struct snd_kcontrol_new msm_loopback_controls[] = {
+ SOC_SINGLE_EXT("HFP TX Mute", SND_SOC_NOPM, 0, 1, 0,
+ msm_loopback_session_mute_get,
+ msm_loopback_session_mute_put),
+};
+
+static int msm_pcm_loopback_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_add_platform_controls(platform, msm_loopback_controls,
+ ARRAY_SIZE(msm_loopback_controls));
+
+ return 0;
+}
+static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd,
+ uint32_t volume)
+{
+ int rc = -EINVAL;
+
+ pr_debug("%s: Setting volume 0x%x\n", __func__, volume);
+
+ if (prtd && prtd->audio_client) {
+ rc = q6asm_set_volume(prtd->audio_client, volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ prtd->volume = volume;
+ }
+ return rc;
+}
+
+static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd,
+ struct msm_pcm_loopback **pcm)
+{
+ int ret = 0;
+ int n, index = -1;
+
+ dev_dbg(rtd->platform->dev, "%s: stream %s\n", __func__,
+ rtd->dai_link->stream_name);
+
+ mutex_lock(&loopback_session_lock);
+ for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
+ if (!strcmp(rtd->dai_link->stream_name,
+ session_map[n].stream_name)) {
+ *pcm = session_map[n].loopback_priv;
+ goto exit;
+ }
+ /*
+ * Store the min index value for allocating a new session.
+ * Here, if session stream name is not found in the
+ * existing entries after the loop iteration, then this
+ * index will be used to allocate the new session.
+ * This index variable is expected to point to the topmost
+ * available free session.
+ */
+ if (!(session_map[n].stream_name[0]) && (index < 0))
+ index = n;
+ }
+
+ if (index < 0) {
+ dev_err(rtd->platform->dev, "%s: Max Sessions allocated\n",
+ __func__);
+ ret = -EAGAIN;
+ goto exit;
+ }
+
+ session_map[index].loopback_priv = kzalloc(
+ sizeof(struct msm_pcm_loopback), GFP_KERNEL);
+ if (!session_map[index].loopback_priv) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ strlcpy(session_map[index].stream_name,
+ rtd->dai_link->stream_name,
+ sizeof(session_map[index].stream_name));
+ dev_dbg(rtd->platform->dev, "%s: stream %s index %d\n",
+ __func__, session_map[index].stream_name, index);
+
+ mutex_init(&session_map[index].loopback_priv->lock);
+ *pcm = session_map[index].loopback_priv;
+exit:
+ mutex_unlock(&loopback_session_lock);
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct msm_pcm_loopback *pcm = NULL;
+ int ret = 0;
+ uint16_t bits_per_sample = 16;
+ struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window;
+ uint32_t param_id;
+ struct msm_pcm_pdata *pdata;
+
+ ret = msm_pcm_loopback_get_session(rtd, &pcm);
+ if (ret)
+ return ret;
+
+ mutex_lock(&pcm->lock);
+
+ pcm->volume = 0x2000;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pcm->playback_substream = substream;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm->capture_substream = substream;
+
+ pcm->instance++;
+ dev_dbg(rtd->platform->dev, "%s: pcm out open: %d,%d\n", __func__,
+ pcm->instance, substream->stream);
+ if (pcm->instance == 2) {
+ if (pcm->audio_client != NULL)
+ stop_pcm(pcm);
+
+ pdata = (struct msm_pcm_pdata *)
+ dev_get_drvdata(rtd->platform->dev);
+ if (!pdata) {
+ dev_err(rtd->platform->dev,
+ "%s: platform data not populated\n", __func__);
+ mutex_unlock(&pcm->lock);
+ return -EINVAL;
+ }
+
+ pcm->audio_client = q6asm_audio_client_alloc(
+ (app_cb)msm_pcm_loopback_event_handler, pcm);
+ if (!pcm->audio_client) {
+ dev_err(rtd->platform->dev,
+ "%s: Could not allocate memory\n", __func__);
+ mutex_unlock(&pcm->lock);
+ return -ENOMEM;
+ }
+
+ pcm->audio_client->perf_mode = pdata->perf_mode;
+ ret = q6asm_open_loopback_with_retry(pcm->audio_client,
+ bits_per_sample);
+ pcm->session_id = pcm->audio_client->session;
+
+ if (ret < 0) {
+ dev_err(rtd->platform->dev,
+ "%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(pcm->audio_client);
+ mutex_unlock(&pcm->lock);
+ return -ENOMEM;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ pcm->playback_substream = substream;
+ ret = pcm_loopback_set_volume(pcm, pcm->volume);
+ if (ret < 0)
+ dev_err(rtd->platform->dev,
+ "Error %d setting volume", ret);
+ }
+ /* Set to largest negative value */
+ asm_mtmx_strtr_window.window_lsw = 0x00000000;
+ asm_mtmx_strtr_window.window_msw = 0x80000000;
+ param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2;
+ q6asm_send_mtmx_strtr_window(pcm->audio_client,
+ &asm_mtmx_strtr_window,
+ param_id);
+ /* Set to largest positive value */
+ asm_mtmx_strtr_window.window_lsw = 0xffffffff;
+ asm_mtmx_strtr_window.window_msw = 0x7fffffff;
+ param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2;
+ q6asm_send_mtmx_strtr_window(pcm->audio_client,
+ &asm_mtmx_strtr_window,
+ param_id);
+ }
+ dev_info(rtd->platform->dev, "%s: Instance = %d, Stream ID = %s\n",
+ __func__, pcm->instance, substream->pcm->id);
+ runtime->private_data = pcm;
+
+ msm_adsp_init_mixer_ctl_pp_event_queue(substream->private_data);
+
+ mutex_unlock(&pcm->lock);
+
+ return 0;
+}
+
+static void stop_pcm(struct msm_pcm_loopback *pcm)
+{
+ struct snd_soc_pcm_runtime *soc_pcm_rx;
+ struct snd_soc_pcm_runtime *soc_pcm_tx;
+
+ if (pcm->audio_client == NULL)
+ return;
+
+ mutex_lock(&loopback_session_lock);
+ q6asm_cmd(pcm->audio_client, CMD_CLOSE);
+
+ if (pcm->playback_substream != NULL) {
+ soc_pcm_rx = pcm->playback_substream->private_data;
+ msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+ if (pcm->capture_substream != NULL) {
+ soc_pcm_tx = pcm->capture_substream->private_data;
+ msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ }
+ q6asm_audio_client_free(pcm->audio_client);
+ pcm->audio_client = NULL;
+ mutex_unlock(&loopback_session_lock);
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm_loopback *pcm = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ int ret = 0, n;
+ bool found = false;
+
+ mutex_lock(&pcm->lock);
+
+ dev_dbg(rtd->platform->dev, "%s: end pcm call:%d\n",
+ __func__, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pcm->playback_start = 0;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm->capture_start = 0;
+
+ pcm->instance--;
+ if (!pcm->playback_start || !pcm->capture_start) {
+ dev_dbg(rtd->platform->dev, "%s: end pcm call\n", __func__);
+ stop_pcm(pcm);
+ }
+
+ if (!pcm->instance) {
+ mutex_lock(&loopback_session_lock);
+ for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
+ if (!strcmp(rtd->dai_link->stream_name,
+ session_map[n].stream_name)) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ memset(session_map[n].stream_name, 0,
+ sizeof(session_map[n].stream_name));
+ mutex_unlock(&pcm->lock);
+ mutex_destroy(&session_map[n].loopback_priv->lock);
+ session_map[n].loopback_priv = NULL;
+ kfree(pcm);
+ dev_dbg(rtd->platform->dev, "%s: stream freed %s\n",
+ __func__, rtd->dai_link->stream_name);
+ mutex_unlock(&loopback_session_lock);
+ return 0;
+ }
+ mutex_unlock(&loopback_session_lock);
+ }
+ msm_adsp_clean_mixer_ctl_pp_event_queue(substream->private_data);
+ mutex_unlock(&pcm->lock);
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm_loopback *pcm = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct msm_pcm_routing_evt event;
+
+ mutex_lock(&pcm->lock);
+
+ dev_dbg(rtd->platform->dev, "%s: ASM loopback stream:%d\n",
+ __func__, substream->stream);
+
+ if (pcm->playback_start && pcm->capture_start) {
+ mutex_unlock(&pcm->lock);
+ return ret;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!pcm->playback_start)
+ pcm->playback_start = 1;
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (!pcm->capture_start)
+ pcm->capture_start = 1;
+ }
+
+ if (pcm->playback_start && pcm->capture_start) {
+ struct snd_soc_pcm_runtime *soc_pcm_rx =
+ pcm->playback_substream->private_data;
+ struct snd_soc_pcm_runtime *soc_pcm_tx =
+ pcm->capture_substream->private_data;
+ event.event_func = msm_pcm_route_event_handler;
+ event.priv_data = (void *) pcm;
+ msm_pcm_routing_reg_phy_stream(soc_pcm_tx->dai_link->be_id,
+ pcm->audio_client->perf_mode,
+ pcm->session_id, pcm->capture_substream->stream);
+ msm_pcm_routing_reg_phy_stream_v2(soc_pcm_rx->dai_link->be_id,
+ pcm->audio_client->perf_mode,
+ pcm->session_id, pcm->playback_substream->stream,
+ event);
+ }
+
+ mutex_unlock(&pcm->lock);
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm_loopback *pcm = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dev_dbg(rtd->platform->dev,
+ "%s: playback_start:%d,capture_start:%d\n", __func__,
+ pcm->playback_start, pcm->capture_start);
+ if (pcm->playback_start && pcm->capture_start)
+ q6asm_run_nowait(pcm->audio_client, 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ dev_dbg(rtd->platform->dev,
+ "%s:Pause/Stop - playback_start:%d,capture_start:%d\n",
+ __func__, pcm->playback_start, pcm->capture_start);
+ if (pcm->playback_start && pcm->capture_start)
+ q6asm_cmd_nowait(pcm->audio_client, CMD_PAUSE);
+ break;
+ default:
+ pr_err("%s: default cmd %d\n", __func__, cmd);
+ break;
+ }
+
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .close = msm_pcm_close,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+};
+
+static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct snd_pcm_volume *vol = kcontrol->private_data;
+ struct snd_pcm_substream *substream = NULL;
+ struct msm_pcm_loopback *prtd;
+ int volume = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: volume : 0x%x\n", __func__, volume);
+
+ if (!vol) {
+ pr_err("%s: vol is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!vol->pcm) {
+ pr_err("%s: vol->pcm is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ substream = vol->pcm->streams[0].substream;
+ mutex_lock(&loopback_session_lock);
+ if ((!substream) || (!substream->runtime)) {
+ pr_err("%s substream or runtime not found\n", __func__);
+ rc = -ENODEV;
+ goto exit;
+ }
+ prtd = substream->runtime->private_data;
+ if (!prtd) {
+ rc = -ENODEV;
+ mutex_unlock(&loopback_session_lock);
+ goto exit;
+ }
+ rc = pcm_loopback_set_volume(prtd, volume);
+ mutex_unlock(&loopback_session_lock);
+
+exit:
+ return rc;
+}
+
+static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+ struct snd_pcm_substream *substream =
+ vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ struct msm_pcm_loopback *prtd;
+
+ pr_debug("%s\n", __func__);
+
+ if (!vol) {
+ pr_err("%s: vol is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!vol->pcm) {
+ pr_err("%s: vol->pcm is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ substream = vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+
+ if ((!substream) || (!substream->runtime)) {
+ pr_err("%s substream or runtime not found\n", __func__);
+ rc = -ENODEV;
+ goto exit;
+ }
+ mutex_lock(&loopback_session_lock);
+ prtd = substream->runtime->private_data;
+ if (!prtd) {
+ rc = -ENODEV;
+ mutex_unlock(&loopback_session_lock);
+ goto exit;
+ }
+ ucontrol->value.integer.value[0] = prtd->volume;
+ mutex_unlock(&loopback_session_lock);
+
+exit:
+ return rc;
+}
+
+static int msm_pcm_add_volume_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
+ struct snd_pcm_volume *volume_info;
+ struct snd_kcontrol *kctl;
+ int ret = 0;
+
+ dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__);
+ ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 1,
+ rtd->dai_link->be_id,
+ &volume_info);
+ if (ret < 0)
+ return ret;
+ kctl = volume_info->kctl;
+ kctl->put = msm_pcm_volume_ctl_put;
+ kctl->get = msm_pcm_volume_ctl_get;
+ kctl->tlv.p = loopback_rx_vol_gain;
+ return 0;
+}
+
+static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = ucontrol->value.integer.value[3];
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
+ int ret = 0;
+
+ cfg_data.app_type = ucontrol->value.integer.value[0];
+ cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
+ if (ucontrol->value.integer.value[2] != 0)
+ cfg_data.sample_rate = ucontrol->value.integer.value[2];
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, &cfg_data);
+ if (ret < 0)
+ pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = 0;
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ &be_id, &cfg_data);
+ if (ret < 0) {
+ pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[0] = cfg_data.app_type;
+ ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
+ ucontrol->value.integer.value[2] = cfg_data.sample_rate;
+ ucontrol->value.integer.value[3] = be_id;
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
+
+done:
+ return ret;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = ucontrol->value.integer.value[3];
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
+ int ret = 0;
+
+ cfg_data.app_type = ucontrol->value.integer.value[0];
+ cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
+ if (ucontrol->value.integer.value[2] != 0)
+ cfg_data.sample_rate = ucontrol->value.integer.value[2];
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, &cfg_data);
+ if (ret < 0)
+ pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = 0;
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ &be_id, &cfg_data);
+ if (ret < 0) {
+ pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[0] = cfg_data.app_type;
+ ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
+ ucontrol->value.integer.value[2] = cfg_data.sample_rate;
+ ucontrol->value.integer.value[3] = be_id;
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
+done:
+ return ret;
+}
+
+static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
+ struct snd_pcm_usr *app_type_info;
+ struct snd_kcontrol *kctl;
+ const char *playback_mixer_ctl_name = "Audio Stream";
+ const char *capture_mixer_ctl_name = "Audio Stream Capture";
+ const char *deviceNo = "NN";
+ const char *suffix = "App Type Cfg";
+ int ctl_len, ret = 0;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ pr_debug("%s: Playback app type cntrl add\n", __func__);
+ ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 1, ctl_len, rtd->dai_link->be_id,
+ &app_type_info);
+ if (ret < 0)
+ return ret;
+ kctl = app_type_info->kctl;
+ snprintf(kctl->id.name, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ kctl->put = msm_pcm_playback_app_type_cfg_ctl_put;
+ kctl->get = msm_pcm_playback_app_type_cfg_ctl_get;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ pr_debug("%s: Capture app type cntrl add\n", __func__);
+ ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ NULL, 1, ctl_len, rtd->dai_link->be_id,
+ &app_type_info);
+ if (ret < 0)
+ return ret;
+ kctl = app_type_info->kctl;
+ snprintf(kctl->id.name, ctl_len, "%s %d %s",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix);
+ kctl->put = msm_pcm_capture_app_type_cfg_ctl_put;
+ kctl->get = msm_pcm_capture_app_type_cfg_ctl_get;
+ }
+
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int ret = 0;
+ int stream_id = 0;
+ int be_id = 0;
+ struct msm_pcm_loopback *prtd = NULL;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(platform->dev);
+ struct snd_pcm *pcm = NULL;
+ struct snd_pcm_substream *substream = NULL;
+ struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ pcm = pdata->pcm_device[fe_id];
+ if (!pcm) {
+ pr_err("%s invalid pcm handle for fe_id %llu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (session_type == SESSION_TYPE_RX)
+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ else
+ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ chmixer_pspd->enable = ucontrol->value.integer.value[0];
+ chmixer_pspd->rule = ucontrol->value.integer.value[1];
+ chmixer_pspd->input_channel = ucontrol->value.integer.value[2];
+ chmixer_pspd->output_channel = ucontrol->value.integer.value[3];
+ chmixer_pspd->port_idx = ucontrol->value.integer.value[4];
+
+ /* cache value and take effect during adm_open stage */
+ msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+ session_type,
+ chmixer_pspd);
+
+ if (substream->runtime) {
+ prtd = substream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s find invalid prtd fail\n", __func__);
+ return -EINVAL;
+ }
+
+ if (chmixer_pspd->enable) {
+ stream_id = prtd->audio_client->session;
+ be_id = chmixer_pspd->port_idx;
+ msm_pcm_routing_set_channel_mixer_runtime(be_id,
+ stream_id,
+ session_type,
+ chmixer_pspd);
+ }
+ }
+done:
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ ucontrol->value.integer.value[0] = chmixer_pspd->enable;
+ ucontrol->value.integer.value[1] = chmixer_pspd->rule;
+ ucontrol->value.integer.value[2] = chmixer_pspd->input_channel;
+ ucontrol->value.integer.value[3] = chmixer_pspd->output_channel;
+ ucontrol->value.integer.value[4] = chmixer_pspd->port_idx;
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ chmixer_pspd->out_ch_map[i] =
+ ucontrol->value.integer.value[i];
+
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->out_ch_map[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ chmixer_pspd->in_ch_map[i] = ucontrol->value.integer.value[i];
+
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->in_ch_map[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int channel = (kcontrol->private_value >> 16) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V2) {
+ pr_err("%s: invalid channel number %d\n", __func__, channel);
+ return -EINVAL;
+ }
+
+ channel--;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ chmixer_pspd->channel_weight[channel][i] =
+ ucontrol->value.integer.value[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int channel = (kcontrol->private_value >> 16) & 0xFF;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(platform->dev);
+ int i = 0;
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V2) {
+ pr_err("%s: invalid channel number %d\n", __func__, channel);
+ return -EINVAL;
+ }
+
+ channel--;
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->channel_weight[channel][i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 32;
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 64;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_output_map_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *deviceNo = "NN";
+ const char *suffix = "ChMixer Output Map";
+ int ctl_len = 0;
+ int session_type = 0;
+ char *playback_mixer_str = NULL;
+ char *capture_mixer_str = NULL;
+ int ret = 0;
+ struct snd_kcontrol_new channel_mixer_output_map_control[2] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_output_map_info,
+ .put = msm_pcm_channel_mixer_output_map_ctl_put,
+ .get = msm_pcm_channel_mixer_output_map_ctl_get,
+ .private_value = 0,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_output_map_info,
+ .put = msm_pcm_channel_mixer_output_map_ctl_put,
+ .get = msm_pcm_channel_mixer_output_map_ctl_get,
+ .private_value = 0,
+ }
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ playback_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (playback_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_RX;
+ snprintf(playback_mixer_str, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_output_map_control[0].name = playback_mixer_str;
+ channel_mixer_output_map_control[0].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_output_map_control[0],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ capture_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (capture_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_TX;
+ snprintf(capture_mixer_str, ctl_len, "%s %d %s",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_output_map_control[1].name = capture_mixer_str;
+ channel_mixer_output_map_control[1].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_output_map_control[1],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ kfree(playback_mixer_str);
+ kfree(capture_mixer_str);
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 32;
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 64;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_input_map_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *deviceNo = "NN";
+ const char *suffix = "ChMixer Input Map";
+ int ctl_len = 0;
+ int session_type = 0;
+ char *playback_mixer_str = NULL;
+ char *capture_mixer_str = NULL;
+ int ret = 0;
+ struct snd_kcontrol_new channel_mixer_input_map_control[2] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_input_map_info,
+ .put = msm_pcm_channel_mixer_input_map_ctl_put,
+ .get = msm_pcm_channel_mixer_input_map_ctl_get,
+ .private_value = 0,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_input_map_info,
+ .put = msm_pcm_channel_mixer_input_map_ctl_put,
+ .get = msm_pcm_channel_mixer_input_map_ctl_get,
+ .private_value = 0,
+ }
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ playback_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (playback_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_RX;
+ snprintf(playback_mixer_str, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_input_map_control[0].name = playback_mixer_str;
+ channel_mixer_input_map_control[0].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_input_map_control[0],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ capture_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (capture_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_TX;
+ snprintf(capture_mixer_str, ctl_len, "%s %d %s",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_input_map_control[1].name = capture_mixer_str;
+ channel_mixer_input_map_control[1].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_input_map_control[1],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ kfree(playback_mixer_str);
+ kfree(capture_mixer_str);
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 5;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_cfg_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *deviceNo = "NN";
+ const char *suffix = "ChMixer Cfg";
+ int ctl_len = 0;
+ char *playback_mixer_str = NULL;
+ char *capture_mixer_str = NULL;
+ int session_type = 0;
+ int ret = 0;
+ struct msm_pcm_pdata *pdata = NULL;
+ struct snd_kcontrol_new channel_mixer_cfg_control[2] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_cfg_info,
+ .put = msm_pcm_channel_mixer_cfg_ctl_put,
+ .get = msm_pcm_channel_mixer_cfg_ctl_get,
+ .private_value = 0,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_cfg_info,
+ .put = msm_pcm_channel_mixer_cfg_ctl_put,
+ .get = msm_pcm_channel_mixer_cfg_ctl_get,
+ .private_value = 0,
+ }
+ };
+
+ pdata = (struct msm_pcm_pdata *)
+ dev_get_drvdata(rtd->platform->dev);
+ if (pdata == NULL) {
+ pr_err("%s: platform data not populated\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pdata->pcm_device[rtd->dai_link->be_id] = rtd->pcm;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ playback_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (playback_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_RX;
+ snprintf(playback_mixer_str, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_cfg_control[0].name = playback_mixer_str;
+ channel_mixer_cfg_control[0].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_cfg_control[0],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ capture_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (capture_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_TX;
+ snprintf(capture_mixer_str, ctl_len, "%s %d %s",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_cfg_control[1].name = capture_mixer_str;
+ channel_mixer_cfg_control[1].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_cfg_control[1],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ kfree(playback_mixer_str);
+ kfree(capture_mixer_str);
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_weight_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 32;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0x4000;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_weight_controls(
+ struct snd_soc_pcm_runtime *rtd,
+ int channel)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *deviceNo = "NN";
+ const char *channelNo = "NN";
+ const char *suffix = "ChMixer Weight Ch";
+ int ctl_len = 0;
+ int session_type = 0;
+ char *playback_mixer_str = NULL;
+ char *capture_mixer_str = NULL;
+ int ret = 0;
+ struct snd_kcontrol_new channel_mixer_weight_control[2] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_weight_info,
+ .put = msm_pcm_channel_mixer_weight_ctl_put,
+ .get = msm_pcm_channel_mixer_weight_ctl_get,
+ .private_value = 0,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_weight_info,
+ .put = msm_pcm_channel_mixer_weight_ctl_put,
+ .get = msm_pcm_channel_mixer_weight_ctl_get,
+ .private_value = 0,
+ }
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1 +
+ strlen(channelNo) + 1;
+ playback_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (playback_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_RX;
+ snprintf(playback_mixer_str, ctl_len, "%s %d %s %d",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix,
+ channel);
+ channel_mixer_weight_control[0].name = playback_mixer_str;
+ channel_mixer_weight_control[0].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8)
+ | (channel << 16);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_weight_control[0],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1 +
+ strlen(channelNo) + 1;
+ capture_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (capture_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_TX;
+ snprintf(capture_mixer_str, ctl_len, "%s %d %s %d",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix,
+ channel);
+ channel_mixer_weight_control[1].name = capture_mixer_str;
+ channel_mixer_weight_control[1].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8)
+ | (channel << 16);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_weight_control[1],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ kfree(playback_mixer_str);
+ kfree(capture_mixer_str);
+ return ret;
+}
+
+static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ int i, ret = 0;
+
+ ret = msm_pcm_add_channel_mixer_cfg_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add channel mixer cfg controls failed:%d\n",
+ __func__, ret);
+ ret = msm_pcm_add_channel_mixer_input_map_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add channel mixer input map controls failed:%d\n",
+ __func__, ret);
+ ret = msm_pcm_add_channel_mixer_output_map_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add channel mixer output map controls failed:%d\n",
+ __func__, ret);
+
+ for (i = 1; i <= PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ ret |= msm_pcm_add_channel_mixer_weight_controls(rtd, i);
+ if (ret)
+ pr_err("%s: pcm add channel mixer weight controls failed:%d\n",
+ __func__, ret);
+ return ret;
+}
+
+static int msm_loopback_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct msm_adsp_event_data *event_data = NULL;
+ uint64_t actual_payload_len = 0;
+ struct audio_client *audio_client = NULL;
+ struct msm_pcm_routing_fdai_data fe_dai;
+ unsigned long fe_id = kcontrol->private_value;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received invalid fe_id %lu\n", __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ msm_pcm_routing_get_fedai_info(fe_id, SESSION_TYPE_RX, &fe_dai);
+ audio_client = q6asm_get_audio_client(fe_dai.strm_id);
+
+ event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data;
+ if ((event_data->event_type < ADSP_STREAM_PP_EVENT) ||
+ (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) {
+ pr_err("%s: invalid event_type=%d",
+ __func__, event_data->event_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ actual_payload_len = sizeof(struct msm_adsp_event_data) +
+ event_data->payload_len;
+ if (actual_payload_len >= U32_MAX) {
+ pr_err("%s payload length 0x%X exceeds limit",
+ __func__, event_data->payload_len);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (event_data->payload_len > sizeof(ucontrol->value.bytes.data)
+ - sizeof(struct msm_adsp_event_data)) {
+ pr_err("%s param length=%d exceeds limit",
+ __func__, event_data->payload_len);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = q6asm_send_stream_cmd(audio_client, event_data);
+ if (ret < 0)
+ pr_err("%s: failed to send stream event cmd, err = %d\n",
+ __func__, ret);
+done:
+
+ return ret;
+}
+
+static int msm_pcm_add_audio_adsp_stream_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CMD;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_audio_adsp_stream_cmd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_loopback_adsp_stream_cmd_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s rtd is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_audio_adsp_stream_cmd_config_control[0].name = mixer_str;
+ fe_audio_adsp_stream_cmd_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s\n", mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_audio_adsp_stream_cmd_config_control,
+ ARRAY_SIZE(fe_audio_adsp_stream_cmd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_pcm_add_audio_adsp_stream_callback_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol *kctl;
+
+ struct snd_kcontrol_new fe_audio_adsp_callback_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_callback_info,
+ .get = msm_adsp_stream_callback_get,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: added new pcm FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_audio_adsp_callback_config_control[0].name = mixer_str;
+ fe_audio_adsp_callback_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_audio_adsp_callback_config_control,
+ ARRAY_SIZE(fe_audio_adsp_callback_config_control));
+ if (ret < 0) {
+ pr_err("%s: failed to add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl->private_data = NULL;
+
+free_mixer_str:
+ kfree(mixer_str);
+done:
+ return ret;
+}
+static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ ret = msm_pcm_add_volume_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add volume controls failed:%d\n",
+ __func__, ret);
+ ret = msm_pcm_add_app_type_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add app type controls failed:%d\n",
+ __func__, ret);
+
+ ret = msm_pcm_add_channel_mixer_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add channel mixer controls failed:%d\n",
+ __func__, ret);
+
+ ret = msm_pcm_add_audio_adsp_stream_cmd_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm ADSP Stream Cmd Control\n",
+ __func__);
+
+ ret = msm_pcm_add_audio_adsp_stream_callback_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm ADSP Stream Callback Control\n",
+ __func__);
+ return ret;
+}
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = msm_pcm_add_controls(rtd);
+ if (ret)
+ dev_err(rtd->dev, "%s, kctl add failed\n", __func__);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+ .probe = msm_pcm_loopback_probe,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+ struct msm_pcm_pdata *pdata;
+
+ dev_dbg(&pdev->dev, "%s: dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+
+ pdata = kzalloc(sizeof(struct msm_pcm_pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (of_property_read_bool(pdev->dev.of_node,
+ "qcom,msm-pcm-loopback-low-latency"))
+ pdata->perf_mode = LOW_LATENCY_PCM_MODE;
+ else
+ pdata->perf_mode = LEGACY_PCM_MODE;
+
+ dev_set_drvdata(&pdev->dev, pdata);
+
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_pcm_loopback_dt_match[] = {
+ {.compatible = "qcom,msm-pcm-loopback"},
+ {}
+};
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-loopback",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_pcm_loopback_dt_match,
+ },
+ .probe = msm_pcm_probe,
+ .remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM loopback platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
new file mode 100644
index 000000000000..d0eb0381bd81
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
@@ -0,0 +1,1363 @@
+/* Copyright (c) 2016-2017, 2019-2020 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6audio-v2.h>
+#include <sound/timer.h>
+#include <sound/hwdep.h>
+
+#include <asm/dma.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+#include <sound/devdep_params.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+
+#define PCM_MASTER_VOL_MAX_STEPS 0x2000
+static const DECLARE_TLV_DB_LINEAR(msm_pcm_vol_gain, 0,
+ PCM_MASTER_VOL_MAX_STEPS);
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+#define CMD_EOS_MIN_TIMEOUT_LENGTH 50
+#define CMD_EOS_TIMEOUT_MULTIPLIER (HZ * 50)
+
+#if defined(CONFIG_TRACING) && defined(DEBUG)
+#define msm_trace_printk(...) trace_printk(__VA_ARGS__)
+#else
+#define msm_trace_printk(...)
+#endif
+
+#define ATRACE_END() \
+ msm_trace_printk("tracing_mark_write: E\n")
+#define ATRACE_BEGIN(name) \
+ msm_trace_printk("tracing_mark_write: B|%d|%s\n", current->tgid, name)
+#define ATRACE_FUNC() ATRACE_BEGIN(__func__)
+#define ATRACE_INT(name, value) \
+ msm_trace_printk("tracing_mark_write: C|%d|%s|%d\n", \
+ current->tgid, name, (int)(value))
+
+#define SIO_PLAYBACK_MAX_PERIOD_SIZE PLAYBACK_MAX_PERIOD_SIZE
+#define SIO_PLAYBACK_MIN_PERIOD_SIZE 48
+#define SIO_PLAYBACK_MAX_NUM_PERIODS 512
+#define SIO_PLAYBACK_MIN_NUM_PERIODS PLAYBACK_MIN_NUM_PERIODS
+#define SIO_PLAYBACK_MIN_BYTES (SIO_PLAYBACK_MIN_NUM_PERIODS * \
+ SIO_PLAYBACK_MIN_PERIOD_SIZE)
+
+#define SIO_PLAYBACK_MAX_BYTES ((SIO_PLAYBACK_MAX_NUM_PERIODS) * \
+ (SIO_PLAYBACK_MAX_PERIOD_SIZE))
+
+#define SIO_CAPTURE_MAX_PERIOD_SIZE CAPTURE_MAX_PERIOD_SIZE
+#define SIO_CAPTURE_MIN_PERIOD_SIZE 48
+#define SIO_CAPTURE_MAX_NUM_PERIODS 512
+#define SIO_CAPTURE_MIN_NUM_PERIODS CAPTURE_MIN_NUM_PERIODS
+
+#define SIO_CAPTURE_MIN_BYTES (SIO_CAPTURE_MIN_NUM_PERIODS * \
+ SIO_CAPTURE_MIN_PERIOD_SIZE)
+
+#define SIO_CAPTURE_MAX_BYTES (SIO_CAPTURE_MAX_NUM_PERIODS * \
+ SIO_CAPTURE_MAX_PERIOD_SIZE)
+
+static struct snd_pcm_hardware msm_pcm_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE),
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = SIO_PLAYBACK_MAX_NUM_PERIODS *
+ SIO_PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = SIO_PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = SIO_PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = SIO_PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = SIO_PLAYBACK_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE),
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 4,
+ .buffer_bytes_max = SIO_CAPTURE_MAX_NUM_PERIODS *
+ SIO_CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = SIO_CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = SIO_CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = SIO_CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = SIO_CAPTURE_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+ 88200, 96000, 176400, 192000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static unsigned long msm_pcm_fe_topology[MSM_FRONTEND_DAI_MAX];
+
+/* default value is DTS (i.e read from device tree) */
+static char const *msm_pcm_fe_topology_text[] = {
+ "DTS", "ULL", "ULL_PP", "LL" };
+
+static const struct soc_enum msm_pcm_fe_topology_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(msm_pcm_fe_topology_text),
+ msm_pcm_fe_topology_text),
+};
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ uint32_t *ptrmem = (uint32_t *)payload;
+
+ switch (opcode) {
+ case ASM_DATA_EVENT_WATERMARK:
+ pr_debug("%s: Watermark level = 0x%08x\n", __func__, *ptrmem);
+ break;
+ case APR_BASIC_RSP_RESULT:
+ pr_debug("%s: Payload = [0x%x]stat[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2:
+ case ASM_SESSION_CMD_PAUSE:
+ case ASM_STREAM_CMD_FLUSH:
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd;
+ int ret = 0;
+
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ prtd->substream = substream;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = msm_pcm_hardware_playback;
+ else
+ runtime->hw = msm_pcm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret)
+ pr_info("snd_pcm_hw_constraint_list failed\n");
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret)
+ pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ SIO_PLAYBACK_MIN_BYTES,
+ SIO_PLAYBACK_MAX_BYTES);
+ if (ret) {
+ pr_info("%s: P buffer bytes minmax constraint ret %d\n",
+ __func__, ret);
+ }
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ SIO_CAPTURE_MIN_BYTES,
+ SIO_CAPTURE_MAX_BYTES);
+ if (ret) {
+ pr_info("%s: C buffer bytes minmax constraint ret %d\n",
+ __func__, ret);
+ }
+ }
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ if (ret) {
+ pr_err("%s: Constraint for period bytes step ret = %d\n",
+ __func__, ret);
+ }
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+ if (ret) {
+ pr_err("%s: Constraint for buffer bytes step ret = %d\n",
+ __func__, ret);
+ }
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_err("%s: client alloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto fail_cmd;
+ }
+ prtd->dsp_cnt = 0;
+ prtd->set_channel_map = false;
+ runtime->private_data = prtd;
+ return 0;
+
+fail_cmd:
+ kfree(prtd);
+ return ret;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ struct msm_plat_data *pdata;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ struct shared_io_config config;
+ uint16_t sample_word_size;
+ uint16_t bits_per_sample;
+ int ret;
+ int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? IN : OUT;
+ unsigned long topology;
+ int perf_mode;
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ ret = -EINVAL;
+ pr_err("%s: platform data not populated ret: %d\n", __func__,
+ ret);
+ return ret;
+ }
+
+ topology = msm_pcm_fe_topology[soc_prtd->dai_link->be_id];
+
+ if (!strcmp(msm_pcm_fe_topology_text[topology], "ULL_PP"))
+ perf_mode = ULL_POST_PROCESSING_PCM_MODE;
+ else if (!strcmp(msm_pcm_fe_topology_text[topology], "ULL"))
+ perf_mode = ULTRA_LOW_LATENCY_PCM_MODE;
+ else if (!strcmp(msm_pcm_fe_topology_text[topology], "LL"))
+ perf_mode = LOW_LATENCY_PCM_MODE;
+ else
+ /* use the default from the device tree */
+ perf_mode = pdata->perf_mode;
+
+
+ /* need to set LOW_LATENCY_PCM_MODE for capture since
+ * push mode does not support ULL
+ */
+ prtd->audio_client->perf_mode = (dir == IN) ?
+ perf_mode :
+ LOW_LATENCY_PCM_MODE;
+
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = params_rate(params);
+ prtd->channel_mode = params_channels(params);
+ if (prtd->enabled)
+ return 0;
+
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits_per_sample = 24;
+ sample_word_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bits_per_sample = 24;
+ sample_word_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bits_per_sample = 16;
+ sample_word_size = 16;
+ break;
+ }
+
+ config.format = FORMAT_LINEAR_PCM;
+ config.bits_per_sample = bits_per_sample;
+ config.rate = params_rate(params);
+ config.channels = params_channels(params);
+ config.sample_word_size = sample_word_size;
+ config.bufsz = params_buffer_bytes(params) / params_periods(params);
+ config.bufcnt = params_periods(params);
+
+ ret = q6asm_open_shared_io(prtd->audio_client, &config, dir);
+ if (ret) {
+ pr_err("%s: q6asm_open_write_shared_io failed ret: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ prtd->pcm_size = params_buffer_bytes(params);
+ prtd->pcm_count = params_buffer_bytes(params);
+ prtd->pcm_irq_pos = 0;
+
+ buf = prtd->audio_client->port[dir].buf;
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf->data;
+ dma_buf->addr = buf->phys;
+ dma_buf->bytes = prtd->pcm_size;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ pr_debug("%s: session ID %d, perf %d\n", __func__,
+ prtd->audio_client->session,
+ prtd->audio_client->perf_mode);
+ prtd->session_id = prtd->audio_client->session;
+
+ pr_debug("msm_pcm_routing_reg_phy_stream w/ id %d\n",
+ soc_prtd->dai_link->be_id);
+ ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
+ prtd->session_id, substream->stream);
+
+ if (ret) {
+ pr_err("%s: stream reg failed ret:%d\n", __func__, ret);
+ return ret;
+ }
+
+ atomic_set(&prtd->out_count, runtime->periods);
+ prtd->enabled = 1;
+ prtd->cmd_pending = 0;
+ prtd->cmd_interrupt = 0;
+
+ return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
+ struct audio_buffer *buf;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: %s Trigger start\n", __func__,
+ dir == 0 ? "P" : "C");
+ ret = q6asm_run(prtd->audio_client, 0, 0, 0);
+ if (ret)
+ break;
+ atomic_set(&prtd->start, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
+ atomic_set(&prtd->start, 0);
+ q6asm_cmd(prtd->audio_client, CMD_PAUSE);
+ q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+ buf = q6asm_shared_io_buf(prtd->audio_client, dir);
+ if (buf == NULL) {
+ pr_err("%s: shared IO buffer is null\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+ memset(buf->data, 0, buf->actual_size);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_PAUSE\n", __func__);
+ ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+#ifdef CONFIG_SND_HWDEP
+static int msm_pcm_mmap_fd(struct snd_pcm_substream *substream,
+ struct snd_pcm_mmap_fd *mmap_fd)
+{
+ struct msm_audio *prtd;
+ struct audio_port_data *apd;
+ struct audio_buffer *ab;
+ int dir = -1;
+
+ if (!substream->runtime) {
+ pr_err("%s substream runtime not found\n", __func__);
+ return -EFAULT;
+ }
+
+ prtd = substream->runtime->private_data;
+ if (!prtd || !prtd->audio_client || !prtd->mmap_flag) {
+ pr_err("%s no audio client or not an mmap session\n", __func__);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+
+ apd = prtd->audio_client->port;
+ ab = &(apd[dir].buf[0]);
+ mmap_fd->fd = ion_share_dma_buf_fd(ab->client, ab->handle);
+ if (mmap_fd->fd >= 0) {
+ mmap_fd->dir = dir;
+ mmap_fd->actual_size = ab->actual_size;
+ mmap_fd->size = ab->size;
+ }
+ return mmap_fd->fd < 0 ? -EFAULT : 0;
+}
+#endif
+
+static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
+ struct audio_buffer *buf;
+
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL1_RESET:
+ pr_debug("%s: %s SNDRV_PCM_IOCTL1_RESET\n", __func__,
+ dir == 0 ? "P" : "C");
+ buf = q6asm_shared_io_buf(prtd->audio_client, dir);
+
+ if (buf && buf->data)
+ memset(buf->data, 0, buf->actual_size);
+ break;
+ default:
+ break;
+ }
+
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+#ifdef CONFIG_COMPAT
+static int msm_pcm_compat_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ /* we only handle RESET which is common for both modes */
+ return msm_pcm_ioctl(substream, cmd, arg);
+}
+#endif
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ uint32_t read_index, wall_clk_msw, wall_clk_lsw;
+ /*these are offsets, unlike ASoC's full values*/
+ snd_pcm_sframes_t hw_ptr;
+ snd_pcm_sframes_t period_size;
+ int ret;
+ int retries = 10;
+ struct msm_audio *prtd = runtime->private_data;
+
+ period_size = runtime->period_size;
+
+ do {
+ ret = q6asm_get_shared_pos(prtd->audio_client,
+ &read_index, &wall_clk_msw,
+ &wall_clk_lsw);
+ } while (ret == -EAGAIN && --retries);
+
+ if (ret || !period_size) {
+ pr_err("get_shared_pos error or zero period size\n");
+ return 0;
+ }
+
+ hw_ptr = bytes_to_frames(substream->runtime,
+ read_index);
+
+ if (runtime->control->appl_ptr == 0) {
+ pr_debug("ptr(%s): appl(0), hw = %lu read_index = %u\n",
+ prtd->substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "P" : "C",
+ hw_ptr, read_index);
+ }
+ return (hw_ptr/period_size) * period_size;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ return -EINVAL;
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct audio_client *ac = prtd->audio_client;
+ struct audio_port_data *apd = ac->port;
+ struct audio_buffer *ab;
+ int dir = -1;
+ int ret;
+
+ pr_debug("%s: mmap begin\n", __func__);
+ prtd->mmap_flag = 1;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+
+ ab = &(apd[dir].buf[0]);
+
+ ret = msm_audio_ion_mmap(ab, vma);
+
+ if (ret)
+ prtd->mmap_flag = 0;
+
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ if (!prtd || !prtd->mmap_flag)
+ return -EIO;
+
+ return 0;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct msm_plat_data *pdata = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ struct audio_client *ac = prtd->audio_client;
+ uint32_t timeout;
+ int dir = 0;
+ int ret = 0;
+
+ if (!soc_prtd) {
+ pr_debug("%s private_data not found\n",
+ __func__);
+ return 0;
+ }
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: pdata not found\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&pdata->lock);
+ if (ac) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ dir = OUT;
+
+ /* determine timeout length */
+ if (runtime->frame_bits == 0 || runtime->rate == 0) {
+ timeout = CMD_EOS_MIN_TIMEOUT_LENGTH;
+ } else {
+ timeout = (runtime->period_size *
+ CMD_EOS_TIMEOUT_MULTIPLIER) /
+ ((runtime->frame_bits / 8) *
+ runtime->rate);
+ if (timeout < CMD_EOS_MIN_TIMEOUT_LENGTH)
+ timeout = CMD_EOS_MIN_TIMEOUT_LENGTH;
+ }
+
+ q6asm_cmd(ac, CMD_CLOSE);
+
+ ret = q6asm_shared_io_free(ac, dir);
+
+ if (ret) {
+ pr_err("%s: Failed to close pull mode, ret %d\n",
+ __func__, ret);
+ }
+ q6asm_audio_client_free(ac);
+ }
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ dir == IN ?
+ SNDRV_PCM_STREAM_PLAYBACK :
+ SNDRV_PCM_STREAM_CAPTURE);
+ kfree(prtd);
+ runtime->private_data = NULL;
+ mutex_unlock(&pdata->lock);
+
+ return 0;
+}
+
+static int msm_pcm_set_volume(struct msm_audio *prtd, uint32_t volume)
+{
+ int rc = 0;
+
+ if (prtd && prtd->audio_client) {
+ pr_debug("%s: channels %d volume 0x%x\n", __func__,
+ prtd->channel_mode, volume);
+ rc = q6asm_set_volume(prtd->audio_client, volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed rc=%d\n",
+ __func__, rc);
+ }
+ }
+ return rc;
+}
+
+static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+ struct msm_plat_data *pdata = NULL;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_soc_pcm_runtime *soc_prtd = NULL;
+ struct msm_audio *prtd;
+
+ pr_debug("%s\n", __func__);
+ if (!vol) {
+ pr_err("%s: vol is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!vol->pcm) {
+ pr_err("%s: vol->pcm is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ substream = vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ return -ENODEV;
+ }
+ soc_prtd = substream->private_data;
+ if (!substream->runtime || !soc_prtd) {
+ pr_debug("%s substream runtime or private_data not found\n",
+ __func__);
+ return 0;
+ }
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: pdata not found\n", __func__);
+ return -ENODEV;
+ }
+ mutex_lock(&pdata->lock);
+ prtd = substream->runtime->private_data;
+ if (prtd)
+ ucontrol->value.integer.value[0] = prtd->volume;
+ mutex_unlock(&pdata->lock);
+ return 0;
+}
+
+static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+ struct msm_plat_data *pdata = NULL;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_soc_pcm_runtime *soc_prtd = NULL;
+ struct msm_audio *prtd;
+ int volume = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: volume : 0x%x\n", __func__, volume);
+
+ if (!vol) {
+ pr_err("%s: vol is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!vol->pcm) {
+ pr_err("%s: vol->pcm is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ substream = vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ return -ENODEV;
+ }
+ soc_prtd = substream->private_data;
+ if (!substream->runtime || !soc_prtd) {
+ pr_err("%s substream runtime or private_data not found\n",
+ __func__);
+ return 0;
+ }
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: pdata not found\n", __func__);
+ return -ENODEV;
+ }
+ mutex_lock(&pdata->lock);
+ prtd = substream->runtime->private_data;
+ if (prtd) {
+ rc = msm_pcm_set_volume(prtd, volume);
+ prtd->volume = volume;
+ }
+ mutex_unlock(&pdata->lock);
+ return rc;
+}
+
+static int msm_pcm_add_volume_control(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_pcm_volume *volume_info;
+ struct snd_kcontrol *kctl;
+
+ dev_dbg(rtd->dev, "%s, Volume control add\n", __func__);
+ ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 1, rtd->dai_link->be_id,
+ &volume_info);
+ if (ret < 0) {
+ pr_err("%s volume control failed ret %d\n", __func__, ret);
+ return ret;
+ }
+ kctl = volume_info->kctl;
+ kctl->put = msm_pcm_volume_ctl_put;
+ kctl->get = msm_pcm_volume_ctl_get;
+ kctl->tlv.p = msm_pcm_vol_gain;
+ return 0;
+}
+
+static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
+
+ pr_debug("%s", __func__);
+ substream = snd_pcm_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+ if (!substream->runtime)
+ return 0;
+
+ prtd = substream->runtime->private_data;
+ if (prtd) {
+ prtd->set_channel_map = true;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ prtd->channel_map[i] =
+ (char)(ucontrol->value.integer.value[i]);
+ }
+ return 0;
+}
+
+static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
+
+ pr_debug("%s", __func__);
+ substream = snd_pcm_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+ memset(ucontrol->value.integer.value, 0,
+ sizeof(ucontrol->value.integer.value));
+ if (!substream->runtime)
+ return 0; /* no channels set */
+
+ prtd = substream->runtime->private_data;
+
+ if (prtd && prtd->set_channel_map == true) {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ ucontrol->value.integer.value[i] =
+ (int)prtd->channel_map[i];
+ } else {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ ucontrol->value.integer.value[i] = 0;
+ }
+
+ return 0;
+}
+
+static int msm_pcm_add_chmap_control(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_pcm_chmap *chmap_info;
+ struct snd_kcontrol *kctl;
+ char device_num[12];
+ int i, ret;
+
+ pr_debug("%s, Channel map cntrl add\n", __func__);
+ ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps,
+ PCM_FORMAT_MAX_NUM_CHANNEL, 0,
+ &chmap_info);
+ if (ret)
+ return ret;
+
+ kctl = chmap_info->kctl;
+ for (i = 0; i < kctl->count; i++)
+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ snprintf(device_num, sizeof(device_num), "%d", pcm->device);
+ strlcat(kctl->id.name, device_num, sizeof(kctl->id.name));
+ pr_debug("%s, Overwriting channel map control name to: %s",
+ __func__, kctl->id.name);
+ kctl->put = msm_pcm_chmap_ctl_put;
+ kctl->get = msm_pcm_chmap_ctl_get;
+ return 0;
+}
+
+static int msm_pcm_fe_topology_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const struct soc_enum *e = &msm_pcm_fe_topology_enum[0];
+
+ return snd_ctl_enum_info(uinfo, 1, e->items, e->texts);
+}
+
+static int msm_pcm_fe_topology_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned long fe_id = kcontrol->private_value;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: %lu topology %s\n", __func__, fe_id,
+ msm_pcm_fe_topology_text[msm_pcm_fe_topology[fe_id]]);
+ ucontrol->value.enumerated.item[0] = msm_pcm_fe_topology[fe_id];
+ return 0;
+}
+
+static int msm_pcm_fe_topology_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned long fe_id = kcontrol->private_value;
+ unsigned int item;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ item = ucontrol->value.enumerated.item[0];
+ if (item >= ARRAY_SIZE(msm_pcm_fe_topology_text)) {
+ pr_err("%s Received out of bound topology %lu\n", __func__,
+ fe_id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: %lu new topology %s\n", __func__, fe_id,
+ msm_pcm_fe_topology_text[item]);
+ msm_pcm_fe_topology[fe_id] = item;
+ return 0;
+}
+
+static int msm_pcm_add_fe_topology_control(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "PCM_Dev";
+ const char *deviceNo = "NN";
+ const char *topo_text = "Topology";
+ char *mixer_str = NULL;
+ int ctl_len;
+ int ret;
+ struct snd_kcontrol_new topology_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "?",
+ .info = msm_pcm_fe_topology_info,
+ .get = msm_pcm_fe_topology_get,
+ .put = msm_pcm_fe_topology_put,
+ .private_value = 0,
+ },
+ };
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 +
+ strlen(topo_text) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+
+ if (!mixer_str)
+ return -ENOMEM;
+
+ snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name,
+ rtd->pcm->device, topo_text);
+
+ topology_control[0].name = mixer_str;
+ topology_control[0].private_value = rtd->dai_link->be_id;
+ ret = snd_soc_add_platform_controls(rtd->platform, topology_control,
+ ARRAY_SIZE(topology_control));
+ msm_pcm_fe_topology[rtd->dai_link->be_id] = 0;
+ kfree(mixer_str);
+ return ret;
+}
+
+static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = ucontrol->value.integer.value[3];
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ int ret = 0;
+
+ cfg_data.app_type = ucontrol->value.integer.value[0];
+ cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
+ if (ucontrol->value.integer.value[2] != 0)
+ cfg_data.sample_rate = ucontrol->value.integer.value[2];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, &cfg_data);
+ if (ret < 0)
+ pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+ return ret;
+}
+
+static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = 0;
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ &be_id, &cfg_data);
+ if (ret < 0) {
+ pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[0] = cfg_data.app_type;
+ ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
+ ucontrol->value.integer.value[2] = cfg_data.sample_rate;
+ ucontrol->value.integer.value[3] = be_id;
+ pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+done:
+ return ret;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = ucontrol->value.integer.value[3];
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ int ret = 0;
+
+ cfg_data.app_type = ucontrol->value.integer.value[0];
+ cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
+ if (ucontrol->value.integer.value[2] != 0)
+ cfg_data.sample_rate = ucontrol->value.integer.value[2];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, &cfg_data);
+ if (ret < 0)
+ pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = 0;
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ &be_id, &cfg_data);
+ if (ret < 0) {
+ pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[0] = cfg_data.app_type;
+ ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
+ ucontrol->value.integer.value[2] = cfg_data.sample_rate;
+ ucontrol->value.integer.value[3] = be_id;
+ pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+done:
+ return ret;
+}
+
+static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_pcm_usr *app_type_info;
+ struct snd_kcontrol *kctl;
+ const char *playback_mixer_ctl_name = "Audio Stream";
+ const char *capture_mixer_ctl_name = "Audio Stream Capture";
+ const char *deviceNo = "NN";
+ const char *suffix = "App Type Cfg";
+ int ctl_len, ret = 0;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 +
+ strlen(suffix) + 1;
+ pr_debug("%s: Playback app type cntrl add\n", __func__);
+ ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 1, ctl_len, rtd->dai_link->be_id,
+ &app_type_info);
+ if (ret < 0) {
+ pr_err("%s: playback app type cntrl add failed, err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ kctl = app_type_info->kctl;
+ snprintf(kctl->id.name, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ kctl->put = msm_pcm_playback_app_type_cfg_ctl_put;
+ kctl->get = msm_pcm_playback_app_type_cfg_ctl_get;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ pr_debug("%s: Capture app type cntrl add\n", __func__);
+ ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ NULL, 1, ctl_len, rtd->dai_link->be_id,
+ &app_type_info);
+ if (ret < 0) {
+ pr_err("%s: capture app type cntrl add failed, err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ kctl = app_type_info->kctl;
+ snprintf(kctl->id.name, ctl_len, "%s %d %s",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix);
+ kctl->put = msm_pcm_capture_app_type_cfg_ctl_put;
+ kctl->get = msm_pcm_capture_app_type_cfg_ctl_get;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SND_HWDEP
+static int msm_pcm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ struct snd_pcm *pcm = hw->private_data;
+ struct snd_pcm_mmap_fd __user *_mmap_fd = NULL;
+ struct snd_pcm_mmap_fd mmap_fd;
+ struct snd_pcm_substream *substream = NULL;
+ int32_t dir = -1;
+
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL_MMAP_DATA_FD:
+ _mmap_fd = (struct snd_pcm_mmap_fd __user *)arg;
+ if (get_user(dir, (int32_t __user *)&(_mmap_fd->dir))) {
+ pr_err("%s: error copying mmap_fd from user\n",
+ __func__);
+ ret = -EFAULT;
+ break;
+ }
+ if (dir != OUT && dir != IN) {
+ pr_err("%s invalid stream dir\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+ substream = pcm->streams[dir].substream;
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ ret = -ENODEV;
+ break;
+ }
+ pr_debug("%s : %s MMAP Data fd\n", __func__,
+ dir == 0 ? "P" : "C");
+ if (msm_pcm_mmap_fd(substream, &mmap_fd) < 0) {
+ pr_err("%s: error getting fd\n",
+ __func__);
+ ret = -EFAULT;
+ break;
+ }
+ if (put_user(mmap_fd.fd, &_mmap_fd->fd) ||
+ put_user(mmap_fd.size, &_mmap_fd->size) ||
+ put_user(mmap_fd.actual_size, &_mmap_fd->actual_size)) {
+ pr_err("%s: error copying fd\n", __func__);
+ return -EFAULT;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static int msm_pcm_hwdep_compat_ioctl(struct snd_hwdep *hw,
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ /* we only support mmap fd. Handling is common in both modes */
+ return msm_pcm_hwdep_ioctl(hw, file, cmd, arg);
+}
+#else
+static int msm_pcm_hwdep_compat_ioctl(struct snd_hwdep *hw,
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ return -EINVAL;
+}
+#endif
+
+static int msm_pcm_add_hwdep_dev(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_hwdep *hwdep;
+ int rc;
+ char id[] = "NOIRQ_NN";
+
+ snprintf(id, sizeof(id), "NOIRQ_%d", runtime->pcm->device);
+ pr_debug("%s: pcm dev %d\n", __func__, runtime->pcm->device);
+ rc = snd_hwdep_new(runtime->card->snd_card,
+ &id[0],
+ HWDEP_FE_BASE + runtime->pcm->device,
+ &hwdep);
+ if (!hwdep || rc < 0) {
+ pr_err("%s: hwdep intf failed to create %s - hwdep\n", __func__,
+ id);
+ return rc;
+ }
+
+ hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_BE; /* for lack of a FE iface */
+ hwdep->private_data = runtime->pcm; /* of type struct snd_pcm */
+ hwdep->ops.ioctl = msm_pcm_hwdep_ioctl;
+ hwdep->ops.ioctl_compat = msm_pcm_hwdep_compat_ioctl;
+ return 0;
+}
+#endif
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret;
+
+ pr_debug("%s , register new control\n", __func__);
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = msm_pcm_add_chmap_control(rtd);
+ if (ret) {
+ pr_err("%s failed to add chmap cntls\n", __func__);
+ goto exit;
+ }
+ ret = msm_pcm_add_volume_control(rtd);
+ if (ret) {
+ pr_err("%s: Could not add pcm Volume Control %d\n",
+ __func__, ret);
+ }
+
+ ret = msm_pcm_add_fe_topology_control(rtd);
+ if (ret) {
+ pr_err("%s: Could not add pcm topology control %d\n",
+ __func__, ret);
+ }
+
+ ret = msm_pcm_add_app_type_controls(rtd);
+ if (ret) {
+ pr_err("%s: Could not add app type controls failed %d\n",
+ __func__, ret);
+ }
+#ifdef CONFIG_SND_HWDEP
+ ret = msm_pcm_add_hwdep_dev(rtd);
+ if (ret)
+ pr_err("%s: Could not add hw dep node\n", __func__);
+#endif
+ pcm->nonatomic = true;
+exit:
+ return ret;
+}
+
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .prepare = msm_pcm_prepare,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .ioctl = msm_pcm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = msm_pcm_compat_ioctl,
+#endif
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+ .close = msm_pcm_close,
+};
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct msm_plat_data *pdata;
+ const char *latency_level;
+ int perf_mode = LOW_LATENCY_PCM_MODE;
+
+ dev_dbg(&pdev->dev, "Pull mode driver probe\n");
+
+ if (of_property_read_bool(pdev->dev.of_node,
+ "qcom,msm-pcm-low-latency")) {
+
+ rc = of_property_read_string(pdev->dev.of_node,
+ "qcom,latency-level", &latency_level);
+ if (!rc) {
+ if (!strcmp(latency_level, "ultra"))
+ perf_mode = ULTRA_LOW_LATENCY_PCM_MODE;
+ else if (!strcmp(latency_level, "ull-pp"))
+ perf_mode = ULL_POST_PROCESSING_PCM_MODE;
+ }
+ }
+
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_plat_data), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->perf_mode = perf_mode;
+
+ mutex_init(&pdata->lock);
+
+ dev_set_drvdata(&pdev->dev, pdata);
+
+ dev_dbg(&pdev->dev, "%s: dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+ dev_dbg(&pdev->dev, "Pull mode driver register\n");
+ rc = snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+
+ if (rc)
+ dev_err(&pdev->dev, "Failed to register pull mode driver\n");
+
+ return rc;
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ struct msm_plat_data *pdata;
+
+ dev_dbg(&pdev->dev, "Pull mode remove\n");
+ pdata = dev_get_drvdata(&pdev->dev);
+ mutex_destroy(&pdata->lock);
+ devm_kfree(&pdev->dev, pdata);
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+static const struct of_device_id msm_pcm_dt_match[] = {
+ {.compatible = "qcom,msm-pcm-dsp-noirq"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_pcm_dt_match);
+
+static struct platform_driver msm_pcm_driver_noirq = {
+ .driver = {
+ .name = "msm-pcm-dsp-noirq",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_pcm_dt_match,
+ },
+ .probe = msm_pcm_probe,
+ .remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_pcm_driver_noirq);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver_noirq);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM NOIRQ module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
new file mode 100644
index 000000000000..487aaf2390c0
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -0,0 +1,3257 @@
+/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6audio-v2.h>
+#include <sound/timer.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/msm_audio.h>
+
+#include <linux/of_device.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+#include <sound/q6core.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+#include "msm-qti-pp-config.h"
+
+enum stream_state {
+ IDLE = 0,
+ STOPPED,
+ RUNNING,
+};
+
+static struct audio_locks the_locks;
+
+#define PCM_MASTER_VOL_MAX_STEPS 0x2000
+static const DECLARE_TLV_DB_LINEAR(msm_pcm_vol_gain, 0,
+ PCM_MASTER_VOL_MAX_STEPS);
+
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+#define CMD_EOS_MIN_TIMEOUT_LENGTH 50
+#define CMD_EOS_TIMEOUT_MULTIPLIER (HZ * 50)
+#define MAX_PB_COPY_RETRIES 3
+
+static struct snd_pcm_hardware msm_pcm_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 4,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS *
+ CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS *
+ PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = PLAYBACK_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+ 88200, 96000, 176400, 192000, 352800, 384000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
+ void *priv_data)
+{
+ struct msm_audio *prtd = priv_data;
+
+ BUG_ON(!prtd);
+
+ pr_debug("%s: event %x\n", __func__, event);
+
+ switch (event) {
+ case MSM_PCM_RT_EVT_BUF_RECFG:
+ q6asm_cmd(prtd->audio_client, CMD_PAUSE);
+ q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+ q6asm_run(prtd->audio_client, 0, 0, 0);
+ default:
+ break;
+ }
+}
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_audio *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ uint32_t *ptrmem = (uint32_t *)payload;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+ uint8_t buf_index;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret = 0;
+
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2: {
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start))
+ break;
+ if (!prtd->mmap_flag || prtd->reset_event)
+ break;
+ if (q6asm_is_cpu_buf_avail_nolock(IN,
+ prtd->audio_client,
+ &size, &idx)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+ __func__, prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ }
+ break;
+ }
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ pr_debug("ASM_DATA_EVENT_RENDERED_EOS\n");
+ clear_bit(CMD_EOS, &prtd->cmd_pending);
+ wake_up(&the_locks.eos_wait);
+ break;
+ case ASM_DATA_EVENT_READ_DONE_V2: {
+ pr_debug("ASM_DATA_EVENT_READ_DONE_V2\n");
+ buf_index = q6asm_get_buf_index_from_token(token);
+ if (buf_index >= CAPTURE_MAX_NUM_PERIODS) {
+ pr_err("%s: buffer index %u is out of range.\n",
+ __func__, buf_index);
+ return;
+ }
+ pr_debug("%s: token=0x%08x buf_index=0x%08x\n",
+ __func__, token, buf_index);
+ prtd->in_frame_info[buf_index].size = payload[4];
+ prtd->in_frame_info[buf_index].offset = payload[5];
+ /* assume data size = 0 during flushing */
+ if (prtd->in_frame_info[buf_index].size) {
+ prtd->pcm_irq_pos +=
+ prtd->in_frame_info[buf_index].size;
+ pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ if (atomic_read(&prtd->in_count) <= prtd->periods)
+ atomic_inc(&prtd->in_count);
+ wake_up(&the_locks.read_wait);
+ if (prtd->mmap_flag &&
+ q6asm_is_cpu_buf_avail_nolock(OUT,
+ prtd->audio_client,
+ &size, &idx) &&
+ (substream->runtime->status->state ==
+ SNDRV_PCM_STATE_RUNNING))
+ q6asm_read_nolock(prtd->audio_client);
+ } else {
+ pr_debug("%s: reclaim flushed buf in_count %x\n",
+ __func__, atomic_read(&prtd->in_count));
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (prtd->mmap_flag) {
+ if (q6asm_is_cpu_buf_avail_nolock(OUT,
+ prtd->audio_client,
+ &size, &idx) &&
+ (substream->runtime->status->state ==
+ SNDRV_PCM_STATE_RUNNING))
+ q6asm_read_nolock(prtd->audio_client);
+ } else {
+ atomic_inc(&prtd->in_count);
+ }
+ if (atomic_read(&prtd->in_count) == prtd->periods) {
+ pr_info("%s: reclaimed all bufs\n", __func__);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ wake_up(&the_locks.read_wait);
+ }
+ }
+ break;
+ }
+ case ASM_STREAM_PP_EVENT:
+ case ASM_STREAM_CMD_ENCDEC_EVENTS: {
+ pr_debug("%s: ASM_STREAM_EVENT (0x%x)\n", __func__, opcode);
+ if (!substream) {
+ pr_err("%s: substream is NULL.\n", __func__);
+ return;
+ }
+
+ rtd = substream->private_data;
+ if (!rtd) {
+ pr_err("%s: rtd is NULL\n", __func__);
+ return;
+ }
+
+ ret = msm_adsp_inform_mixer_ctl(rtd, payload);
+ if (ret) {
+ pr_err("%s: failed to inform mixer ctl. err = %d\n",
+ __func__, ret);
+ return;
+ }
+
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2:
+ if (substream->stream
+ != SNDRV_PCM_STREAM_PLAYBACK) {
+ atomic_set(&prtd->start, 1);
+ break;
+ }
+ if (prtd->mmap_flag) {
+ pr_debug("%s:writing %d bytes of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ } else {
+ while (atomic_read(&prtd->out_needed)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ atomic_dec(&prtd->out_needed);
+ wake_up(&the_locks.write_wait);
+ };
+ }
+ atomic_set(&prtd->start, 1);
+ break;
+ case ASM_STREAM_CMD_REGISTER_PP_EVENTS:
+ pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS:",
+ __func__);
+ break;
+ default:
+ pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ break;
+ }
+ }
+ break;
+ case RESET_EVENTS:
+ pr_debug("%s RESET_EVENTS\n", __func__);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ atomic_inc(&prtd->out_count);
+ atomic_inc(&prtd->in_count);
+ prtd->reset_event = true;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ wake_up(&the_locks.eos_wait);
+ wake_up(&the_locks.write_wait);
+ wake_up(&the_locks.read_wait);
+ break;
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ struct msm_plat_data *pdata;
+ struct snd_pcm_hw_params *params;
+ int ret;
+ uint32_t fmt_type = FORMAT_LINEAR_PCM;
+ uint16_t bits_per_sample;
+ uint16_t sample_word_size;
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: platform data not populated\n", __func__);
+ return -EINVAL;
+ }
+ if (!prtd || !prtd->audio_client) {
+ pr_err("%s: private data null or audio client freed\n",
+ __func__);
+ return -EINVAL;
+ }
+ params = &soc_prtd->dpcm[substream->stream].hw_params;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ if (prtd->enabled)
+ return 0;
+
+ prtd->audio_client->perf_mode = pdata->perf_mode;
+ pr_debug("%s: perf: %x\n", __func__, pdata->perf_mode);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bits_per_sample = 32;
+ sample_word_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits_per_sample = 24;
+ sample_word_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bits_per_sample = 24;
+ sample_word_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bits_per_sample = 16;
+ sample_word_size = 16;
+ break;
+ }
+ if (prtd->compress_enable) {
+ fmt_type = FORMAT_GEN_COMPR;
+ pr_debug("%s: Compressed enabled!\n", __func__);
+ ret = q6asm_open_write_compressed(prtd->audio_client, fmt_type,
+ COMPRESSED_PASSTHROUGH_GEN);
+ if (ret < 0) {
+ pr_err("%s: q6asm_open_write_compressed failed (%d)\n",
+ __func__, ret);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ return -ENOMEM;
+ }
+ } else if (pdata->avs_ver &&
+ (q6core_get_avs_version() == Q6_SUBSYS_AVS2_7)) {
+ ret = q6asm_open_write_v3(prtd->audio_client,
+ FORMAT_LINEAR_PCM, bits_per_sample);
+ if (ret < 0) {
+ pr_err("%s: q6asm_open_write_v3 failed (%d)\n",
+ __func__, ret);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ return -ENOMEM;
+ }
+ } else {
+ ret = q6asm_open_write_with_retry(prtd->audio_client,
+ fmt_type, bits_per_sample);
+ if (ret < 0) {
+ pr_err("%s: q6asm_open_write_with_retry failed (%d)\n",
+ __func__, ret);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ return -ENOMEM;
+ }
+
+ ret = q6asm_send_cal(prtd->audio_client);
+ if (ret < 0)
+ pr_debug("%s : Send cal failed : %d", __func__, ret);
+ }
+ pr_debug("%s: session ID %d\n", __func__,
+ prtd->audio_client->session);
+ prtd->session_id = prtd->audio_client->session;
+
+ if (prtd->compress_enable) {
+ ret = msm_pcm_routing_reg_phy_compr_stream(
+ soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
+ prtd->session_id,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ COMPRESSED_PASSTHROUGH_GEN);
+ } else {
+ ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
+ prtd->session_id, substream->stream);
+ }
+ if (ret) {
+ pr_err("%s: stream reg failed ret:%d\n", __func__, ret);
+ return ret;
+ }
+ if (prtd->compress_enable) {
+ ret = q6asm_media_format_block_gen_compr(
+ prtd->audio_client, runtime->rate,
+ runtime->channels, !prtd->set_channel_map,
+ prtd->channel_map, bits_per_sample);
+ } else {
+ if (q6asm_get_svc_version(APR_SVC_ASM) >=
+ ADSP_ASM_API_VERSION_V2)
+ ret = q6asm_media_format_block_multi_ch_pcm_v5(
+ prtd->audio_client, runtime->rate,
+ runtime->channels, !prtd->set_channel_map,
+ prtd->channel_map, bits_per_sample,
+ sample_word_size, ASM_LITTLE_ENDIAN,
+ DEFAULT_QF);
+ else if (pdata->avs_ver &&
+ (q6core_get_avs_version() ==
+ Q6_SUBSYS_AVS2_7))
+ ret = q6asm_media_format_block_multi_ch_pcm_v3(
+ prtd->audio_client, runtime->rate,
+ runtime->channels, !prtd->set_channel_map,
+ prtd->channel_map, bits_per_sample,
+ sample_word_size);
+ else
+ ret = q6asm_media_format_block_multi_ch_pcm_v4(
+ prtd->audio_client, runtime->rate,
+ runtime->channels, !prtd->set_channel_map,
+ prtd->channel_map, bits_per_sample,
+ sample_word_size, ASM_LITTLE_ENDIAN,
+ DEFAULT_QF);
+ }
+ if (ret < 0)
+ pr_info("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+
+ prtd->enabled = 1;
+ prtd->cmd_pending = 0;
+ prtd->cmd_interrupt = 0;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_plat_data *pdata;
+ struct snd_pcm_hw_params *params;
+ struct msm_pcm_routing_evt event;
+ int ret = 0;
+ int i = 0;
+ uint16_t bits_per_sample = 16;
+ uint16_t sample_word_size;
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: platform data not populated\n", __func__);
+ return -EINVAL;
+ }
+ if (!prtd || !prtd->audio_client) {
+ pr_err("%s: private data null or audio client freed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (prtd->enabled == IDLE) {
+ pr_debug("%s:perf_mode=%d periods=%d\n", __func__,
+ pdata->perf_mode, runtime->periods);
+ params = &soc_prtd->dpcm[substream->stream].hw_params;
+ if ((params_format(params) == SNDRV_PCM_FORMAT_S24_LE) ||
+ (params_format(params) == SNDRV_PCM_FORMAT_S24_3LE))
+ bits_per_sample = 24;
+ else if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE)
+ bits_per_sample = 32;
+
+ /* ULL mode is not supported in capture path */
+ if (pdata->perf_mode == LEGACY_PCM_MODE)
+ prtd->audio_client->perf_mode = LEGACY_PCM_MODE;
+ else
+ prtd->audio_client->perf_mode = LOW_LATENCY_PCM_MODE;
+
+ pr_debug("%s Opening %d-ch PCM read stream, perf_mode %d\n",
+ __func__, params_channels(params),
+ prtd->audio_client->perf_mode);
+ if (pdata->avs_ver &&
+ (q6core_get_avs_version() == Q6_SUBSYS_AVS2_7))
+ ret = q6asm_open_read_v3(prtd->audio_client,
+ FORMAT_LINEAR_PCM,
+ bits_per_sample);
+ else
+ ret = q6asm_open_read_with_retry(prtd->audio_client,
+ FORMAT_LINEAR_PCM,
+ bits_per_sample, false);
+ if (ret < 0) {
+ pr_err("%s: q6asm_open_read failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ return -ENOMEM;
+ }
+
+ ret = q6asm_send_cal(prtd->audio_client);
+ if (ret < 0)
+ pr_debug("%s : Send cal failed : %d", __func__, ret);
+
+ pr_debug("%s: session ID %d\n",
+ __func__, prtd->audio_client->session);
+ prtd->session_id = prtd->audio_client->session;
+ event.event_func = msm_pcm_route_event_handler;
+ event.priv_data = (void *) prtd;
+ ret = msm_pcm_routing_reg_phy_stream_v2(
+ soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
+ prtd->session_id, substream->stream,
+ event);
+ if (ret) {
+ pr_err("%s: stream reg failed ret:%d\n", __func__, ret);
+ return ret;
+ }
+ }
+
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+
+ if (prtd->enabled == IDLE || prtd->enabled == STOPPED) {
+ for (i = 0; i < runtime->periods; i++)
+ q6asm_read(prtd->audio_client);
+ prtd->periods = runtime->periods;
+ }
+
+ if (prtd->enabled != IDLE)
+ return 0;
+
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bits_per_sample = 32;
+ sample_word_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits_per_sample = 24;
+ sample_word_size = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bits_per_sample = 24;
+ sample_word_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bits_per_sample = 16;
+ sample_word_size = 16;
+ break;
+ }
+
+ pr_debug("%s: Samp_rate = %d Channel = %d bit width = %d, word size = %d\n",
+ __func__, prtd->samp_rate, prtd->channel_mode,
+ bits_per_sample, sample_word_size);
+ if (q6asm_get_svc_version(APR_SVC_ASM) >=
+ ADSP_ASM_API_VERSION_V2)
+ ret = q6asm_enc_cfg_blk_pcm_format_support_v5(
+ prtd->audio_client,
+ prtd->samp_rate,
+ prtd->channel_mode,
+ bits_per_sample,
+ sample_word_size,
+ ASM_LITTLE_ENDIAN,
+ DEFAULT_QF);
+ else if (pdata->avs_ver &&
+ (q6core_get_avs_version() ==
+ Q6_SUBSYS_AVS2_7))
+ ret = q6asm_enc_cfg_blk_pcm_format_support_v3(
+ prtd->audio_client,
+ prtd->samp_rate,
+ prtd->channel_mode,
+ bits_per_sample,
+ sample_word_size);
+ else
+ ret = q6asm_enc_cfg_blk_pcm_format_support_v4(
+ prtd->audio_client,
+ prtd->samp_rate,
+ prtd->channel_mode,
+ bits_per_sample,
+ sample_word_size,
+ ASM_LITTLE_ENDIAN,
+ DEFAULT_QF);
+ if (ret < 0)
+ pr_debug("%s: cmd cfg pcm was block failed", __func__);
+
+ prtd->enabled = RUNNING;
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: Trigger start\n", __func__);
+ ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
+ prtd->enabled = STOPPED;
+ ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ break;
+ }
+ /* pending CMD_EOS isn't expected */
+ WARN_ON_ONCE(test_bit(CMD_EOS, &prtd->cmd_pending));
+ set_bit(CMD_EOS, &prtd->cmd_pending);
+ ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ if (ret)
+ clear_bit(CMD_EOS, &prtd->cmd_pending);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd;
+ struct msm_plat_data *pdata;
+ int ret = 0;
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: platform data not populated\n", __func__);
+ return -EINVAL;
+ }
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_info("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ prtd = NULL;
+ return -ENOMEM;
+ }
+
+ prtd->audio_client->dev = soc_prtd->platform->dev;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = msm_pcm_hardware_playback;
+
+ /* Capture path */
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = msm_pcm_hardware_capture;
+ else {
+ pr_err("Invalid Stream type %d\n", substream->stream);
+ return -EINVAL;
+ }
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE,
+ PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE);
+ if (ret < 0) {
+ pr_err("constraint for buffer bytes min max ret = %d\n",
+ ret);
+ }
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ CAPTURE_MIN_NUM_PERIODS * CAPTURE_MIN_PERIOD_SIZE,
+ CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE);
+ if (ret < 0) {
+ pr_err("constraint for buffer bytes min max ret = %d\n",
+ ret);
+ }
+ }
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ if (ret < 0) {
+ pr_err("constraint for period bytes step ret = %d\n",
+ ret);
+ }
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+ if (ret < 0) {
+ pr_err("constraint for buffer bytes step ret = %d\n",
+ ret);
+ }
+
+ prtd->enabled = IDLE;
+ prtd->dsp_cnt = 0;
+ prtd->set_channel_map = false;
+ prtd->reset_event = false;
+ runtime->private_data = prtd;
+ msm_adsp_init_mixer_ctl_pp_event_queue(soc_prtd);
+ /* Vote to update the Rx thread priority to RT Thread for playback */
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (pdata->perf_mode == LOW_LATENCY_PCM_MODE))
+ apr_start_rx_rt(prtd->audio_client->apr);
+
+ /* Vote to update the Rx thread priority to RT Thread for playback */
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (pdata->perf_mode == LOW_LATENCY_PCM_MODE))
+ apr_start_rx_rt(prtd->audio_client->apr);
+
+ return 0;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer = 0;
+ char *bufptr = NULL;
+ void *data = NULL;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+ uint32_t retries = 0;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ pr_debug("%s: prtd->out_count = %d\n",
+ __func__, atomic_read(&prtd->out_count));
+
+ while ((fbytes > 0) && (retries < MAX_PB_COPY_RETRIES)) {
+ if (prtd->reset_event) {
+ pr_err("%s: In SSR return ENETRESET before wait\n",
+ __func__);
+ return -ENETRESET;
+ }
+
+ ret = wait_event_timeout(the_locks.write_wait,
+ (atomic_read(&prtd->out_count)), 5 * HZ);
+ if (!ret) {
+ pr_err("%s: wait_event_timeout failed\n", __func__);
+ ret = -ETIMEDOUT;
+ goto fail;
+ }
+ ret = 0;
+
+ if (prtd->reset_event) {
+ pr_err("%s: In SSR return ENETRESET after wait\n",
+ __func__);
+ return -ENETRESET;
+ }
+
+ if (!atomic_read(&prtd->out_count)) {
+ pr_err("%s: pcm stopped out_count 0\n", __func__);
+ return 0;
+ }
+
+ data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size,
+ &idx);
+ if (data == NULL) {
+ retries++;
+ continue;
+ } else {
+ retries = 0;
+ }
+
+ if (fbytes > size)
+ xfer = size;
+ else
+ xfer = fbytes;
+
+ bufptr = data;
+ if (bufptr) {
+ pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
+ __func__, fbytes, xfer, size);
+ if (copy_from_user(bufptr, buf, xfer)) {
+ ret = -EFAULT;
+ pr_err("%s: copy_from_user failed\n",
+ __func__);
+ q6asm_cpu_buf_release(IN, prtd->audio_client);
+ goto fail;
+ }
+ buf += xfer;
+ fbytes -= xfer;
+ pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes,
+ xfer);
+ if (atomic_read(&prtd->start)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp\n",
+ __func__, xfer);
+ ret = q6asm_write(prtd->audio_client, xfer,
+ 0, 0, NO_TIMESTAMP);
+ if (ret < 0) {
+ ret = -EFAULT;
+ q6asm_cpu_buf_release(IN,
+ prtd->audio_client);
+ goto fail;
+ }
+ } else
+ atomic_inc(&prtd->out_needed);
+ atomic_dec(&prtd->out_count);
+ }
+ }
+fail:
+ if (retries >= MAX_PB_COPY_RETRIES)
+ ret = -ENOMEM;
+
+ return ret;
+}
+
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ struct msm_plat_data *pdata;
+ uint32_t timeout;
+ int dir = 0;
+ int ret = 0;
+
+ pr_debug("%s: cmd_pending 0x%lx\n", __func__, prtd->cmd_pending);
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: platform data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&pdata->lock);
+ if (prtd->audio_client) {
+ dir = IN;
+
+ /*
+ * Unvote to downgrade the Rx thread priority from
+ * RT Thread for Low-Latency use case.
+ */
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (pdata) {
+ if (pdata->perf_mode == LOW_LATENCY_PCM_MODE)
+ apr_end_rx_rt(prtd->audio_client->apr);
+ }
+ /* determine timeout length */
+ if (runtime->frame_bits == 0 || runtime->rate == 0) {
+ timeout = CMD_EOS_MIN_TIMEOUT_LENGTH;
+ } else {
+ timeout = (runtime->period_size *
+ CMD_EOS_TIMEOUT_MULTIPLIER) /
+ ((runtime->frame_bits / 8) *
+ runtime->rate);
+ if (timeout < CMD_EOS_MIN_TIMEOUT_LENGTH)
+ timeout = CMD_EOS_MIN_TIMEOUT_LENGTH;
+ }
+ pr_debug("%s: CMD_EOS timeout is %d\n", __func__, timeout);
+
+ ret = wait_event_timeout(the_locks.eos_wait,
+ !test_bit(CMD_EOS, &prtd->cmd_pending),
+ timeout);
+ if (!ret)
+ pr_err("%s: CMD_EOS failed, cmd_pending 0x%lx\n",
+ __func__, prtd->cmd_pending);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+ q6asm_audio_client_free(prtd->audio_client);
+ }
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd);
+ kfree(prtd);
+ runtime->private_data = NULL;
+ mutex_unlock(&pdata->lock);
+
+ return 0;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer;
+ char *bufptr;
+ void *data = NULL;
+ static uint32_t idx;
+ static uint32_t size;
+ uint32_t offset = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = substream->runtime->private_data;
+
+
+ pr_debug("%s\n", __func__);
+ fbytes = frames_to_bytes(runtime, frames);
+
+ pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
+ pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
+ pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
+
+ if (prtd->reset_event) {
+ pr_err("%s: In SSR return ENETRESET before wait\n", __func__);
+ return -ENETRESET;
+ }
+ ret = wait_event_timeout(the_locks.read_wait,
+ (atomic_read(&prtd->in_count)), 5 * HZ);
+ if (!ret) {
+ pr_debug("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+ if (prtd->reset_event) {
+ pr_err("%s: In SSR return ENETRESET after wait\n", __func__);
+ return -ENETRESET;
+ }
+ if (!atomic_read(&prtd->in_count)) {
+ pr_debug("%s: pcm stopped in_count 0\n", __func__);
+ return 0;
+ }
+ pr_debug("Checking if valid buffer is available...%pK\n",
+ data);
+ data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ pr_debug("Size = %d\n", size);
+ pr_debug("fbytes = %d\n", fbytes);
+ pr_debug("idx = %d\n", idx);
+ if (bufptr) {
+ xfer = fbytes;
+ if (xfer > size)
+ xfer = size;
+ offset = prtd->in_frame_info[idx].offset;
+ pr_debug("Offset value = %d\n", offset);
+ if (copy_to_user(buf, bufptr+offset, xfer)) {
+ pr_err("Failed to copy buf to user\n");
+ ret = -EFAULT;
+ q6asm_cpu_buf_release(OUT, prtd->audio_client);
+ goto fail;
+ }
+ fbytes -= xfer;
+ size -= xfer;
+ prtd->in_frame_info[idx].offset += xfer;
+ pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
+ __func__, fbytes, size, xfer);
+ pr_debug(" Sending next buffer to dsp\n");
+ memset(&prtd->in_frame_info[idx], 0,
+ sizeof(struct msm_audio_in_frame_info));
+ atomic_dec(&prtd->in_count);
+ ret = q6asm_read(prtd->audio_client);
+ if (ret < 0) {
+ pr_err("q6asm read failed\n");
+ ret = -EFAULT;
+ q6asm_cpu_buf_release(OUT, prtd->audio_client);
+ goto fail;
+ }
+ } else
+ pr_err("No valid buffer\n");
+
+ pr_debug("Returning from capture_copy... %d\n", ret);
+fail:
+ return ret;
+}
+
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = OUT;
+ struct msm_plat_data *pdata;
+
+ pr_debug("%s\n", __func__);
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: platform data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&pdata->lock);
+ if (prtd->audio_client) {
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+ q6asm_audio_client_free(prtd->audio_client);
+ }
+
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ kfree(prtd);
+ runtime->private_data = NULL;
+ mutex_unlock(&pdata->lock);
+
+ return 0;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct audio_client *ac = prtd->audio_client;
+ struct audio_port_data *apd = ac->port;
+ struct audio_buffer *ab;
+ int dir = -1;
+
+ prtd->mmap_flag = 1;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+ ab = &(apd[dir].buf[0]);
+
+ return msm_audio_ion_mmap(ab, vma);
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ (params_buffer_bytes(params) / params_periods(params)),
+ params_periods(params));
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed rc = %d\n",
+ ret);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+ if (buf == NULL || buf[0].data == NULL)
+ return -ENOMEM;
+
+ pr_debug("%s:buf = %pK\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = params_buffer_bytes(params);
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *pcm = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(pcm);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
+ int ret = 0;
+ struct msm_adsp_event_data *event_data = NULL;
+ uint64_t actual_payload_len = 0;
+
+ if (!pdata) {
+ pr_err("%s pdata is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&pdata->lock);
+ substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!substream->runtime) {
+ pr_err("%s substream runtime not found\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ prtd = substream->runtime->private_data;
+ if (prtd == NULL) {
+ pr_err("%s prtd is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client == NULL) {
+ pr_err("%s prtd is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&pdata->lock);
+ event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data;
+ if ((event_data->event_type < ADSP_STREAM_PP_EVENT) ||
+ (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) {
+ pr_err("%s: invalid event_type=%d",
+ __func__, event_data->event_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ actual_payload_len = sizeof(struct msm_adsp_event_data) +
+ event_data->payload_len;
+ if (actual_payload_len >= U32_MAX) {
+ pr_err("%s payload length 0x%X exceeds limit",
+ __func__, event_data->payload_len);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (event_data->payload_len > sizeof(ucontrol->value.bytes.data)
+ - sizeof(struct msm_adsp_event_data)) {
+ pr_err("%s param length=%d exceeds limit",
+ __func__, event_data->payload_len);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = q6asm_send_stream_cmd(prtd->audio_client, event_data);
+ if (ret < 0)
+ pr_err("%s: failed to send stream event cmd, err = %d\n",
+ __func__, ret);
+done:
+ mutex_unlock(&pdata->lock);
+ return ret;
+}
+
+static int msm_pcm_add_audio_adsp_stream_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CMD;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_audio_adsp_stream_cmd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_pcm_adsp_stream_cmd_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s rtd is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_audio_adsp_stream_cmd_config_control[0].name = mixer_str;
+ fe_audio_adsp_stream_cmd_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s\n", mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_audio_adsp_stream_cmd_config_control,
+ ARRAY_SIZE(fe_audio_adsp_stream_cmd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_pcm_add_audio_adsp_stream_callback_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol *kctl;
+
+ struct snd_kcontrol_new fe_audio_adsp_callback_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_callback_info,
+ .get = msm_adsp_stream_callback_get,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: added new pcm FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_audio_adsp_callback_config_control[0].name = mixer_str;
+ fe_audio_adsp_callback_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_audio_adsp_callback_config_control,
+ ARRAY_SIZE(fe_audio_adsp_callback_config_control));
+ if (ret < 0) {
+ pr_err("%s: failed to add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl->private_data = NULL;
+
+free_mixer_str:
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_pcm_set_volume(struct msm_audio *prtd, uint32_t volume)
+{
+ int rc = 0;
+
+ if (prtd && prtd->audio_client) {
+ pr_debug("%s: channels %d volume 0x%x\n", __func__,
+ prtd->channel_mode, volume);
+ rc = q6asm_set_volume(prtd->audio_client, volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed rc=%d\n",
+ __func__, rc);
+ }
+ }
+ return rc;
+}
+
+static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+ struct msm_plat_data *pdata = NULL;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_soc_pcm_runtime *soc_prtd = NULL;
+ struct msm_audio *prtd;
+
+ pr_debug("%s\n", __func__);
+ if (!vol) {
+ pr_err("%s: vol is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!vol->pcm) {
+ pr_err("%s: vol->pcm is NULL\n", __func__);
+ return -ENODEV;
+ }
+ substream = vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ return -ENODEV;
+ }
+ soc_prtd = substream->private_data;
+ if (!substream->runtime || !soc_prtd) {
+ pr_debug("%s substream runtime or private_data not found\n",
+ __func__);
+ return 0;
+ }
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: pdata not found\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&pdata->lock);
+ prtd = substream->runtime->private_data;
+ if (prtd)
+ ucontrol->value.integer.value[0] = prtd->volume;
+ mutex_unlock(&pdata->lock);
+ return 0;
+}
+
+static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+ struct snd_pcm_substream *substream = NULL;
+ struct msm_audio *prtd;
+ int volume = ucontrol->value.integer.value[0];
+ struct snd_soc_pcm_runtime *soc_prtd = NULL;
+ struct msm_plat_data *pdata = NULL;
+
+ pr_debug("%s: volume : 0x%x\n", __func__, volume);
+ if (!vol) {
+ pr_err("%s: vol is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!vol->pcm) {
+ pr_err("%s: vol->pcm is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ substream = vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (!substream) {
+ pr_err("%s: substream not found\n", __func__);
+ return -ENODEV;
+ }
+ soc_prtd = substream->private_data;
+ if (!substream->runtime || !soc_prtd) {
+ pr_err("%s: substream runtime or private_data not found\n",
+ __func__);
+ return 0;
+ }
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: pdata not found\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&pdata->lock);
+ prtd = substream->runtime->private_data;
+ if (prtd) {
+ rc = msm_pcm_set_volume(prtd, volume);
+ prtd->volume = volume;
+ }
+ mutex_unlock(&pdata->lock);
+ return rc;
+}
+
+static int msm_pcm_add_volume_control(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_pcm_volume *volume_info;
+ struct snd_kcontrol *kctl;
+
+ dev_dbg(rtd->dev, "%s, Volume control add\n", __func__);
+ ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 1, rtd->dai_link->be_id,
+ &volume_info);
+ if (ret < 0) {
+ pr_err("%s volume control failed ret %d\n", __func__, ret);
+ return ret;
+ }
+ kctl = volume_info->kctl;
+ kctl->put = msm_pcm_volume_ctl_put;
+ kctl->get = msm_pcm_volume_ctl_get;
+ kctl->tlv.p = msm_pcm_vol_gain;
+ return 0;
+}
+
+static int msm_pcm_compress_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0x2000;
+ return 0;
+}
+
+static int msm_pcm_compress_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
+
+ if (!pdata) {
+ pr_err("%s pdata is NULL\n", __func__);
+ return -ENODEV;
+ }
+ substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ return -EINVAL;
+ }
+ if (!substream->runtime) {
+ pr_err("%s substream runtime not found\n", __func__);
+ return 0;
+ }
+ mutex_lock(&pdata->lock);
+ prtd = substream->runtime->private_data;
+ if (prtd)
+ ucontrol->value.integer.value[0] = prtd->compress_enable;
+ mutex_unlock(&pdata->lock);
+ return 0;
+}
+
+static int msm_pcm_compress_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
+ int compress = ucontrol->value.integer.value[0];
+
+ if (!pdata) {
+ pr_err("%s pdata is NULL\n", __func__);
+ return -ENODEV;
+ }
+ substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ pr_debug("%s: compress : 0x%x\n", __func__, compress);
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ return -EINVAL;
+ }
+ if (!substream->runtime) {
+ pr_err("%s substream runtime not found\n", __func__);
+ return 0;
+ }
+ mutex_lock(&pdata->lock);
+ prtd = substream->runtime->private_data;
+ if (prtd) {
+ pr_debug("%s: setting compress flag to 0x%x\n",
+ __func__, compress);
+ prtd->compress_enable = compress;
+ }
+ mutex_unlock(&pdata->lock);
+ return rc;
+}
+
+static int msm_pcm_add_compress_control(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Playback ";
+ const char *mixer_ctl_end_name = " Compress";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len;
+ int ret = 0;
+ struct msm_plat_data *pdata;
+ struct snd_kcontrol_new pcm_compress_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_compress_ctl_info,
+ .get = msm_pcm_compress_ctl_get,
+ .put = msm_pcm_compress_ctl_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s: NULL rtd\n", __func__);
+ return -EINVAL;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + strlen(deviceNo) +
+ strlen(mixer_ctl_end_name) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+
+ if (!mixer_str)
+ return -ENOMEM;
+
+ snprintf(mixer_str, ctl_len, "%s%d%s", mixer_ctl_name,
+ rtd->pcm->device, mixer_ctl_end_name);
+
+ pcm_compress_control[0].name = mixer_str;
+ pcm_compress_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ pdata = dev_get_drvdata(rtd->platform->dev);
+ if (pdata) {
+ if (!pdata->pcm) {
+ pdata->pcm = rtd->pcm;
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ pcm_compress_control,
+ ARRAY_SIZE
+ (pcm_compress_control));
+ if (ret < 0)
+ pr_err("%s: failed add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+ }
+ } else {
+ pr_err("%s: NULL pdata\n", __func__);
+ ret = -EINVAL;
+ }
+ kfree(mixer_str);
+ return ret;
+}
+
+static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
+ struct snd_soc_pcm_runtime *rtd = NULL;
+ struct msm_plat_data *pdata = NULL;
+
+ pr_debug("%s", __func__);
+ substream = snd_pcm_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+
+ rtd = substream->private_data;
+ if (rtd) {
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(rtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: pdata not found\n", __func__);
+ return -ENODEV;
+ }
+ }
+
+ if (!substream->runtime || !rtd)
+ return 0;
+
+ mutex_lock(&pdata->lock);
+ prtd = substream->runtime->private_data;
+ if (prtd) {
+ prtd->set_channel_map = true;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ prtd->channel_map[i] =
+ (char)(ucontrol->value.integer.value[i]);
+ }
+ mutex_unlock(&pdata->lock);
+ return 0;
+}
+
+static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
+ struct snd_soc_pcm_runtime *rtd = NULL;
+ struct msm_plat_data *pdata = NULL;
+
+ pr_debug("%s", __func__);
+ substream = snd_pcm_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+
+ rtd = substream->private_data;
+ if (rtd) {
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(rtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: pdata not found\n", __func__);
+ return -ENODEV;
+ }
+ }
+
+ memset(ucontrol->value.integer.value, 0,
+ sizeof(ucontrol->value.integer.value));
+ if (!substream->runtime || !rtd)
+ return 0; /* no channels set */
+
+ mutex_lock(&pdata->lock);
+ prtd = substream->runtime->private_data;
+
+ if (prtd && prtd->set_channel_map == true) {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ ucontrol->value.integer.value[i] =
+ (int)prtd->channel_map[i];
+ } else {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ ucontrol->value.integer.value[i] = 0;
+ }
+
+ mutex_unlock(&pdata->lock);
+ return 0;
+}
+
+static int msm_pcm_add_chmap_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_pcm_chmap *chmap_info;
+ struct snd_kcontrol *kctl;
+ char device_num[12];
+ int i, ret = 0;
+
+ pr_debug("%s, Channel map cntrl add\n", __func__);
+ ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps,
+ PCM_FORMAT_MAX_NUM_CHANNEL_V2, 0,
+ &chmap_info);
+ if (ret < 0) {
+ pr_err("%s, channel map cntrl add failed\n", __func__);
+ return ret;
+ }
+ kctl = chmap_info->kctl;
+ for (i = 0; i < kctl->count; i++)
+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ snprintf(device_num, sizeof(device_num), "%d", pcm->device);
+ strlcat(kctl->id.name, device_num, sizeof(kctl->id.name));
+ pr_debug("%s, Overwriting channel map control name to: %s\n",
+ __func__, kctl->id.name);
+ kctl->put = msm_pcm_chmap_ctl_put;
+ kctl->get = msm_pcm_chmap_ctl_get;
+ return 0;
+}
+
+static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = ucontrol->value.integer.value[3];
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
+ int ret = 0;
+
+ cfg_data.app_type = ucontrol->value.integer.value[0];
+ cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
+ if (ucontrol->value.integer.value[2] != 0)
+ cfg_data.sample_rate = ucontrol->value.integer.value[2];
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, &cfg_data);
+ if (ret < 0)
+ pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = 0;
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ &be_id, &cfg_data);
+ if (ret < 0) {
+ pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[0] = cfg_data.app_type;
+ ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
+ ucontrol->value.integer.value[2] = cfg_data.sample_rate;
+ ucontrol->value.integer.value[3] = be_id;
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
+done:
+ return ret;
+}
+
+static int msm_pcm_playback_pan_scale_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(struct asm_stream_pan_ctrl_params);
+ return 0;
+}
+
+static int msm_pcm_playback_pan_scale_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int len = 0;
+ int i = 0;
+ struct snd_soc_component *usr_info = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform;
+ struct msm_plat_data *pdata;
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
+ struct asm_stream_pan_ctrl_params pan_param;
+ char *usr_value = NULL;
+ uint32_t *gain_ptr = NULL;
+
+ if (!usr_info) {
+ pr_err("%s: usr_info is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ platform = snd_soc_component_to_platform(usr_info);
+ if (!platform) {
+ pr_err("%s: platform is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ pdata = dev_get_drvdata(platform->dev);
+ if (!pdata) {
+ pr_err("%s: pdata is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!substream->runtime) {
+ pr_err("%s substream runtime not found\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ prtd = substream->runtime->private_data;
+ if (!prtd) {
+ ret = -EINVAL;
+ goto done;
+ }
+ usr_value = (char *) ucontrol->value.bytes.data;
+ if (!usr_value) {
+ pr_err("%s ucontrol data is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ memcpy(&pan_param.num_output_channels, &usr_value[len],
+ sizeof(pan_param.num_output_channels));
+ len += sizeof(pan_param.num_output_channels);
+ if (pan_param.num_output_channels >
+ PCM_FORMAT_MAX_NUM_CHANNEL) {
+ ret = -EINVAL;
+ goto done;
+ }
+ memcpy(&pan_param.num_input_channels, &usr_value[len],
+ sizeof(pan_param.num_input_channels));
+ len += sizeof(pan_param.num_input_channels);
+ if (pan_param.num_input_channels >
+ PCM_FORMAT_MAX_NUM_CHANNEL) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (usr_value[len++]) {
+ memcpy(pan_param.output_channel_map, &usr_value[len],
+ (pan_param.num_output_channels *
+ sizeof(pan_param.output_channel_map[0])));
+ len += (pan_param.num_output_channels *
+ sizeof(pan_param.output_channel_map[0]));
+ }
+ if (usr_value[len++]) {
+ memcpy(pan_param.input_channel_map, &usr_value[len],
+ (pan_param.num_input_channels *
+ sizeof(pan_param.input_channel_map[0])));
+ len += (pan_param.num_input_channels *
+ sizeof(pan_param.input_channel_map[0]));
+ }
+ if (usr_value[len++]) {
+ gain_ptr = (uint32_t *) &usr_value[len];
+ for (i = 0; i < pan_param.num_output_channels *
+ pan_param.num_input_channels; i++) {
+ pan_param.gain[i] =
+ !(gain_ptr[i] > 0) ?
+ 0 : 2 << 13;
+ len += sizeof(pan_param.gain[i]);
+ }
+ len += (pan_param.num_input_channels *
+ pan_param.num_output_channels * sizeof(pan_param.gain[0]));
+ }
+
+ ret = q6asm_set_mfc_panning_params(prtd->audio_client,
+ &pan_param);
+ len -= pan_param.num_output_channels *
+ pan_param.num_input_channels * sizeof(pan_param.gain[0]);
+ if (gain_ptr) {
+ for (i = 0; i < pan_param.num_output_channels *
+ pan_param.num_input_channels; i++) {
+ /*
+ * The data userspace passes is already in Q14 format.
+ * For volume gain is in Q28.
+ */
+ pan_param.gain[i] =
+ (gain_ptr[i]) << 14;
+ len += sizeof(pan_param.gain[i]);
+ }
+ }
+ ret = q6asm_set_vol_ctrl_gain_pair(prtd->audio_client,
+ &pan_param);
+
+done:
+ return ret;
+}
+
+static int msm_pcm_playback_pan_scale_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int msm_add_stream_pan_scale_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *playback_mixer_ctl_name = "Audio Stream";
+ const char *deviceNo = "NN";
+ const char *suffix = "Pan Scale Control";
+ char *mixer_str = NULL;
+ int ctl_len;
+ int ret = 0;
+ struct msm_plat_data *pdata;
+ struct snd_kcontrol_new pan_scale_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_playback_pan_scale_ctl_info,
+ .get = msm_pcm_playback_pan_scale_ctl_get,
+ .put = msm_pcm_playback_pan_scale_ctl_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s: NULL rtd\n", __func__);
+ return -EINVAL;
+ }
+
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ pan_scale_control[0].name = mixer_str;
+ pan_scale_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ pdata = dev_get_drvdata(rtd->platform->dev);
+ if (pdata) {
+ if (!pdata->pcm)
+ pdata->pcm = rtd->pcm;
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ pan_scale_control,
+ ARRAY_SIZE
+ (pan_scale_control));
+ if (ret < 0)
+ pr_err("%s: failed add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+ } else {
+ pr_err("%s: NULL pdata\n", __func__);
+ ret = -EINVAL;
+ }
+
+ kfree(mixer_str);
+done:
+ return ret;
+
+}
+
+static int msm_pcm_playback_dnmix_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int msm_pcm_playback_dnmix_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(struct asm_stream_pan_ctrl_params);
+ return 0;
+}
+
+static int msm_pcm_playback_dnmix_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int len = 0;
+
+ struct snd_soc_component *usr_info = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform;
+ struct msm_plat_data *pdata;
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
+ struct asm_stream_pan_ctrl_params dnmix_param;
+ char *usr_value;
+ int be_id = 0;
+ int stream_id = 0;
+
+ if (!usr_info) {
+ pr_err("%s usr_info is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ platform = snd_soc_component_to_platform(usr_info);
+ if (!platform) {
+ pr_err("%s platform is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ pdata = dev_get_drvdata(platform->dev);
+ if (!pdata) {
+ pr_err("%s pdata is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (!substream->runtime) {
+ pr_err("%s substream runtime not found\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ prtd = substream->runtime->private_data;
+ if (!prtd) {
+ ret = -EINVAL;
+ goto done;
+ }
+ usr_value = (char *) ucontrol->value.bytes.data;
+ if (!usr_value) {
+ pr_err("%s usrvalue is null\n", __func__);
+ goto done;
+ }
+ memcpy(&be_id, usr_value, sizeof(be_id));
+ len += sizeof(be_id);
+ stream_id = prtd->audio_client->session;
+ memcpy(&dnmix_param.num_output_channels, &usr_value[len],
+ sizeof(dnmix_param.num_output_channels));
+ len += sizeof(dnmix_param.num_output_channels);
+ if (dnmix_param.num_output_channels >
+ PCM_FORMAT_MAX_NUM_CHANNEL) {
+ ret = -EINVAL;
+ goto done;
+ }
+ memcpy(&dnmix_param.num_input_channels, &usr_value[len],
+ sizeof(dnmix_param.num_input_channels));
+ len += sizeof(dnmix_param.num_input_channels);
+ if (dnmix_param.num_input_channels >
+ PCM_FORMAT_MAX_NUM_CHANNEL) {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (usr_value[len++]) {
+ memcpy(dnmix_param.output_channel_map, &usr_value[len],
+ (dnmix_param.num_output_channels *
+ sizeof(dnmix_param.output_channel_map[0])));
+ len += (dnmix_param.num_output_channels *
+ sizeof(dnmix_param.output_channel_map[0]));
+ }
+ if (usr_value[len++]) {
+ memcpy(dnmix_param.input_channel_map, &usr_value[len],
+ (dnmix_param.num_input_channels *
+ sizeof(dnmix_param.input_channel_map[0])));
+ len += (dnmix_param.num_input_channels *
+ sizeof(dnmix_param.input_channel_map[0]));
+ }
+ if (usr_value[len++]) {
+ memcpy(dnmix_param.gain, (uint32_t *) &usr_value[len],
+ (dnmix_param.num_input_channels *
+ dnmix_param.num_output_channels *
+ sizeof(dnmix_param.gain[0])));
+ len += (dnmix_param.num_input_channels *
+ dnmix_param.num_output_channels * sizeof(dnmix_param.gain[0]));
+ }
+ msm_routing_set_downmix_control_data(be_id,
+ stream_id,
+ &dnmix_param);
+
+done:
+ return ret;
+}
+
+static int msm_add_device_down_mix_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ const char *playback_mixer_ctl_name = "Audio Device";
+ const char *deviceNo = "NN";
+ const char *suffix = "Downmix Control";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct msm_plat_data *pdata;
+ struct snd_kcontrol_new device_downmix_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_playback_dnmix_ctl_info,
+ .get = msm_pcm_playback_dnmix_ctl_get,
+ .put = msm_pcm_playback_dnmix_ctl_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ device_downmix_control[0].name = mixer_str;
+ device_downmix_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ pdata = dev_get_drvdata(rtd->platform->dev);
+ if (pdata) {
+ if (!pdata->pcm)
+ pdata->pcm = rtd->pcm;
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ device_downmix_control,
+ ARRAY_SIZE
+ (device_downmix_control));
+ if (ret < 0)
+ pr_err("%s: failed add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+ } else {
+ pr_err("%s: NULL pdata\n", __func__);
+ ret = -EINVAL;
+ }
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = ucontrol->value.integer.value[3];
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
+ int ret = 0;
+
+ cfg_data.app_type = ucontrol->value.integer.value[0];
+ cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
+ if (ucontrol->value.integer.value[2] != 0)
+ cfg_data.sample_rate = ucontrol->value.integer.value[2];
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, &cfg_data);
+ if (ret < 0)
+ pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = 0;
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ &be_id, &cfg_data);
+ if (ret < 0) {
+ pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[0] = cfg_data.app_type;
+ ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
+ ucontrol->value.integer.value[2] = cfg_data.sample_rate;
+ ucontrol->value.integer.value[3] = be_id;
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
+done:
+ return ret;
+}
+
+static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_pcm_usr *app_type_info;
+ struct snd_kcontrol *kctl;
+ const char *playback_mixer_ctl_name = "Audio Stream";
+ const char *capture_mixer_ctl_name = "Audio Stream Capture";
+ const char *deviceNo = "NN";
+ const char *suffix = "App Type Cfg";
+ int ctl_len, ret = 0;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ pr_debug("%s: Playback app type cntrl add\n", __func__);
+ ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 1, ctl_len, rtd->dai_link->be_id,
+ &app_type_info);
+ if (ret < 0) {
+ pr_err("%s: playback app type cntrl add failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ kctl = app_type_info->kctl;
+ snprintf(kctl->id.name, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ kctl->put = msm_pcm_playback_app_type_cfg_ctl_put;
+ kctl->get = msm_pcm_playback_app_type_cfg_ctl_get;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ pr_debug("%s: Capture app type cntrl add\n", __func__);
+ ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ NULL, 1, ctl_len, rtd->dai_link->be_id,
+ &app_type_info);
+ if (ret < 0) {
+ pr_err("%s: capture app type cntrl add failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ kctl = app_type_info->kctl;
+ snprintf(kctl->id.name, ctl_len, "%s %d %s",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix);
+ kctl->put = msm_pcm_capture_app_type_cfg_ctl_put;
+ kctl->get = msm_pcm_capture_app_type_cfg_ctl_get;
+ }
+
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int ret = 0;
+ int stream_id = 0;
+ int be_id = 0;
+ struct msm_audio *prtd = NULL;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ struct snd_pcm *pcm = NULL;
+ struct snd_pcm_substream *substream = NULL;
+ struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ pcm = pdata->pcm_device[fe_id];
+ if (!pcm) {
+ pr_err("%s invalid pcm handle for fe_id %llu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (session_type == SESSION_TYPE_RX)
+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ else
+ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ chmixer_pspd->enable = ucontrol->value.integer.value[0];
+ chmixer_pspd->rule = ucontrol->value.integer.value[1];
+ chmixer_pspd->input_channel = ucontrol->value.integer.value[2];
+ chmixer_pspd->output_channel = ucontrol->value.integer.value[3];
+ chmixer_pspd->port_idx = ucontrol->value.integer.value[4];
+
+ mutex_lock(&pdata->lock);
+ /* cache value and take effect during adm_open stage */
+ msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+ session_type,
+ chmixer_pspd);
+
+ if (substream->runtime) {
+ prtd = substream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s find invalid prtd fail\n", __func__);
+ return -EINVAL;
+ }
+
+ if (chmixer_pspd->enable) {
+ stream_id = prtd->audio_client->session;
+ be_id = chmixer_pspd->port_idx;
+ msm_pcm_routing_set_channel_mixer_runtime(be_id,
+ stream_id,
+ session_type,
+ chmixer_pspd);
+ }
+ }
+done:
+ mutex_unlock(&pdata->lock);
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ ucontrol->value.integer.value[0] = chmixer_pspd->enable;
+ ucontrol->value.integer.value[1] = chmixer_pspd->rule;
+ ucontrol->value.integer.value[2] = chmixer_pspd->input_channel;
+ ucontrol->value.integer.value[3] = chmixer_pspd->output_channel;
+ ucontrol->value.integer.value[4] = chmixer_pspd->port_idx;
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ chmixer_pspd->out_ch_map[i] =
+ ucontrol->value.integer.value[i];
+
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->out_ch_map[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ chmixer_pspd->in_ch_map[i] = ucontrol->value.integer.value[i];
+
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->in_ch_map[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int channel = (kcontrol->private_value >> 16) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V2) {
+ pr_err("%s: invalid channel number %d\n", __func__, channel);
+ return -EINVAL;
+ }
+
+ channel--;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ chmixer_pspd->channel_weight[channel][i] =
+ ucontrol->value.integer.value[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int channel = (kcontrol->private_value >> 16) & 0xFF;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(comp);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ int i = 0;
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V2) {
+ pr_err("%s: invalid channel number %d\n", __func__, channel);
+ return -EINVAL;
+ }
+
+ channel--;
+ chmixer_pspd = &(pdata->chmixer_pspd[fe_id][session_type]);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->channel_weight[channel][i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V2;
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 64;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_output_map_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *deviceNo = "NN";
+ const char *suffix = "ChMixer Output Map";
+ int ctl_len = 0;
+ int session_type = 0;
+ char *playback_mixer_str = NULL;
+ char *capture_mixer_str = NULL;
+ int ret = 0;
+ struct snd_kcontrol_new channel_mixer_output_map_control[2] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_output_map_info,
+ .put = msm_pcm_channel_mixer_output_map_ctl_put,
+ .get = msm_pcm_channel_mixer_output_map_ctl_get,
+ .private_value = 0,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_output_map_info,
+ .put = msm_pcm_channel_mixer_output_map_ctl_put,
+ .get = msm_pcm_channel_mixer_output_map_ctl_get,
+ .private_value = 0,
+ }
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ playback_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (playback_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_RX;
+ snprintf(playback_mixer_str, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_output_map_control[0].name = playback_mixer_str;
+ channel_mixer_output_map_control[0].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_output_map_control[0],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ capture_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (capture_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_TX;
+ snprintf(capture_mixer_str, ctl_len, "%s %d %s",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_output_map_control[1].name = capture_mixer_str;
+ channel_mixer_output_map_control[1].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_output_map_control[1],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ kfree(playback_mixer_str);
+ kfree(capture_mixer_str);
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V2;
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 64;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_input_map_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *deviceNo = "NN";
+ const char *suffix = "ChMixer Input Map";
+ int ctl_len = 0;
+ int session_type = 0;
+ char *playback_mixer_str = NULL;
+ char *capture_mixer_str = NULL;
+ int ret = 0;
+ struct snd_kcontrol_new channel_mixer_input_map_control[2] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_input_map_info,
+ .put = msm_pcm_channel_mixer_input_map_ctl_put,
+ .get = msm_pcm_channel_mixer_input_map_ctl_get,
+ .private_value = 0,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_input_map_info,
+ .put = msm_pcm_channel_mixer_input_map_ctl_put,
+ .get = msm_pcm_channel_mixer_input_map_ctl_get,
+ .private_value = 0,
+ }
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ playback_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (playback_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_RX;
+ snprintf(playback_mixer_str, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_input_map_control[0].name = playback_mixer_str;
+ channel_mixer_input_map_control[0].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_input_map_control[0],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ capture_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (capture_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_TX;
+ snprintf(capture_mixer_str, ctl_len, "%s %d %s",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_input_map_control[1].name = capture_mixer_str;
+ channel_mixer_input_map_control[1].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_input_map_control[1],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ kfree(playback_mixer_str);
+ kfree(capture_mixer_str);
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 5;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_cfg_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *deviceNo = "NN";
+ const char *suffix = "ChMixer Cfg";
+ int ctl_len = 0;
+ char *playback_mixer_str = NULL;
+ char *capture_mixer_str = NULL;
+ int session_type = 0;
+ int ret = 0;
+ struct msm_plat_data *pdata = NULL;
+ struct snd_kcontrol_new channel_mixer_cfg_control[2] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_cfg_info,
+ .put = msm_pcm_channel_mixer_cfg_ctl_put,
+ .get = msm_pcm_channel_mixer_cfg_ctl_get,
+ .private_value = 0,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_cfg_info,
+ .put = msm_pcm_channel_mixer_cfg_ctl_put,
+ .get = msm_pcm_channel_mixer_cfg_ctl_get,
+ .private_value = 0,
+ }
+ };
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(rtd->platform->dev);
+ if (pdata == NULL) {
+ pr_err("%s: platform data not populated\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pdata->pcm_device[rtd->dai_link->be_id] = rtd->pcm;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ playback_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (playback_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_RX;
+ snprintf(playback_mixer_str, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_cfg_control[0].name = playback_mixer_str;
+ channel_mixer_cfg_control[0].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_cfg_control[0],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ capture_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (capture_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_TX;
+ snprintf(capture_mixer_str, ctl_len, "%s %d %s",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix);
+ channel_mixer_cfg_control[1].name = capture_mixer_str;
+ channel_mixer_cfg_control[1].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_cfg_control[1],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ kfree(playback_mixer_str);
+ kfree(capture_mixer_str);
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_weight_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0x4000;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_weight_controls(
+ struct snd_soc_pcm_runtime *rtd,
+ int channel)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *deviceNo = "NN";
+ const char *channelNo = "NN";
+ const char *suffix = "ChMixer Weight Ch";
+ int ctl_len = 0;
+ int session_type = 0;
+ char *playback_mixer_str = NULL;
+ char *capture_mixer_str = NULL;
+ int ret = 0;
+ struct snd_kcontrol_new channel_mixer_weight_control[2] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_weight_info,
+ .put = msm_pcm_channel_mixer_weight_ctl_put,
+ .get = msm_pcm_channel_mixer_weight_ctl_get,
+ .private_value = 0,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_weight_info,
+ .put = msm_pcm_channel_mixer_weight_ctl_put,
+ .get = msm_pcm_channel_mixer_weight_ctl_get,
+ .private_value = 0,
+ }
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1 +
+ strlen(channelNo) + 1;
+ playback_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (playback_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_RX;
+ snprintf(playback_mixer_str, ctl_len, "%s %d %s %d",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix,
+ channel);
+ channel_mixer_weight_control[0].name = playback_mixer_str;
+ channel_mixer_weight_control[0].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8)
+ | (channel << 16);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_weight_control[0],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1 +
+ strlen(channelNo) + 1;
+ capture_mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (capture_mixer_str == NULL) {
+ pr_err("failed to allocate mixer ctrl str of len %d",
+ ctl_len);
+ goto done;
+ }
+ session_type = SESSION_TYPE_TX;
+ snprintf(capture_mixer_str, ctl_len, "%s %d %s %d",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix,
+ channel);
+ channel_mixer_weight_control[1].name = capture_mixer_str;
+ channel_mixer_weight_control[1].private_value =
+ (rtd->dai_link->be_id) | (session_type << 8)
+ | (channel << 16);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ &channel_mixer_weight_control[1],
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ kfree(playback_mixer_str);
+ kfree(capture_mixer_str);
+ return ret;
+}
+
+static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ int i, ret = 0;
+
+ ret = msm_pcm_add_channel_mixer_cfg_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add channel mixer cfg controls failed:%d\n",
+ __func__, ret);
+ ret = msm_pcm_add_channel_mixer_input_map_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add channel mixer input map controls failed:%d\n",
+ __func__, ret);
+ ret = msm_pcm_add_channel_mixer_output_map_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add channel mixer output map controls failed:%d\n",
+ __func__, ret);
+
+ for (i = 1; i <= PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ ret |= msm_pcm_add_channel_mixer_weight_controls(rtd, i);
+ if (ret)
+ pr_err("%s: pcm add channel mixer weight controls failed:%d\n",
+ __func__, ret);
+ return ret;
+}
+
+static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ ret = msm_pcm_add_chmap_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add controls failed:%d\n", __func__, ret);
+ ret = msm_pcm_add_app_type_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add app type controls failed:%d\n",
+ __func__, ret);
+ ret = msm_add_stream_pan_scale_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add pan scale controls failed:%d\n",
+ __func__, ret);
+ ret = msm_add_device_down_mix_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add dnmix controls failed:%d\n",
+ __func__, ret);
+ ret = msm_pcm_add_channel_mixer_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add channel mixer controls failed:%d\n",
+ __func__, ret);
+ return ret;
+}
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = msm_pcm_add_controls(rtd);
+ if (ret) {
+ pr_err("%s, kctl add failed:%d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = msm_pcm_add_volume_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm Volume Control %d\n",
+ __func__, ret);
+
+ ret = msm_pcm_add_compress_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm Compress Control %d\n",
+ __func__, ret);
+
+ ret = msm_pcm_add_audio_adsp_stream_cmd_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm ADSP Stream Cmd Control\n",
+ __func__);
+
+ ret = msm_pcm_add_audio_adsp_stream_callback_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm ADSP Stream Callback Control\n",
+ __func__);
+
+ return ret;
+}
+
+static snd_pcm_sframes_t msm_pcm_delay_blk(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct audio_client *ac = prtd->audio_client;
+ snd_pcm_sframes_t frames;
+ int ret;
+
+ ret = q6asm_get_path_delay(prtd->audio_client);
+ if (ret) {
+ pr_err("%s: get_path_delay failed, ret=%d\n", __func__, ret);
+ return 0;
+ }
+
+ /* convert microseconds to frames */
+ frames = ac->path_delay / 1000 * runtime->rate / 1000;
+
+ /* also convert the remainder from the initial division */
+ frames += ac->path_delay % 1000 * runtime->rate / 1000000;
+
+ /* overcompensate for the loss of precision (empirical) */
+ frames += 2;
+
+ return frames;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+ .delay_blk = msm_pcm_delay_blk,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+ int rc;
+ int id;
+ struct msm_plat_data *pdata;
+ const char *latency_level;
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,msm-pcm-dsp-id", &id);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: qcom,msm-pcm-dsp-id missing in DT node\n",
+ __func__);
+ return rc;
+ }
+
+ pdata = kzalloc(sizeof(struct msm_plat_data), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
+ return -ENOMEM;
+ }
+
+ if (of_property_read_bool(pdev->dev.of_node,
+ "qcom,msm-pcm-low-latency")) {
+
+ pdata->perf_mode = LOW_LATENCY_PCM_MODE;
+ rc = of_property_read_string(pdev->dev.of_node,
+ "qcom,latency-level", &latency_level);
+ if (!rc) {
+ if (!strcmp(latency_level, "ultra"))
+ pdata->perf_mode = ULTRA_LOW_LATENCY_PCM_MODE;
+ else if (!strcmp(latency_level, "ull-pp"))
+ pdata->perf_mode =
+ ULL_POST_PROCESSING_PCM_MODE;
+ }
+ } else {
+ pdata->perf_mode = LEGACY_PCM_MODE;
+ }
+
+ if (of_property_read_bool(pdev->dev.of_node,
+ "qcom,avs-version"))
+ pdata->avs_ver = true;
+ else
+ pdata->avs_ver = false;
+
+ pr_debug("%s: avs_ver = %d\n", __func__, pdata->avs_ver);
+
+ mutex_init(&pdata->lock);
+ dev_set_drvdata(&pdev->dev, pdata);
+
+ dev_dbg(&pdev->dev, "%s: dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ struct msm_plat_data *pdata;
+
+ pdata = dev_get_drvdata(&pdev->dev);
+ mutex_destroy(&pdata->lock);
+ kfree(pdata);
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+static const struct of_device_id msm_pcm_dt_match[] = {
+ {.compatible = "qcom,msm-pcm-dsp"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_pcm_dt_match);
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-dsp",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_pcm_dt_match,
+ },
+ .probe = msm_pcm_probe,
+ .remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
new file mode 100644
index 000000000000..f0a96484a03b
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ */
+
+#ifndef _MSM_PCM_H
+#define _MSM_PCM_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+#include "msm-pcm-routing-v2.h"
+
+/* Support unconventional sample rates 12000, 24000 as well */
+#define USE_RATE \
+ (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+extern int copy_count;
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used;
+ unsigned addr;
+};
+
+struct buffer_rec {
+ void *data;
+ unsigned int size;
+ unsigned int read;
+ unsigned int addr;
+};
+
+struct audio_locks {
+ spinlock_t event_lock;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t eos_wait;
+ wait_queue_head_t enable_wait;
+ wait_queue_head_t flush_wait;
+};
+
+struct msm_audio_in_frame_info {
+ uint32_t size;
+ uint32_t offset;
+};
+
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 122880
+#define PLAYBACK_MIN_PERIOD_SIZE 128
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 122880
+#define CAPTURE_MIN_PERIOD_SIZE 320
+
+struct msm_audio {
+ struct snd_pcm_substream *substream;
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ uint16_t source; /* Encoding source bit mask */
+
+ struct audio_client *audio_client;
+
+ uint16_t session_id;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t dsp_cnt;
+
+ int abort; /* set when error, like sample rate mismatch */
+
+ bool reset_event;
+ int enabled;
+ int close_ack;
+ int cmd_ack;
+ /*
+ * cmd_ack doesn't tell if paticular command has been sent so can't
+ * determine if it needs to wait for completion.
+ * Use cmd_pending instead when checking whether a command is been
+ * sent or not.
+ */
+ unsigned long cmd_pending;
+ atomic_t start;
+ atomic_t stop;
+ atomic_t out_count;
+ atomic_t in_count;
+ atomic_t out_needed;
+ atomic_t eos;
+ int out_head;
+ int periods;
+ int mmap_flag;
+ atomic_t pending_buffer;
+ bool set_channel_map;
+ char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL_V2];
+ int cmd_interrupt;
+ bool meta_data_mode;
+ uint32_t volume;
+ bool compress_enable;
+ /* array of frame info */
+ struct msm_audio_in_frame_info in_frame_info[CAPTURE_MAX_NUM_PERIODS];
+};
+
+struct output_meta_data_st {
+ uint32_t meta_data_length;
+ uint32_t frame_size;
+ uint32_t timestamp_lsw;
+ uint32_t timestamp_msw;
+ uint32_t reserved[12];
+};
+
+struct msm_plat_data {
+ int perf_mode;
+ bool avs_ver;
+ struct snd_pcm *pcm;
+ struct snd_pcm *pcm_device[MSM_FRONTEND_DAI_MM_SIZE];
+ struct msm_pcm_channel_mixer chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE][2];
+ struct mutex lock;
+};
+
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c
new file mode 100644
index 000000000000..1dc7d7218d42
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c
@@ -0,0 +1,137 @@
+/* Copyright (c) 2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <sound/hwdep.h>
+#include <sound/devdep_params.h>
+#include "msm-pcm-routing-devdep.h"
+#include "msm-ds2-dap-config.h"
+
+#ifdef CONFIG_SND_HWDEP
+static int msm_pcm_routing_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+ pr_debug("%s\n", __func__);
+ msm_ds2_dap_update_port_parameters(hw, file, true);
+ return 0;
+}
+
+static int msm_pcm_routing_hwdep_release(struct snd_hwdep *hw,
+ struct file *file)
+{
+ pr_debug("%s\n", __func__);
+ msm_ds2_dap_update_port_parameters(hw, file, false);
+ return 0;
+}
+
+static int msm_pcm_routing_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ void __user *argp = (void __user *)arg;
+ pr_debug("%s:cmd %x\n", __func__, cmd);
+ switch (cmd) {
+ case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM:
+ case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM:
+ case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND:
+ case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE:
+ msm_pcm_routing_acquire_lock();
+ ret = msm_ds2_dap_ioctl(hw, file, cmd, argp);
+ msm_pcm_routing_release_lock();
+ break;
+ case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER:
+ ret = msm_ds2_dap_ioctl(hw, file, cmd, argp);
+ break;
+ default:
+ pr_err("%s called with invalid control 0x%X\n", __func__, cmd);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm)
+{
+ pr_debug("%s\n", __func__);
+}
+
+#ifdef CONFIG_COMPAT
+static int msm_pcm_routing_hwdep_compat_ioctl(struct snd_hwdep *hw,
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = 0;
+ void __user *argp = (void __user *)arg;
+ pr_debug("%s:cmd %x\n", __func__, cmd);
+ switch (cmd) {
+ case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32:
+ case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32:
+ case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32:
+ case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32:
+ msm_pcm_routing_acquire_lock();
+ ret = msm_ds2_dap_compat_ioctl(hw, file, cmd, argp);
+ msm_pcm_routing_release_lock();
+ break;
+ case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32:
+ ret = msm_ds2_dap_compat_ioctl(hw, file, cmd, argp);
+ break;
+ default:
+ pr_err("%s called with invalid control 0x%X\n", __func__, cmd);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+#endif
+
+int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime,
+ struct msm_pcm_routing_bdai_data *msm_bedais)
+{
+ struct snd_hwdep *hwdep;
+ struct snd_soc_dai_link *dai_link = runtime->dai_link;
+ int rc;
+
+ if (dai_link->be_id < 0 ||
+ dai_link->be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s:be_id %d invalid index\n",
+ __func__, dai_link->be_id);
+ return -EINVAL;
+ }
+ pr_debug("%s be_id %d\n", __func__, dai_link->be_id);
+ rc = snd_hwdep_new(runtime->card->snd_card,
+ msm_bedais[dai_link->be_id].name,
+ dai_link->be_id, &hwdep);
+ if (hwdep == NULL) {
+ pr_err("%s: hwdep intf failed to create %s- hwdep NULL\n",
+ __func__, msm_bedais[dai_link->be_id].name);
+ return rc;
+ }
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: hwdep intf failed to create %s rc %d\n", __func__,
+ msm_bedais[dai_link->be_id].name, rc);
+ return rc;
+ }
+
+ hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_BE;
+ hwdep->private_data = &msm_bedais[dai_link->be_id];
+ hwdep->ops.open = msm_pcm_routing_hwdep_open;
+ hwdep->ops.ioctl = msm_pcm_routing_hwdep_ioctl;
+ hwdep->ops.release = msm_pcm_routing_hwdep_release;
+#ifdef CONFIG_COMPAT
+ hwdep->ops.ioctl_compat = msm_pcm_routing_hwdep_compat_ioctl;
+#endif
+ return rc;
+}
+#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h
new file mode 100644
index 000000000000..2bb0579a53f6
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#ifndef _MSM_PCM_ROUTING_DEVDEP_H_
+#define _MSM_PCM_ROUTING_DEVDEP_H_
+
+#include <sound/soc.h>
+#include "msm-pcm-routing-v2.h"
+
+#ifdef CONFIG_SND_HWDEP
+int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime,
+ struct msm_pcm_routing_bdai_data *msm_bedais);
+void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm);
+#else
+static inline int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime,
+ struct msm_pcm_routing_bdai_data *msm_bedais)
+{
+ return 0;
+}
+
+static inline void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm)
+{
+ return;
+}
+#endif
+#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
new file mode 100644
index 000000000000..8e65e2ae3684
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -0,0 +1,22571 @@
+/* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6common.h>
+#include <sound/tlv.h>
+#include <sound/asound.h>
+#include <sound/pcm_params.h>
+#include <sound/q6core.h>
+#include <sound/audio_cal_utils.h>
+#include <sound/audio_effects.h>
+#include <sound/hwdep.h>
+#include <sound/q6adm-v2.h>
+#include <sound/apr_audio-v2.h>
+
+#include "msm-pcm-routing-v2.h"
+#include "msm-pcm-routing-devdep.h"
+#include "msm-qti-pp-config.h"
+#include "msm-dts-srs-tm-config.h"
+#include "msm-dolby-dap-config.h"
+#include "msm-ds2-dap-config.h"
+#include "q6voice.h"
+#include "sound/q6lsm.h"
+
+#ifndef CONFIG_DOLBY_DAP
+#undef DOLBY_ADM_COPP_TOPOLOGY_ID
+#define DOLBY_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFE
+#endif
+
+#ifndef CONFIG_DOLBY_DS2
+#undef DS2_ADM_COPP_TOPOLOGY_ID
+#define DS2_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFF
+#endif
+
+static struct mutex routing_lock;
+
+static struct cal_type_data *cal_data;
+
+static int fm_switch_enable;
+static int hfp_switch_enable;
+static int int0_mi2s_switch_enable;
+static int int4_mi2s_switch_enable;
+static int pri_mi2s_switch_enable;
+static int sec_mi2s_switch_enable;
+static int tert_mi2s_switch_enable;
+static int quat_mi2s_switch_enable;
+static int fm_pcmrx_switch_enable;
+static int usb_switch_enable;
+static int lsm_port_index;
+static int slim0_rx_aanc_fb_port;
+static int msm_route_ec_ref_rx;
+static int msm_ec_ref_ch = 4;
+static int msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_ec_ref_sampling_rate = 48000;
+static uint32_t voc_session_id = ALL_SESSION_VSID;
+static int msm_route_ext_ec_ref;
+static bool is_custom_stereo_on;
+static bool is_ds2_on;
+static bool swap_ch;
+static int msm_native_mode;
+
+#define WEIGHT_0_DB 0x4000
+/* all the FEs which can support channel mixer */
+static struct msm_pcm_channel_mixer channel_mixer[MSM_FRONTEND_DAI_MM_SIZE];
+
+/* all the FES which can support channel mixer for bidirection */
+static struct msm_pcm_channel_mixer
+ channel_mixer_v2[MSM_FRONTEND_DAI_MM_SIZE][2];
+
+/* input BE for each FE */
+static int channel_input[MSM_FRONTEND_DAI_MM_SIZE][ADM_MAX_CHANNELS];
+
+enum {
+ MADNONE,
+ MADAUDIO,
+ MADBEACON,
+ MADULTRASOUND,
+ MADSWAUDIO,
+};
+
+#define ADM_LSM_PORT_INDEX 9
+
+#define SLIMBUS_0_TX_TEXT "SLIMBUS_0_TX"
+#define SLIMBUS_1_TX_TEXT "SLIMBUS_1_TX"
+#define SLIMBUS_2_TX_TEXT "SLIMBUS_2_TX"
+#define SLIMBUS_3_TX_TEXT "SLIMBUS_3_TX"
+#define SLIMBUS_4_TX_TEXT "SLIMBUS_4_TX"
+#define SLIMBUS_5_TX_TEXT "SLIMBUS_5_TX"
+#define TERT_MI2S_TX_TEXT "TERT_MI2S_TX"
+#define QUAT_MI2S_TX_TEXT "QUAT_MI2S_TX"
+#define ADM_LSM_TX_TEXT "ADM_LSM_TX"
+#define INT3_MI2S_TX_TEXT "INT3_MI2S_TX"
+#define QUAT_TDM_TX_0_TEXT "QUAT_TDM_TX_0"
+
+#define LSM_FUNCTION_TEXT "LSM Function"
+static const char * const lsm_port_text[] = {
+ "None",
+ SLIMBUS_0_TX_TEXT, SLIMBUS_1_TX_TEXT, SLIMBUS_2_TX_TEXT,
+ SLIMBUS_3_TX_TEXT, SLIMBUS_4_TX_TEXT, SLIMBUS_5_TX_TEXT,
+ TERT_MI2S_TX_TEXT, QUAT_MI2S_TX_TEXT, ADM_LSM_TX_TEXT,
+ INT3_MI2S_TX_TEXT, QUAT_TDM_TX_0_TEXT
+};
+
+struct msm_pcm_route_bdai_pp_params {
+ unsigned long pp_params_config;
+ bool mute_on;
+ int latency;
+};
+
+static struct msm_pcm_route_bdai_pp_params
+ msm_bedais_pp_params[MSM_BACKEND_DAI_MAX];
+
+/*
+ * The be_dai_name_table is passed to HAL so that it can specify the
+ * BE ID for the BE it wants to enable based on the name. Thus there
+ * is a matching table and structure in HAL that need to be updated
+ * if any changes to these are made.
+ */
+struct msm_pcm_route_bdai_name {
+ unsigned int be_id;
+ char be_name[LPASS_BE_NAME_MAX_LENGTH];
+};
+static struct msm_pcm_route_bdai_name be_dai_name_table[MSM_BACKEND_DAI_MAX];
+
+static bool msm_pcm_routing_test_pp_param(int be_idx, long param_bit);
+static int msm_routing_send_device_pp_params(int port_id, int copp_idx,
+ int fe_id);
+
+static int msm_routing_get_bit_width(unsigned int format)
+{
+ int bit_width;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bit_width = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bit_width = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bit_width = 16;
+ }
+ return bit_width;
+}
+
+static bool msm_is_resample_needed(int input_sr, int output_sr)
+{
+ bool rc = false;
+
+ if (input_sr != output_sr)
+ rc = true;
+
+ pr_debug("perform resampling (%s) for copp rate (%d)afe rate (%d)",
+ (rc ? "oh yes" : "not really"),
+ input_sr, output_sr);
+
+ return rc;
+}
+
+static void msm_pcm_routing_cfg_pp(int port_id, int copp_idx, int topology,
+ int channels)
+{
+ int rc = 0;
+ switch (topology) {
+ case SRS_TRUMEDIA_TOPOLOGY_ID:
+ pr_debug("%s: SRS_TRUMEDIA_TOPOLOGY_ID\n", __func__);
+ msm_dts_srs_tm_init(port_id, copp_idx);
+ break;
+ case DS2_ADM_COPP_TOPOLOGY_ID:
+ pr_debug("%s: DS2_ADM_COPP_TOPOLOGY %d\n",
+ __func__, DS2_ADM_COPP_TOPOLOGY_ID);
+ rc = msm_ds2_dap_init(port_id, copp_idx, channels,
+ is_custom_stereo_on);
+ if (rc < 0)
+ pr_err("%s: DS2 topo_id 0x%x, port %d, CS %d rc %d\n",
+ __func__, topology, port_id,
+ is_custom_stereo_on, rc);
+ break;
+ case DOLBY_ADM_COPP_TOPOLOGY_ID:
+ if (is_ds2_on) {
+ pr_debug("%s: DS2_ADM_COPP_TOPOLOGY\n", __func__);
+ rc = msm_ds2_dap_init(port_id, copp_idx, channels,
+ is_custom_stereo_on);
+ if (rc < 0)
+ pr_err("%s:DS2 topo_id 0x%x, port %d, rc %d\n",
+ __func__, topology, port_id, rc);
+ } else {
+ pr_debug("%s: DOLBY_ADM_COPP_TOPOLOGY_ID\n", __func__);
+ rc = msm_dolby_dap_init(port_id, copp_idx, channels,
+ is_custom_stereo_on);
+ if (rc < 0)
+ pr_err("%s: DS1 topo_id 0x%x, port %d, rc %d\n",
+ __func__, topology, port_id, rc);
+ }
+ break;
+ case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE:
+ pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__);
+ rc = msm_qti_pp_asphere_init(port_id, copp_idx);
+ if (rc < 0)
+ pr_err("%s: topo_id 0x%x, port %d, copp %d, rc %d\n",
+ __func__, topology, port_id, copp_idx, rc);
+ break;
+ default:
+ /* custom topology specific feature param handlers */
+ break;
+ }
+}
+
+static void msm_pcm_routing_deinit_pp(int port_id, int topology)
+{
+ switch (topology) {
+ case SRS_TRUMEDIA_TOPOLOGY_ID:
+ pr_debug("%s: SRS_TRUMEDIA_TOPOLOGY_ID\n", __func__);
+ msm_dts_srs_tm_deinit(port_id);
+ break;
+ case DS2_ADM_COPP_TOPOLOGY_ID:
+ pr_debug("%s: DS2_ADM_COPP_TOPOLOGY_ID %d\n",
+ __func__, DS2_ADM_COPP_TOPOLOGY_ID);
+ msm_ds2_dap_deinit(port_id);
+ break;
+ case DOLBY_ADM_COPP_TOPOLOGY_ID:
+ if (is_ds2_on) {
+ pr_debug("%s: DS2_ADM_COPP_TOPOLOGY_ID\n", __func__);
+ msm_ds2_dap_deinit(port_id);
+ } else {
+ pr_debug("%s: DOLBY_ADM_COPP_TOPOLOGY_ID\n", __func__);
+ msm_dolby_dap_deinit(port_id);
+ }
+ break;
+ case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE:
+ pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__);
+ msm_qti_pp_asphere_deinit(port_id);
+ break;
+ default:
+ /* custom topology specific feature deinit handlers */
+ break;
+ }
+}
+
+static void msm_pcm_routng_cfg_matrix_map_pp(struct route_payload payload,
+ int path_type, int perf_mode)
+{
+ int itr = 0, rc = 0;
+ if ((path_type == ADM_PATH_PLAYBACK) &&
+ (perf_mode == LEGACY_PCM_MODE) &&
+ is_custom_stereo_on) {
+ for (itr = 0; itr < payload.num_copps; itr++) {
+ if ((payload.port_id[itr] != SLIMBUS_0_RX) &&
+ (payload.port_id[itr] != RT_PROXY_PORT_001_RX)) {
+ continue;
+ }
+
+ rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd(
+ payload.port_id[itr],
+ payload.copp_idx[itr],
+ payload.session_id,
+ Q14_GAIN_ZERO_POINT_FIVE,
+ Q14_GAIN_ZERO_POINT_FIVE,
+ Q14_GAIN_ZERO_POINT_FIVE,
+ Q14_GAIN_ZERO_POINT_FIVE);
+ if (rc < 0)
+ pr_err("%s: err setting custom stereo\n",
+ __func__);
+ }
+ }
+}
+
+#define SLIMBUS_EXTPROC_RX AFE_PORT_INVALID
+struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
+ { PRIMARY_I2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_PRI_I2S_RX},
+ { PRIMARY_I2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_PRI_I2S_TX},
+ { SLIMBUS_0_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_0_RX},
+ { SLIMBUS_0_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_0_TX},
+ { HDMI_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_HDMI},
+ { INT_BT_SCO_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_INT_BT_SCO_RX},
+ { INT_BT_SCO_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_INT_BT_SCO_TX},
+ { INT_FM_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_INT_FM_RX},
+ { INT_FM_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_INT_FM_TX},
+ { RT_PROXY_PORT_001_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_AFE_PCM_RX},
+ { RT_PROXY_PORT_001_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_AFE_PCM_TX},
+ { AFE_PORT_ID_PRIMARY_PCM_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_AUXPCM_RX},
+ { AFE_PORT_ID_PRIMARY_PCM_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_AUXPCM_TX},
+ { VOICE_PLAYBACK_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_VOICE_PLAYBACK_TX},
+ { VOICE2_PLAYBACK_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_VOICE2_PLAYBACK_TX},
+ { VOICE_RECORD_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INCALL_RECORD_RX},
+ { VOICE_RECORD_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INCALL_RECORD_TX},
+ { MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_MI2S_RX},
+ { MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_MI2S_TX},
+ { SECONDARY_I2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SEC_I2S_RX},
+ { SLIMBUS_1_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_1_RX},
+ { SLIMBUS_1_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_1_TX},
+ { SLIMBUS_2_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_2_RX},
+ { SLIMBUS_2_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_2_TX},
+ { SLIMBUS_3_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_3_RX},
+ { SLIMBUS_3_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_3_TX},
+ { SLIMBUS_4_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_4_RX},
+ { SLIMBUS_4_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_4_TX},
+ { SLIMBUS_5_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_5_RX},
+ { SLIMBUS_5_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_5_TX},
+ { SLIMBUS_6_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_6_RX},
+ { SLIMBUS_6_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_6_TX},
+ { SLIMBUS_7_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_7_RX},
+ { SLIMBUS_7_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_7_TX},
+ { SLIMBUS_8_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_8_RX},
+ { SLIMBUS_8_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_SLIMBUS_8_TX},
+ { SLIMBUS_EXTPROC_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_STUB_RX},
+ { SLIMBUS_EXTPROC_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_STUB_TX},
+ { SLIMBUS_EXTPROC_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, LPASS_BE_STUB_1_TX},
+ { AFE_PORT_ID_QUATERNARY_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_MI2S_RX},
+ { AFE_PORT_ID_QUATERNARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_MI2S_TX},
+ { AFE_PORT_ID_SECONDARY_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_MI2S_RX},
+ { AFE_PORT_ID_SECONDARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_MI2S_TX},
+ { AFE_PORT_ID_PRIMARY_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_MI2S_RX},
+ { AFE_PORT_ID_PRIMARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_MI2S_TX},
+ { AFE_PORT_ID_TERTIARY_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_MI2S_RX},
+ { AFE_PORT_ID_TERTIARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_MI2S_TX},
+ { AUDIO_PORT_ID_I2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_AUDIO_I2S_RX},
+ { AFE_PORT_ID_SECONDARY_PCM_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_AUXPCM_RX},
+ { AFE_PORT_ID_SECONDARY_PCM_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_AUXPCM_TX},
+ { AFE_PORT_ID_SPDIF_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SPDIF_RX},
+ { AFE_PORT_ID_SECONDARY_MI2S_RX_SD1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_MI2S_RX_SD1},
+ { AFE_PORT_ID_QUINARY_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUIN_MI2S_RX},
+ { AFE_PORT_ID_QUINARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUIN_MI2S_TX},
+ { AFE_PORT_ID_SENARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SENARY_MI2S_TX},
+ { AFE_PORT_ID_PRIMARY_TDM_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_RX_0},
+ { AFE_PORT_ID_PRIMARY_TDM_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_TX_0},
+ { AFE_PORT_ID_PRIMARY_TDM_RX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_RX_1},
+ { AFE_PORT_ID_PRIMARY_TDM_TX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_TX_1},
+ { AFE_PORT_ID_PRIMARY_TDM_RX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_RX_2},
+ { AFE_PORT_ID_PRIMARY_TDM_TX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_TX_2},
+ { AFE_PORT_ID_PRIMARY_TDM_RX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_RX_3},
+ { AFE_PORT_ID_PRIMARY_TDM_TX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_TX_3},
+ { AFE_PORT_ID_PRIMARY_TDM_RX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_RX_4},
+ { AFE_PORT_ID_PRIMARY_TDM_TX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_TX_4},
+ { AFE_PORT_ID_PRIMARY_TDM_RX_5, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_RX_5},
+ { AFE_PORT_ID_PRIMARY_TDM_TX_5, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_TX_5},
+ { AFE_PORT_ID_PRIMARY_TDM_RX_6, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_RX_6},
+ { AFE_PORT_ID_PRIMARY_TDM_TX_6, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_TX_6},
+ { AFE_PORT_ID_PRIMARY_TDM_RX_7, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_RX_7},
+ { AFE_PORT_ID_PRIMARY_TDM_TX_7, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PRI_TDM_TX_7},
+ { AFE_PORT_ID_SECONDARY_TDM_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_RX_0},
+ { AFE_PORT_ID_SECONDARY_TDM_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_TX_0},
+ { AFE_PORT_ID_SECONDARY_TDM_RX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_RX_1},
+ { AFE_PORT_ID_SECONDARY_TDM_TX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_TX_1},
+ { AFE_PORT_ID_SECONDARY_TDM_RX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_RX_2},
+ { AFE_PORT_ID_SECONDARY_TDM_TX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_TX_2},
+ { AFE_PORT_ID_SECONDARY_TDM_RX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_RX_3},
+ { AFE_PORT_ID_SECONDARY_TDM_TX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_TX_3},
+ { AFE_PORT_ID_SECONDARY_TDM_RX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_RX_4},
+ { AFE_PORT_ID_SECONDARY_TDM_TX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_TX_4},
+ { AFE_PORT_ID_SECONDARY_TDM_RX_5, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_RX_5},
+ { AFE_PORT_ID_SECONDARY_TDM_TX_5, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_TX_5},
+ { AFE_PORT_ID_SECONDARY_TDM_RX_6, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_RX_6},
+ { AFE_PORT_ID_SECONDARY_TDM_TX_6, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_TX_6},
+ { AFE_PORT_ID_SECONDARY_TDM_RX_7, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_RX_7},
+ { AFE_PORT_ID_SECONDARY_TDM_TX_7, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_TDM_TX_7},
+ { AFE_PORT_ID_TERTIARY_TDM_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_RX_0},
+ { AFE_PORT_ID_TERTIARY_TDM_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_TX_0},
+ { AFE_PORT_ID_TERTIARY_TDM_RX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_RX_1},
+ { AFE_PORT_ID_TERTIARY_TDM_TX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_TX_1},
+ { AFE_PORT_ID_TERTIARY_TDM_RX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_RX_2},
+ { AFE_PORT_ID_TERTIARY_TDM_TX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_TX_2},
+ { AFE_PORT_ID_TERTIARY_TDM_RX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_RX_3},
+ { AFE_PORT_ID_TERTIARY_TDM_TX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_TX_3},
+ { AFE_PORT_ID_TERTIARY_TDM_RX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_RX_4},
+ { AFE_PORT_ID_TERTIARY_TDM_TX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_TX_4},
+ { AFE_PORT_ID_TERTIARY_TDM_RX_5, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_RX_5},
+ { AFE_PORT_ID_TERTIARY_TDM_TX_5, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_TX_5},
+ { AFE_PORT_ID_TERTIARY_TDM_RX_6, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_RX_6},
+ { AFE_PORT_ID_TERTIARY_TDM_TX_6, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_TX_6},
+ { AFE_PORT_ID_TERTIARY_TDM_RX_7, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_RX_7},
+ { AFE_PORT_ID_TERTIARY_TDM_TX_7, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_TDM_TX_7},
+ { AFE_PORT_ID_QUATERNARY_TDM_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_RX_0},
+ { AFE_PORT_ID_QUATERNARY_TDM_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_TX_0},
+ { AFE_PORT_ID_QUATERNARY_TDM_RX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_RX_1},
+ { AFE_PORT_ID_QUATERNARY_TDM_TX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_TX_1},
+ { AFE_PORT_ID_QUATERNARY_TDM_RX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_RX_2},
+ { AFE_PORT_ID_QUATERNARY_TDM_TX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_TX_2},
+ { AFE_PORT_ID_QUATERNARY_TDM_RX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_RX_3},
+ { AFE_PORT_ID_QUATERNARY_TDM_TX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_TX_3},
+ { AFE_PORT_ID_QUATERNARY_TDM_RX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_RX_4},
+ { AFE_PORT_ID_QUATERNARY_TDM_TX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_TX_4},
+ { AFE_PORT_ID_QUATERNARY_TDM_RX_5, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_RX_5},
+ { AFE_PORT_ID_QUATERNARY_TDM_TX_5, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_TX_5},
+ { AFE_PORT_ID_QUATERNARY_TDM_RX_6, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_RX_6},
+ { AFE_PORT_ID_QUATERNARY_TDM_TX_6, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_TX_6},
+ { AFE_PORT_ID_QUATERNARY_TDM_RX_7, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_RX_7},
+ { AFE_PORT_ID_QUATERNARY_TDM_TX_7, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_TDM_TX_7},
+ { INT_BT_A2DP_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT_BT_A2DP_RX},
+ { AFE_PORT_ID_USB_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_USB_AUDIO_RX},
+ { AFE_PORT_ID_USB_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_USB_AUDIO_TX},
+ { DISPLAY_PORT_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_DISPLAY_PORT},
+ { AFE_PORT_ID_TERTIARY_PCM_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_AUXPCM_RX},
+ { AFE_PORT_ID_TERTIARY_PCM_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_AUXPCM_TX},
+ { AFE_PORT_ID_QUATERNARY_PCM_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_AUXPCM_RX},
+ { AFE_PORT_ID_QUATERNARY_PCM_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_AUXPCM_TX},
+ { AFE_PORT_ID_INT0_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT0_MI2S_RX},
+ { AFE_PORT_ID_INT0_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT0_MI2S_TX},
+ { AFE_PORT_ID_INT1_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT1_MI2S_RX},
+ { AFE_PORT_ID_INT1_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT1_MI2S_TX},
+ { AFE_PORT_ID_INT2_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT2_MI2S_RX},
+ { AFE_PORT_ID_INT2_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT2_MI2S_TX},
+ { AFE_PORT_ID_INT3_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT3_MI2S_RX},
+ { AFE_PORT_ID_INT3_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT3_MI2S_TX},
+ { AFE_PORT_ID_INT4_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT4_MI2S_RX},
+ { AFE_PORT_ID_INT4_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT4_MI2S_TX},
+ { AFE_PORT_ID_INT5_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT5_MI2S_RX},
+ { AFE_PORT_ID_INT5_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT5_MI2S_TX},
+ { AFE_PORT_ID_INT6_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT6_MI2S_RX},
+ { AFE_PORT_ID_INT6_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_INT6_MI2S_TX},
+ { AFE_PORT_ID_SECONDARY_MI2S_RX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_MI2S_RX_1},
+ { AFE_PORT_ID_SECONDARY_MI2S_RX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_MI2S_RX_2},
+ { AFE_PORT_ID_SECONDARY_MI2S_RX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_MI2S_RX_3},
+ { AFE_PORT_ID_SECONDARY_MI2S_RX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_MI2S_RX_4},
+ { AFE_PORT_ID_SECONDARY_MI2S_TX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_MI2S_TX_1},
+ { AFE_PORT_ID_SECONDARY_MI2S_TX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_MI2S_TX_2},
+ { AFE_PORT_ID_SECONDARY_MI2S_TX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_MI2S_TX_3},
+ { AFE_PORT_ID_SECONDARY_MI2S_TX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_SEC_MI2S_TX_4},
+ { AFE_PORT_ID_TERTIARY_MI2S_RX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_MI2S_RX_1},
+ { AFE_PORT_ID_TERTIARY_MI2S_RX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_MI2S_RX_2},
+ { AFE_PORT_ID_TERTIARY_MI2S_RX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_MI2S_RX_3},
+ { AFE_PORT_ID_TERTIARY_MI2S_RX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_MI2S_RX_4},
+ { AFE_PORT_ID_TERTIARY_MI2S_TX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_MI2S_TX_1},
+ { AFE_PORT_ID_TERTIARY_MI2S_TX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_MI2S_TX_2},
+ { AFE_PORT_ID_TERTIARY_MI2S_TX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_MI2S_TX_3},
+ { AFE_PORT_ID_TERTIARY_MI2S_TX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_TERT_MI2S_TX_4},
+ { AFE_PORT_ID_QUATERNARY_MI2S_RX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_MI2S_RX_1},
+ { AFE_PORT_ID_QUATERNARY_MI2S_RX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_MI2S_RX_2},
+ { AFE_PORT_ID_QUATERNARY_MI2S_RX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_MI2S_RX_3},
+ { AFE_PORT_ID_QUATERNARY_MI2S_RX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_MI2S_RX_4},
+ { AFE_PORT_ID_QUATERNARY_MI2S_TX_1, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_MI2S_TX_1},
+ { AFE_PORT_ID_QUATERNARY_MI2S_TX_2, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_MI2S_TX_2},
+ { AFE_PORT_ID_QUATERNARY_MI2S_TX_3, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_MI2S_TX_3},
+ { AFE_PORT_ID_QUATERNARY_MI2S_TX_4, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_QUAT_MI2S_TX_4},
+ { RT_PROXY_PORT_002_RX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PROXY_RX},
+ { RT_PROXY_PORT_002_TX, 0, {0}, {0}, 0, 0, 0, 0, {0},
+ LPASS_BE_PROXY_TX}
+};
+
+/* Track ASM playback & capture sessions of DAI
+ * Track LSM listen sessions
+ */
+static struct msm_pcm_routing_fdai_data
+ fe_dai_map[MSM_FRONTEND_DAI_MAX][2] = {
+ /* MULTIMEDIA1 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA2 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA3 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA4 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA5 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA6 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA7*/
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA8 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA9 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA10 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA11 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA12 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA13 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA14 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA15 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA16 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA17 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA18 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA19 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA20 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA21 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA22 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA23 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA24 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA25 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA26 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA27 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA28 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA29 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA30 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA31 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA32 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* MULTIMEDIA33 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* CS_VOICE */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* VOIP */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* AFE_RX */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* AFE_TX */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* VOICE_STUB */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* VOLTE */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* DTMF_RX */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* VOICE2 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* QCHAT */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* VOLTE_STUB */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* LSM1 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* LSM2 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* LSM3 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* LSM4 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* LSM5 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* LSM6 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* LSM7 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* LSM8 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* VOICE2_STUB */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* VOWLAN */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* VOICEMMODE1 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+ /* VOICEMMODE2 */
+ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+ {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+};
+
+static unsigned long session_copp_map[MSM_FRONTEND_DAI_MAX][2]
+ [MSM_BACKEND_DAI_MAX];
+static struct msm_pcm_routing_app_type_data app_type_cfg[MAX_APP_TYPES];
+static struct msm_pcm_routing_app_type_data lsm_app_type_cfg[MAX_APP_TYPES];
+static struct msm_pcm_stream_app_type_cfg
+ fe_dai_app_type_cfg[MSM_FRONTEND_DAI_MAX][2][MSM_BACKEND_DAI_MAX];
+
+static int last_be_id_configured[MSM_FRONTEND_DAI_MAX][MAX_SESSION_TYPES];
+
+/* The caller of this should aqcuire routing lock */
+void msm_pcm_routing_get_bedai_info(int be_idx,
+ struct msm_pcm_routing_bdai_data *be_dai)
+{
+ if (be_idx >= 0 && be_idx < MSM_BACKEND_DAI_MAX)
+ memcpy(be_dai, &msm_bedais[be_idx],
+ sizeof(struct msm_pcm_routing_bdai_data));
+}
+
+/* The caller of this should aqcuire routing lock */
+void msm_pcm_routing_get_fedai_info(int fe_idx, int sess_type,
+ struct msm_pcm_routing_fdai_data *fe_dai)
+{
+ if ((sess_type == SESSION_TYPE_TX) || (sess_type == SESSION_TYPE_RX))
+ memcpy(fe_dai, &fe_dai_map[fe_idx][sess_type],
+ sizeof(struct msm_pcm_routing_fdai_data));
+}
+
+void msm_pcm_routing_acquire_lock(void)
+{
+ mutex_lock(&routing_lock);
+}
+
+void msm_pcm_routing_release_lock(void)
+{
+ mutex_unlock(&routing_lock);
+}
+
+static int msm_pcm_routing_get_app_type_idx(int app_type)
+{
+ int idx;
+
+ pr_debug("%s: app_type: %d\n", __func__, app_type);
+ for (idx = 0; idx < MAX_APP_TYPES; idx++) {
+ if (app_type_cfg[idx].app_type == app_type)
+ return idx;
+ }
+ pr_info("%s: App type not available, fallback to default\n", __func__);
+ return 0;
+}
+
+static int msm_pcm_routing_get_lsm_app_type_idx(int app_type)
+{
+ int idx;
+
+ pr_debug("%s: app_type: %d\n", __func__, app_type);
+ for (idx = 0; idx < MAX_APP_TYPES; idx++) {
+ if (lsm_app_type_cfg[idx].app_type == app_type)
+ return idx;
+ }
+ pr_debug("%s: App type not available, fallback to default\n", __func__);
+ return 0;
+}
+
+static bool is_mm_lsm_fe_id(int fe_id)
+{
+ bool rc = true;
+
+ if (fe_id > MSM_FRONTEND_DAI_MM_MAX_ID &&
+ ((fe_id < MSM_FRONTEND_DAI_LSM1) ||
+ (fe_id > MSM_FRONTEND_DAI_LSM8))) {
+ rc = false;
+ }
+ return rc;
+}
+
+/**
+ * msm_pcm_routing_set_channel_mixer_cfg - cache channel mixer
+ * setting before use case start.
+ *
+ * @fe_id: frontend idx
+ * @type: stream direction type
+ * @params: parameters of channel mixer setting
+ *
+ * Return 0 for success
+ */
+int msm_pcm_routing_set_channel_mixer_cfg(
+ int fe_id, int type,
+ struct msm_pcm_channel_mixer *params)
+{
+ int i, j = 0;
+
+ channel_mixer_v2[fe_id][type].enable = params->enable;
+ channel_mixer_v2[fe_id][type].rule = params->rule;
+ channel_mixer_v2[fe_id][type].input_channel =
+ params->input_channel;
+ channel_mixer_v2[fe_id][type].output_channel =
+ params->output_channel;
+ channel_mixer_v2[fe_id][type].port_idx = params->port_idx;
+
+ for (i = 0; i < ADM_MAX_CHANNELS; i++)
+ channel_mixer_v2[fe_id][type].in_ch_map[i] =
+ params->in_ch_map[i];
+ for (i = 0; i < ADM_MAX_CHANNELS; i++)
+ channel_mixer_v2[fe_id][type].out_ch_map[i] =
+ params->out_ch_map[i];
+
+ for (i = 0; i < ADM_MAX_CHANNELS; i++)
+ for (j = 0; j < ADM_MAX_CHANNELS; j++)
+ channel_mixer_v2[fe_id][type].channel_weight[i][j] =
+ params->channel_weight[i][j];
+ channel_mixer_v2[fe_id][type].override_cfg = 1;
+ return 0;
+}
+EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_cfg);
+
+int msm_pcm_routing_reg_stream_app_type_cfg(
+ int fedai_id, int session_type, int be_id,
+ struct msm_pcm_stream_app_type_cfg *cfg_data)
+{
+ int ret = 0;
+
+ if (cfg_data == NULL) {
+ pr_err("%s: Received NULL pointer for cfg_data\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d copp_token %d\n",
+ __func__, fedai_id, session_type, be_id,
+ cfg_data->app_type, cfg_data->acdb_dev_id,
+ cfg_data->sample_rate, cfg_data->copp_token);
+
+ if (!is_mm_lsm_fe_id(fedai_id)) {
+ pr_err("%s: Invalid machine driver ID %d\n",
+ __func__, fedai_id);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (session_type != SESSION_TYPE_RX &&
+ session_type != SESSION_TYPE_TX) {
+ pr_err("%s: Invalid session type %d\n",
+ __func__, session_type);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: Received out of bounds be_id %d\n",
+ __func__, be_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ fe_dai_app_type_cfg[fedai_id][session_type][be_id] = *cfg_data;
+
+ /*
+ * Store the BE ID of the configuration information set as the latest so
+ * the get mixer control knows what to return.
+ */
+ last_be_id_configured[fedai_id][session_type] = be_id;
+
+done:
+ return ret;
+}
+EXPORT_SYMBOL(msm_pcm_routing_reg_stream_app_type_cfg);
+
+/**
+ * msm_pcm_routing_get_stream_app_type_cfg
+ *
+ * Receives fedai_id, session_type, be_id, and populates app_type,
+ * acdb_dev_id, & sample rate. Returns 0 on success. On failure returns
+ * -EINVAL and does not alter passed values.
+ *
+ * fedai_id - Passed value, front end ID for which app type config is wanted
+ * session_type - Passed value, session type for which app type config
+ * is wanted
+ * be_id - Returned value, back end device id the app type config data is for
+ * cfg_data - Returned value, configuration data used by app type config
+ */
+int msm_pcm_routing_get_stream_app_type_cfg(
+ int fedai_id, int session_type, int *bedai_id,
+ struct msm_pcm_stream_app_type_cfg *cfg_data)
+{
+ int be_id;
+ int ret = 0;
+
+ if (bedai_id == NULL) {
+ pr_err("%s: Received NULL pointer for backend ID\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ } else if (cfg_data == NULL) {
+ pr_err("%s: NULL pointer sent for cfg_data\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ } else if (!is_mm_lsm_fe_id(fedai_id)) {
+ pr_err("%s: Invalid FE ID %d\n", __func__, fedai_id);
+ ret = -EINVAL;
+ goto done;
+ } else if (session_type != SESSION_TYPE_RX &&
+ session_type != SESSION_TYPE_TX) {
+ pr_err("%s: Invalid session type %d\n", __func__, session_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ be_id = last_be_id_configured[fedai_id][session_type];
+ if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: Invalid BE ID %d\n", __func__, be_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ *bedai_id = be_id;
+ *cfg_data = fe_dai_app_type_cfg[fedai_id][session_type][be_id];
+ pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d copp_token %d\n",
+ __func__, fedai_id, session_type, *bedai_id,
+ cfg_data->app_type, cfg_data->acdb_dev_id,
+ cfg_data->sample_rate, cfg_data->copp_token);
+done:
+ return ret;
+}
+EXPORT_SYMBOL(msm_pcm_routing_get_stream_app_type_cfg);
+
+static struct cal_block_data *msm_routing_find_topology_by_path(int path)
+{
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block = NULL;
+ pr_debug("%s\n", __func__);
+
+ list_for_each_safe(ptr, next,
+ &cal_data->cal_blocks) {
+
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+
+ if (((struct audio_cal_info_adm_top *)cal_block->cal_info)
+ ->path == path) {
+ return cal_block;
+ }
+ }
+ pr_debug("%s: Can't find topology for path %d\n", __func__, path);
+ return NULL;
+}
+
+static struct cal_block_data *msm_routing_find_topology(int path,
+ int app_type,
+ int acdb_id)
+{
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block = NULL;
+ struct audio_cal_info_adm_top *cal_info;
+ pr_debug("%s\n", __func__);
+
+ list_for_each_safe(ptr, next,
+ &cal_data->cal_blocks) {
+
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+
+ cal_info = (struct audio_cal_info_adm_top *)
+ cal_block->cal_info;
+ if ((cal_info->path == path) &&
+ (cal_info->app_type == app_type) &&
+ (cal_info->acdb_id == acdb_id)) {
+ return cal_block;
+ }
+ }
+ pr_debug("%s: Can't find topology for path %d, app %d, acdb_id %d defaulting to search by path\n",
+ __func__, path, app_type, acdb_id);
+ return msm_routing_find_topology_by_path(path);
+}
+
+static int msm_routing_get_adm_topology(int fedai_id, int session_type,
+ int be_id)
+{
+ int topology = NULL_COPP_TOPOLOGY;
+ struct cal_block_data *cal_block = NULL;
+ int app_type = 0, acdb_dev_id = 0;
+
+ pr_debug("%s: fedai_id %d, session_type %d, be_id %d\n",
+ __func__, fedai_id, session_type, be_id);
+
+ if (cal_data == NULL)
+ goto done;
+
+ mutex_lock(&cal_data->lock);
+
+ app_type = fe_dai_app_type_cfg[fedai_id][session_type][be_id].app_type;
+ acdb_dev_id =
+ fe_dai_app_type_cfg[fedai_id][session_type][be_id].acdb_dev_id;
+
+ cal_block = msm_routing_find_topology(session_type, app_type,
+ acdb_dev_id);
+ if (cal_block == NULL)
+ goto unlock;
+
+ topology = ((struct audio_cal_info_adm_top *)
+ cal_block->cal_info)->topology;
+unlock:
+ mutex_unlock(&cal_data->lock);
+done:
+ pr_debug("%s: Using topology %d\n", __func__, topology);
+ return topology;
+}
+
+static uint8_t is_be_dai_extproc(int be_dai)
+{
+ if (be_dai == MSM_BACKEND_DAI_EXTPROC_RX ||
+ be_dai == MSM_BACKEND_DAI_EXTPROC_TX ||
+ be_dai == MSM_BACKEND_DAI_EXTPROC_EC_TX)
+ return 1;
+ else
+ return 0;
+}
+
+static void msm_pcm_routing_build_matrix(int fedai_id, int sess_type,
+ int path_type, int perf_mode,
+ uint32_t passthr_mode)
+{
+ int i, port_type, j, num_copps = 0;
+ struct route_payload payload = { {0} };
+
+ port_type = ((path_type == ADM_PATH_PLAYBACK ||
+ path_type == ADM_PATH_COMPRESSED_RX) ?
+ MSM_AFE_PORT_TYPE_RX : MSM_AFE_PORT_TYPE_TX);
+
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (!is_be_dai_extproc(i) &&
+ (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+ (msm_bedais[i].active) &&
+ (test_bit(fedai_id, &msm_bedais[i].fe_sessions[0]))) {
+ for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+ unsigned long copp =
+ session_copp_map[fedai_id][sess_type][i];
+ if (test_bit(j, &copp)) {
+ payload.port_id[num_copps] =
+ msm_bedais[i].port_id;
+ payload.copp_idx[num_copps] = j;
+ payload.app_type[num_copps] =
+ fe_dai_app_type_cfg
+ [fedai_id][sess_type][i]
+ .app_type;
+ payload.acdb_dev_id[num_copps] =
+ fe_dai_app_type_cfg
+ [fedai_id][sess_type][i]
+ .acdb_dev_id;
+ payload.sample_rate[num_copps] =
+ fe_dai_app_type_cfg
+ [fedai_id][sess_type][i]
+ .sample_rate;
+ if (msm_pcm_routing_test_pp_param(i,
+ ADM_PP_PARAM_LIMITER_BIT))
+ set_bit(ADM_STATUS_LIMITER,
+ &payload.route_status
+ [num_copps]);
+ num_copps++;
+ }
+ }
+ }
+ }
+
+ if (num_copps) {
+ payload.num_copps = num_copps;
+ payload.session_id = fe_dai_map[fedai_id][sess_type].strm_id;
+ adm_matrix_map(path_type, payload, perf_mode, passthr_mode);
+ msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode);
+ }
+}
+
+void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
+ int stream_type)
+{
+ int i, session_type, path_type, port_type;
+ u32 mode = 0;
+
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ session_type = SESSION_TYPE_RX;
+ path_type = ADM_PATH_PLAYBACK;
+ port_type = MSM_AFE_PORT_TYPE_RX;
+ } else {
+ session_type = SESSION_TYPE_TX;
+ path_type = ADM_PATH_LIVE_REC;
+ port_type = MSM_AFE_PORT_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ fe_dai_map[fedai_id][session_type].strm_id = dspst_id;
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (!is_be_dai_extproc(i) &&
+ (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+ (msm_bedais[i].active) &&
+ (test_bit(fedai_id, &msm_bedais[i].fe_sessions[0]))) {
+ mode = afe_get_port_type(msm_bedais[i].port_id);
+ adm_connect_afe_port(mode, dspst_id,
+ msm_bedais[i].port_id);
+ break;
+ }
+ }
+ mutex_unlock(&routing_lock);
+}
+
+static bool route_check_fe_id_adm_support(int fe_id)
+{
+ bool rc = true;
+
+ if ((fe_id >= MSM_FRONTEND_DAI_LSM1) &&
+ (fe_id <= MSM_FRONTEND_DAI_LSM8)) {
+ /* fe id is listen while port is set to afe */
+ if (lsm_port_index != ADM_LSM_PORT_INDEX) {
+ pr_debug("%s: fe_id %d, lsm mux slim port %d\n",
+ __func__, fe_id, lsm_port_index);
+ rc = false;
+ }
+ }
+
+ return rc;
+}
+
+int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode,
+ int dspst_id, int stream_type,
+ uint32_t passthr_mode)
+{
+ int i, j, session_type, path_type, port_type, topology;
+ int num_copps = 0;
+ struct route_payload payload;
+ u32 channels, sample_rate;
+ u16 bit_width = 16;
+ bool is_lsm;
+ u32 copp_token = 0;
+
+ pr_debug("%s:fe_id[%d] perf_mode[%d] id[%d] stream_type[%d] passt[%d]",
+ __func__, fe_id, perf_mode, dspst_id,
+ stream_type, passthr_mode);
+ if (!is_mm_lsm_fe_id(fe_id)) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if (!route_check_fe_id_adm_support(fe_id)) {
+ /* ignore adm open if not supported for fe_id */
+ pr_debug("%s: No ADM support for fe id %d\n", __func__, fe_id);
+ return 0;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ session_type = SESSION_TYPE_RX;
+ if (passthr_mode != LEGACY_PCM)
+ path_type = ADM_PATH_COMPRESSED_RX;
+ else
+ path_type = ADM_PATH_PLAYBACK;
+ port_type = MSM_AFE_PORT_TYPE_RX;
+ } else if (stream_type == SNDRV_PCM_STREAM_CAPTURE) {
+ session_type = SESSION_TYPE_TX;
+ if ((passthr_mode != LEGACY_PCM) && (passthr_mode != LISTEN))
+ path_type = ADM_PATH_COMPRESSED_TX;
+ else
+ path_type = ADM_PATH_LIVE_REC;
+ port_type = MSM_AFE_PORT_TYPE_TX;
+ } else {
+ pr_err("%s: invalid stream type %d\n", __func__, stream_type);
+ return -EINVAL;
+ }
+
+ is_lsm = (fe_id >= MSM_FRONTEND_DAI_LSM1) &&
+ (fe_id <= MSM_FRONTEND_DAI_LSM8);
+ mutex_lock(&routing_lock);
+
+ payload.num_copps = 0; /* only RX needs to use payload */
+ fe_dai_map[fe_id][session_type].strm_id = dspst_id;
+ /* re-enable EQ if active */
+ msm_qti_pp_send_eq_values(fe_id);
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (test_bit(fe_id, &msm_bedais[i].fe_sessions[0]))
+ msm_bedais[i].passthr_mode[fe_id] = passthr_mode;
+
+ if (!is_be_dai_extproc(i) &&
+ (afe_get_port_type(msm_bedais[i].port_id) ==
+ port_type) &&
+ (msm_bedais[i].active) &&
+ (test_bit(fe_id, &msm_bedais[i].fe_sessions[0]))) {
+ int app_type, app_type_idx, copp_idx, acdb_dev_id;
+
+ /*
+ * check if ADM needs to be configured with different
+ * channel mapping than backend
+ */
+ if (!msm_bedais[i].adm_override_ch)
+ channels = msm_bedais[i].channel;
+ else
+ channels = msm_bedais[i].adm_override_ch;
+
+ bit_width = msm_routing_get_bit_width(
+ msm_bedais[i].format);
+ app_type =
+ fe_dai_app_type_cfg[fe_id][session_type][i].app_type;
+ if (app_type && is_lsm) {
+ app_type_idx =
+ msm_pcm_routing_get_lsm_app_type_idx(app_type);
+ sample_rate =
+ fe_dai_app_type_cfg[fe_id][session_type][i]
+ .sample_rate;
+ bit_width =
+ lsm_app_type_cfg[app_type_idx].bit_width;
+ } else if (app_type) {
+ app_type_idx =
+ msm_pcm_routing_get_app_type_idx(
+ app_type);
+ sample_rate =
+ fe_dai_app_type_cfg[fe_id][session_type][i].sample_rate;
+ bit_width =
+ app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[fe_id][session_type][i].copp_token;
+ } else {
+ sample_rate = msm_bedais[i].sample_rate;
+ }
+ acdb_dev_id =
+ fe_dai_app_type_cfg[fe_id][session_type][i].acdb_dev_id;
+ topology = msm_routing_get_adm_topology(fe_id,
+ session_type,
+ i);
+ if ((passthr_mode == COMPRESSED_PASSTHROUGH_DSD)
+ || (passthr_mode ==
+ COMPRESSED_PASSTHROUGH_GEN))
+ topology = COMPRESSED_PASSTHROUGH_NONE_TOPOLOGY;
+ pr_debug("%s: Before adm open topology %d\n", __func__,
+ topology);
+
+ copp_idx =
+ adm_open(msm_bedais[i].port_id,
+ path_type, sample_rate, channels,
+ topology, perf_mode, bit_width,
+ app_type, acdb_dev_id, copp_token);
+ if ((copp_idx < 0) ||
+ (copp_idx >= MAX_COPPS_PER_PORT)) {
+ pr_err("%s:adm open failed coppid:%d\n",
+ __func__, copp_idx);
+ mutex_unlock(&routing_lock);
+ return -EINVAL;
+ }
+ pr_debug("%s: set idx bit of fe:%d, type: %d, be:%d\n",
+ __func__, fe_id, session_type, i);
+ set_bit(copp_idx,
+ &session_copp_map[fe_id][session_type][i]);
+
+ if (msm_is_resample_needed(
+ sample_rate,
+ msm_bedais[i].sample_rate))
+ adm_copp_mfc_cfg(
+ msm_bedais[i].port_id, copp_idx,
+ msm_bedais[i].sample_rate);
+
+ for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+ unsigned long copp =
+ session_copp_map[fe_id][session_type][i];
+ if (test_bit(j, &copp)) {
+ payload.port_id[num_copps] =
+ msm_bedais[i].port_id;
+ payload.copp_idx[num_copps] = j;
+ payload.app_type[num_copps] =
+ fe_dai_app_type_cfg
+ [fe_id][session_type][i]
+ .app_type;
+ payload.acdb_dev_id[num_copps] =
+ fe_dai_app_type_cfg
+ [fe_id][session_type][i]
+ .acdb_dev_id;
+ payload.sample_rate[num_copps] =
+ fe_dai_app_type_cfg
+ [fe_id][session_type][i]
+ .sample_rate;
+ if (msm_pcm_routing_test_pp_param(i,
+ ADM_PP_PARAM_LIMITER_BIT))
+ set_bit(ADM_STATUS_LIMITER,
+ &payload.route_status
+ [num_copps]);
+ num_copps++;
+ }
+ }
+ if (passthr_mode != COMPRESSED_PASSTHROUGH_DSD
+ && passthr_mode !=
+ COMPRESSED_PASSTHROUGH_GEN) {
+ msm_routing_send_device_pp_params(
+ msm_bedais[i].port_id,
+ copp_idx, fe_id);
+ }
+ }
+ }
+ if (num_copps) {
+ payload.num_copps = num_copps;
+ payload.session_id = fe_dai_map[fe_id][session_type].strm_id;
+ adm_matrix_map(path_type, payload, perf_mode, passthr_mode);
+ msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode);
+ }
+ mutex_unlock(&routing_lock);
+ return 0;
+}
+
+static u32 msm_pcm_routing_get_voc_sessionid(u16 val)
+{
+ u32 session_id;
+
+ switch (val) {
+ case MSM_FRONTEND_DAI_CS_VOICE:
+ session_id = voc_get_session_id(VOICE_SESSION_NAME);
+ break;
+ case MSM_FRONTEND_DAI_VOLTE:
+ session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+ break;
+ case MSM_FRONTEND_DAI_VOWLAN:
+ session_id = voc_get_session_id(VOWLAN_SESSION_NAME);
+ break;
+ case MSM_FRONTEND_DAI_VOICE2:
+ session_id = voc_get_session_id(VOICE2_SESSION_NAME);
+ break;
+ case MSM_FRONTEND_DAI_QCHAT:
+ session_id = voc_get_session_id(QCHAT_SESSION_NAME);
+ break;
+ case MSM_FRONTEND_DAI_VOIP:
+ session_id = voc_get_session_id(VOIP_SESSION_NAME);
+ break;
+ case MSM_FRONTEND_DAI_VOICEMMODE1:
+ session_id = voc_get_session_id(VOICEMMODE1_NAME);
+ break;
+ case MSM_FRONTEND_DAI_VOICEMMODE2:
+ session_id = voc_get_session_id(VOICEMMODE2_NAME);
+ break;
+ default:
+ session_id = 0;
+ }
+
+ pr_debug("%s session_id 0x%x", __func__, session_id);
+ return session_id;
+}
+
+static int msm_pcm_routing_channel_mixer_v2(int fe_id, bool perf_mode,
+ int dspst_id, int stream_type)
+{
+ int copp_idx = 0;
+ int sess_type = 0;
+ int j = 0, be_id = 0;
+ int ret = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return 0;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK)
+ sess_type = SESSION_TYPE_RX;
+ else
+ sess_type = SESSION_TYPE_TX;
+
+ if (!(channel_mixer_v2[fe_id][sess_type].enable)) {
+ pr_debug("%s: channel mixer not enabled for FE %d direction %d\n",
+ __func__, fe_id, sess_type);
+ return 0;
+ }
+
+ pr_debug("%s sess type %d fe_id %d override %d be active %d\n",
+ __func__,
+ sess_type,
+ fe_id,
+ channel_mixer_v2[fe_id][sess_type].override_cfg,
+ msm_bedais[be_id].active);
+ if (channel_mixer_v2[fe_id][sess_type].override_cfg) {
+ be_id = channel_mixer_v2[fe_id][sess_type].port_idx - 1;
+ channel_mixer_v2[fe_id][sess_type].input_channels[0] =
+ channel_mixer_v2[fe_id][sess_type].input_channel;
+
+ if ((msm_bedais[be_id].active) &&
+ test_bit(fe_id,
+ &msm_bedais[be_id].fe_sessions[0])) {
+ unsigned long copp =
+ session_copp_map[fe_id][sess_type][be_id];
+ for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+ if (test_bit(j, &copp)) {
+ copp_idx = j;
+ break;
+ }
+ }
+
+ ret = adm_programable_channel_mixer(
+ msm_bedais[be_id].port_id,
+ copp_idx, dspst_id, sess_type,
+ &channel_mixer_v2[fe_id][sess_type], 0);
+ }
+ }
+
+ return ret;
+}
+
+static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode,
+ int dspst_id, int stream_type)
+{
+ int copp_idx = 0;
+ int sess_type = 0;
+ int i = 0, j = 0, be_id = 0;
+ int ret = 0;
+
+ ret = msm_pcm_routing_channel_mixer_v2(fe_id, perf_mode,
+ dspst_id, stream_type);
+ if (ret) {
+ pr_err("%s channel mixer v2 cmd set failure%d\n", __func__,
+ fe_id);
+ return ret;
+ }
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return 0;
+ }
+
+ if (!(channel_mixer[fe_id].enable)) {
+ pr_debug("%s: channel mixer not enabled for FE %d\n",
+ __func__, fe_id);
+ return 0;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK)
+ sess_type = SESSION_TYPE_RX;
+ else
+ sess_type = SESSION_TYPE_TX;
+
+ for (i = 0; i < ADM_MAX_CHANNELS && channel_input[fe_id][i] > 0;
+ ++i) {
+ be_id = channel_input[fe_id][i] - 1;
+ channel_mixer[fe_id].input_channels[i] =
+ msm_bedais[be_id].channel;
+
+ if ((msm_bedais[be_id].active) &&
+ test_bit(fe_id,
+ &msm_bedais[be_id].fe_sessions[0])) {
+ unsigned long copp =
+ session_copp_map[fe_id][sess_type][be_id];
+ for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+ if (test_bit(j, &copp)) {
+ copp_idx = j;
+ break;
+ }
+ }
+
+ pr_debug("%s: fe %d, be %d, channel %d, copp %d\n",
+ __func__,
+ fe_id, be_id, msm_bedais[be_id].channel,
+ copp_idx);
+ ret = adm_programable_channel_mixer(
+ msm_bedais[be_id].port_id,
+ copp_idx, dspst_id, sess_type,
+ channel_mixer + fe_id, i);
+ }
+ }
+
+ return ret;
+}
+
+int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
+ int dspst_id, int stream_type)
+{
+ int i, j, session_type, path_type, port_type, topology, num_copps = 0;
+ struct route_payload payload;
+ u32 channels, sample_rate;
+ uint16_t bits_per_sample = 16;
+ uint32_t passthr_mode = LEGACY_PCM;
+ int ret = 0;
+ u32 copp_token = 0;
+
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID %d\n", __func__, fedai_id);
+ return -EINVAL;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ session_type = SESSION_TYPE_RX;
+ path_type = ADM_PATH_PLAYBACK;
+ port_type = MSM_AFE_PORT_TYPE_RX;
+ } else {
+ session_type = SESSION_TYPE_TX;
+ path_type = ADM_PATH_LIVE_REC;
+ port_type = MSM_AFE_PORT_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ payload.num_copps = 0; /* only RX needs to use payload */
+ fe_dai_map[fedai_id][session_type].strm_id = dspst_id;
+ fe_dai_map[fedai_id][session_type].perf_mode = perf_mode;
+
+ /* re-enable EQ if active */
+ msm_qti_pp_send_eq_values(fedai_id);
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+
+ if (test_bit(fedai_id, &msm_bedais[i].fe_sessions[0]))
+ msm_bedais[i].passthr_mode[fedai_id] = LEGACY_PCM;
+
+ if (!is_be_dai_extproc(i) &&
+ (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+ (msm_bedais[i].active) &&
+ (test_bit(fedai_id, &msm_bedais[i].fe_sessions[0]))) {
+ int app_type, app_type_idx, copp_idx, acdb_dev_id;
+ /*
+ * check if ADM needs to be configured with different
+ * channel mapping than backend
+ */
+ if (!msm_bedais[i].adm_override_ch)
+ channels = msm_bedais[i].channel;
+ else
+ channels = msm_bedais[i].adm_override_ch;
+ msm_bedais[i].passthr_mode[fedai_id] =
+ LEGACY_PCM;
+
+ bits_per_sample = msm_routing_get_bit_width(
+ msm_bedais[i].format);
+
+ app_type =
+ fe_dai_app_type_cfg[fedai_id][session_type][i].app_type;
+ if (app_type) {
+ app_type_idx =
+ msm_pcm_routing_get_app_type_idx(app_type);
+ sample_rate =
+ fe_dai_app_type_cfg[fedai_id][session_type][i]
+ .sample_rate;
+ bits_per_sample =
+ app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[fedai_id][session_type][i]
+ .copp_token;
+ } else
+ sample_rate = msm_bedais[i].sample_rate;
+
+ acdb_dev_id =
+ fe_dai_app_type_cfg[fedai_id][session_type][i]
+ .acdb_dev_id;
+ topology = msm_routing_get_adm_topology(fedai_id,
+ session_type,
+ i);
+ copp_idx = adm_open(msm_bedais[i].port_id, path_type,
+ sample_rate, channels, topology,
+ perf_mode, bits_per_sample,
+ app_type, acdb_dev_id, copp_token);
+ if ((copp_idx < 0) ||
+ (copp_idx >= MAX_COPPS_PER_PORT)) {
+ pr_err("%s: adm open failed copp_idx:%d\n",
+ __func__, copp_idx);
+ mutex_unlock(&routing_lock);
+ return -EINVAL;
+ }
+ pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n",
+ __func__, fedai_id, session_type, i);
+ set_bit(copp_idx,
+ &session_copp_map[fedai_id][session_type][i]);
+
+ if (msm_is_resample_needed(
+ sample_rate,
+ msm_bedais[i].sample_rate))
+ adm_copp_mfc_cfg(
+ msm_bedais[i].port_id, copp_idx,
+ msm_bedais[i].sample_rate);
+
+ for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+ unsigned long copp =
+ session_copp_map[fedai_id][session_type][i];
+ if (test_bit(j, &copp)) {
+ payload.port_id[num_copps] =
+ msm_bedais[i].port_id;
+ payload.copp_idx[num_copps] = j;
+ payload.app_type[num_copps] =
+ fe_dai_app_type_cfg
+ [fedai_id][session_type]
+ [i].app_type;
+ payload.acdb_dev_id[num_copps] =
+ fe_dai_app_type_cfg
+ [fedai_id][session_type]
+ [i].acdb_dev_id;
+ payload.sample_rate[num_copps] =
+ fe_dai_app_type_cfg
+ [fedai_id][session_type]
+ [i].sample_rate;
+ if (msm_pcm_routing_test_pp_param(i,
+ ADM_PP_PARAM_LIMITER_BIT))
+ set_bit(ADM_STATUS_LIMITER,
+ &payload.route_status
+ [num_copps]);
+ num_copps++;
+ }
+ }
+ if ((perf_mode == LEGACY_PCM_MODE) &&
+ (msm_bedais[i].passthr_mode[fedai_id] ==
+ LEGACY_PCM))
+ msm_pcm_routing_cfg_pp(msm_bedais[i].port_id,
+ copp_idx, topology,
+ channels);
+ }
+ }
+ if (num_copps) {
+ payload.num_copps = num_copps;
+ payload.session_id = fe_dai_map[fedai_id][session_type].strm_id;
+ adm_matrix_map(path_type, payload, perf_mode, passthr_mode);
+ msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode);
+ }
+
+ ret = msm_pcm_routing_channel_mixer(fedai_id, perf_mode,
+ dspst_id, stream_type);
+ mutex_unlock(&routing_lock);
+ return ret;
+}
+
+int msm_pcm_routing_reg_phy_stream_v2(int fedai_id, int perf_mode,
+ int dspst_id, int stream_type,
+ struct msm_pcm_routing_evt event_info)
+{
+ if (msm_pcm_routing_reg_phy_stream(fedai_id, perf_mode, dspst_id,
+ stream_type)) {
+ pr_err("%s: failed to reg phy stream\n", __func__);
+ return -EINVAL;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK)
+ fe_dai_map[fedai_id][SESSION_TYPE_RX].event_info = event_info;
+ else
+ fe_dai_map[fedai_id][SESSION_TYPE_TX].event_info = event_info;
+ return 0;
+}
+
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type)
+{
+ int i, port_type, session_type, path_type, topology;
+ struct msm_pcm_routing_fdai_data *fdai;
+
+ if (!is_mm_lsm_fe_id(fedai_id)) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ port_type = MSM_AFE_PORT_TYPE_RX;
+ session_type = SESSION_TYPE_RX;
+ path_type = ADM_PATH_PLAYBACK;
+ } else {
+ port_type = MSM_AFE_PORT_TYPE_TX;
+ session_type = SESSION_TYPE_TX;
+ path_type = ADM_PATH_LIVE_REC;
+ }
+
+ mutex_lock(&routing_lock);
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (!is_be_dai_extproc(i) &&
+ (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+ (msm_bedais[i].active) &&
+ (test_bit(fedai_id, &msm_bedais[i].fe_sessions[0]))) {
+ int idx;
+ unsigned long copp =
+ session_copp_map[fedai_id][session_type][i];
+ fdai = &fe_dai_map[fedai_id][session_type];
+
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+ if (test_bit(idx, &copp))
+ break;
+
+ if (idx >= MAX_COPPS_PER_PORT || idx < 0) {
+ pr_debug("%s: copp idx is invalid, exiting\n",
+ __func__);
+ continue;
+ }
+ topology = adm_get_topology_for_port_copp_idx(
+ msm_bedais[i].port_id, idx);
+ adm_close(msm_bedais[i].port_id, fdai->perf_mode, idx);
+ pr_debug("%s:copp:%ld,idx bit fe:%d,type:%d,be:%d\n",
+ __func__, copp, fedai_id, session_type, i);
+ clear_bit(idx,
+ &session_copp_map[fedai_id][session_type][i]);
+ if ((DOLBY_ADM_COPP_TOPOLOGY_ID == topology ||
+ DS2_ADM_COPP_TOPOLOGY_ID == topology) &&
+ (fdai->perf_mode == LEGACY_PCM_MODE) &&
+ (msm_bedais[i].passthr_mode[fedai_id] ==
+ LEGACY_PCM))
+ msm_pcm_routing_deinit_pp(msm_bedais[i].port_id,
+ topology);
+ }
+ }
+
+ fe_dai_map[fedai_id][session_type].strm_id = INVALID_SESSION;
+ fe_dai_map[fedai_id][session_type].be_srate = 0;
+ mutex_unlock(&routing_lock);
+}
+
+/* Check if FE/BE route is set */
+static bool msm_pcm_routing_route_is_set(u16 be_id, u16 fe_id)
+{
+ bool rc = false;
+
+ if (!is_mm_lsm_fe_id(fe_id)) {
+ /* recheck FE ID in the mixer control defined in this file */
+ pr_err("%s: bad MM ID\n", __func__);
+ return rc;
+ }
+
+ if (test_bit(fe_id, &msm_bedais[be_id].fe_sessions[0]))
+ rc = true;
+
+ return rc;
+}
+
+static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
+{
+ int session_type, path_type, topology;
+ u32 channels, sample_rate;
+ uint16_t bits_per_sample = 16;
+ struct msm_pcm_routing_fdai_data *fdai;
+ uint32_t passthr_mode;
+ bool is_lsm;
+ u32 copp_token = 0;
+
+ pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
+
+ if (!is_mm_lsm_fe_id(val)) {
+ /* recheck FE ID in the mixer control defined in this file */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
+ if (!route_check_fe_id_adm_support(val)) {
+ /* ignore adm open if not supported for fe_id */
+ pr_debug("%s: No ADM support for fe id %d\n", __func__, val);
+ return;
+ }
+
+ passthr_mode = msm_bedais[reg].passthr_mode[val];
+ if (afe_get_port_type(msm_bedais[reg].port_id) ==
+ MSM_AFE_PORT_TYPE_RX) {
+ session_type = SESSION_TYPE_RX;
+ if (passthr_mode != LEGACY_PCM)
+ path_type = ADM_PATH_COMPRESSED_RX;
+ else
+ path_type = ADM_PATH_PLAYBACK;
+ } else {
+ session_type = SESSION_TYPE_TX;
+ if (passthr_mode != LEGACY_PCM)
+ path_type = ADM_PATH_COMPRESSED_TX;
+ else
+ path_type = ADM_PATH_LIVE_REC;
+ }
+ is_lsm = (val >= MSM_FRONTEND_DAI_LSM1) &&
+ (val <= MSM_FRONTEND_DAI_LSM8);
+
+ mutex_lock(&routing_lock);
+ if (set) {
+ if (!test_bit(val, &msm_bedais[reg].fe_sessions[0]) &&
+ ((msm_bedais[reg].port_id == VOICE_PLAYBACK_TX) ||
+ (msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX)))
+ voc_start_playback(set, msm_bedais[reg].port_id);
+
+ set_bit(val, &msm_bedais[reg].fe_sessions[0]);
+ fdai = &fe_dai_map[val][session_type];
+ if (msm_bedais[reg].active && fdai->strm_id !=
+ INVALID_SESSION) {
+ int app_type, app_type_idx, copp_idx, acdb_dev_id;
+ /*
+ * check if ADM needs to be configured with different
+ * channel mapping than backend
+ */
+ if (!msm_bedais[reg].adm_override_ch)
+ channels = msm_bedais[reg].channel;
+ else
+ channels = msm_bedais[reg].adm_override_ch;
+ if (session_type == SESSION_TYPE_TX &&
+ fdai->be_srate &&
+ (fdai->be_srate != msm_bedais[reg].sample_rate)) {
+ pr_debug("%s: flush strm %d diff BE rates\n",
+ __func__, fdai->strm_id);
+
+ if (fdai->event_info.event_func)
+ fdai->event_info.event_func(
+ MSM_PCM_RT_EVT_BUF_RECFG,
+ fdai->event_info.priv_data);
+ fdai->be_srate = 0; /* might not need it */
+ }
+
+ bits_per_sample = msm_routing_get_bit_width(
+ msm_bedais[reg].format);
+
+ app_type =
+ fe_dai_app_type_cfg[val][session_type][reg].app_type;
+ if (app_type && is_lsm) {
+ app_type_idx =
+ msm_pcm_routing_get_lsm_app_type_idx(app_type);
+ sample_rate =
+ fe_dai_app_type_cfg[val][session_type][reg]
+ .sample_rate;
+ bits_per_sample =
+ lsm_app_type_cfg[app_type_idx].bit_width;
+ } else if (app_type) {
+ app_type_idx =
+ msm_pcm_routing_get_app_type_idx(app_type);
+ sample_rate =
+ fe_dai_app_type_cfg[val][session_type][reg]
+ .sample_rate;
+ bits_per_sample =
+ app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[val][session_type][reg]
+ .copp_token;
+ } else
+ sample_rate = msm_bedais[reg].sample_rate;
+
+ topology = msm_routing_get_adm_topology(val,
+ session_type,
+ reg);
+ acdb_dev_id =
+ fe_dai_app_type_cfg[val][session_type][reg].acdb_dev_id;
+ copp_idx = adm_open(msm_bedais[reg].port_id, path_type,
+ sample_rate, channels, topology,
+ fdai->perf_mode, bits_per_sample,
+ app_type, acdb_dev_id, copp_token);
+ if ((copp_idx < 0) ||
+ (copp_idx >= MAX_COPPS_PER_PORT)) {
+ pr_err("%s: adm open failed\n", __func__);
+ mutex_unlock(&routing_lock);
+ return;
+ }
+ pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n",
+ __func__, val, session_type, reg);
+ set_bit(copp_idx,
+ &session_copp_map[val][session_type][reg]);
+
+ if (msm_is_resample_needed(
+ sample_rate,
+ msm_bedais[reg].sample_rate))
+ adm_copp_mfc_cfg(
+ msm_bedais[reg].port_id, copp_idx,
+ msm_bedais[reg].sample_rate);
+
+ if (session_type == SESSION_TYPE_RX &&
+ fdai->event_info.event_func)
+ fdai->event_info.event_func(
+ MSM_PCM_RT_EVT_DEVSWITCH,
+ fdai->event_info.priv_data);
+
+ msm_pcm_routing_build_matrix(val, session_type,
+ path_type,
+ fdai->perf_mode,
+ passthr_mode);
+ if ((fdai->perf_mode == LEGACY_PCM_MODE) &&
+ (passthr_mode == LEGACY_PCM))
+ msm_pcm_routing_cfg_pp(msm_bedais[reg].port_id,
+ copp_idx, topology,
+ channels);
+ }
+ } else {
+ if (test_bit(val, &msm_bedais[reg].fe_sessions[0]) &&
+ ((msm_bedais[reg].port_id == VOICE_PLAYBACK_TX) ||
+ (msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX)))
+ voc_start_playback(set, msm_bedais[reg].port_id);
+ clear_bit(val, &msm_bedais[reg].fe_sessions[0]);
+ fdai = &fe_dai_map[val][session_type];
+ if (msm_bedais[reg].active && fdai->strm_id !=
+ INVALID_SESSION) {
+ int idx;
+ int port_id;
+ unsigned long copp =
+ session_copp_map[val][session_type][reg];
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+ if (test_bit(idx, &copp))
+ break;
+
+ port_id = msm_bedais[reg].port_id;
+ topology = adm_get_topology_for_port_copp_idx(port_id,
+ idx);
+ adm_close(msm_bedais[reg].port_id, fdai->perf_mode,
+ idx);
+ pr_debug("%s: copp: %ld, reset idx bit fe:%d, type: %d, be:%d topology=0x%x\n",
+ __func__, copp, val, session_type, reg,
+ topology);
+ clear_bit(idx,
+ &session_copp_map[val][session_type][reg]);
+ if ((DOLBY_ADM_COPP_TOPOLOGY_ID == topology ||
+ DS2_ADM_COPP_TOPOLOGY_ID == topology) &&
+ (fdai->perf_mode == LEGACY_PCM_MODE) &&
+ (passthr_mode == LEGACY_PCM))
+ msm_pcm_routing_deinit_pp(
+ msm_bedais[reg].port_id,
+ topology);
+ msm_pcm_routing_build_matrix(val, session_type,
+ path_type,
+ fdai->perf_mode,
+ passthr_mode);
+ }
+ }
+ if ((msm_bedais[reg].port_id == VOICE_RECORD_RX)
+ || (msm_bedais[reg].port_id == VOICE_RECORD_TX))
+ voc_start_record(msm_bedais[reg].port_id, set, voc_session_id);
+
+ mutex_unlock(&routing_lock);
+}
+
+static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift,
+ mc->rshift, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+
+ if (ucontrol->value.integer.value[0] &&
+ msm_pcm_routing_route_is_set(mc->shift, mc->rshift) == false) {
+ msm_pcm_routing_process_audio(mc->shift, mc->rshift, 1);
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, update);
+ } else if (!ucontrol->value.integer.value[0] &&
+ msm_pcm_routing_route_is_set(mc->shift, mc->rshift) == true) {
+ msm_pcm_routing_process_audio(mc->shift, mc->rshift, 0);
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, update);
+ }
+
+ return 1;
+}
+
+static int msm_routing_get_listen_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift,
+ mc->rshift, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_listen_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+
+ pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift,
+ mc->rshift, ucontrol->value.integer.value[0]);
+
+ if (ucontrol->value.integer.value[0]) {
+ if (msm_pcm_routing_route_is_set(mc->shift, mc->rshift) ==
+ false)
+ msm_pcm_routing_process_audio(mc->shift, mc->rshift, 1);
+ snd_soc_dapm_mixer_update_power(widget->dapm,
+ kcontrol, 1, update);
+ } else if (!ucontrol->value.integer.value[0]) {
+ if (msm_pcm_routing_route_is_set(mc->shift, mc->rshift) == true)
+ msm_pcm_routing_process_audio(mc->shift, mc->rshift, 0);
+ snd_soc_dapm_mixer_update_power(widget->dapm,
+ kcontrol, 0, update);
+ }
+
+ return 1;
+}
+
+static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set)
+{
+ u32 session_id = 0;
+ u16 path_type;
+ struct media_format_info voc_be_media_format;
+
+ pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
+
+ session_id = msm_pcm_routing_get_voc_sessionid(val);
+
+ pr_debug("%s: FE DAI 0x%x session_id 0x%x\n",
+ __func__, val, session_id);
+
+ mutex_lock(&routing_lock);
+
+ if (set)
+ set_bit(val, &msm_bedais[reg].fe_sessions[0]);
+ else
+ clear_bit(val, &msm_bedais[reg].fe_sessions[0]);
+
+ if (val == MSM_FRONTEND_DAI_DTMF_RX &&
+ afe_get_port_type(msm_bedais[reg].port_id) ==
+ MSM_AFE_PORT_TYPE_RX) {
+ pr_debug("%s(): set=%d port id=0x%x for dtmf generation\n",
+ __func__, set, msm_bedais[reg].port_id);
+ afe_set_dtmf_gen_rx_portid(msm_bedais[reg].port_id, set);
+ }
+
+ if (afe_get_port_type(msm_bedais[reg].port_id) ==
+ MSM_AFE_PORT_TYPE_RX)
+ path_type = RX_PATH;
+ else
+ path_type = TX_PATH;
+
+ if (set) {
+ if (msm_bedais[reg].active) {
+ voc_set_route_flag(session_id, path_type, 1);
+
+ memset(&voc_be_media_format, 0,
+ sizeof(struct media_format_info));
+
+ voc_be_media_format.port_id = msm_bedais[reg].port_id;
+ voc_be_media_format.num_channels =
+ msm_bedais[reg].channel;
+ voc_be_media_format.sample_rate =
+ msm_bedais[reg].sample_rate;
+ voc_be_media_format.bits_per_sample =
+ msm_bedais[reg].format;
+ /* Defaulting this to 1 for voice call usecases */
+ voc_be_media_format.channel_mapping[0] = 1;
+
+ voc_set_device_config(session_id, path_type,
+ &voc_be_media_format);
+
+ if (voc_get_route_flag(session_id, TX_PATH) &&
+ voc_get_route_flag(session_id, RX_PATH))
+ voc_enable_device(session_id);
+ } else {
+ pr_debug("%s BE is not active\n", __func__);
+ }
+ } else {
+ voc_set_route_flag(session_id, path_type, 0);
+ voc_disable_device(session_id);
+ }
+
+ mutex_unlock(&routing_lock);
+
+}
+
+static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ mutex_lock(&routing_lock);
+
+ if (test_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ mutex_unlock(&routing_lock);
+
+ pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift,
+ mc->rshift, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+
+ if (ucontrol->value.integer.value[0]) {
+ msm_pcm_routing_process_voice(mc->shift, mc->rshift, 1);
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, update);
+ } else {
+ msm_pcm_routing_process_voice(mc->shift, mc->rshift, 0);
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, update);
+ }
+
+ return 1;
+}
+
+static int msm_routing_get_voice_stub_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ mutex_lock(&routing_lock);
+
+ if (test_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ mutex_unlock(&routing_lock);
+
+ pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift,
+ mc->rshift, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_voice_stub_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+
+ if (ucontrol->value.integer.value[0]) {
+ mutex_lock(&routing_lock);
+ set_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0]);
+ mutex_unlock(&routing_lock);
+
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, update);
+ } else {
+ mutex_lock(&routing_lock);
+ clear_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0]);
+ mutex_unlock(&routing_lock);
+
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, update);
+ }
+
+ pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift,
+ mc->rshift, ucontrol->value.integer.value[0]);
+
+ return 1;
+}
+
+/*
+ * Return the mapping between port ID and backend ID to enable the AFE callback
+ * to determine the acdb_dev_id from the port id
+ */
+int msm_pcm_get_be_id_from_port_id(int port_id)
+{
+ int i;
+ int be_id = -EINVAL;
+
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (msm_bedais[i].port_id == port_id) {
+ be_id = i;
+ break;
+ }
+ }
+
+ return be_id;
+}
+
+/*
+ * Return the registered dev_acdb_id given a port ID to enable identifying the
+ * correct AFE calibration information by comparing the header information.
+ */
+static int msm_pcm_get_dev_acdb_id_by_port_id(int port_id)
+{
+ int acdb_id = -EINVAL;
+ int i = 0;
+ int session;
+ int port_type = afe_get_port_type(port_id);
+ int be_id = msm_pcm_get_be_id_from_port_id(port_id);
+
+ pr_debug("%s:port_id %d be_id %d, port_type 0x%x\n",
+ __func__, port_id, be_id, port_type);
+
+ if (port_type == MSM_AFE_PORT_TYPE_TX) {
+ session = SESSION_TYPE_TX;
+ } else if (port_type == MSM_AFE_PORT_TYPE_RX) {
+ session = SESSION_TYPE_RX;
+ } else {
+ pr_err("%s: Invalid port type %d\n", __func__, port_type);
+ acdb_id = -EINVAL;
+ goto exit;
+ }
+
+ if (be_id < 0) {
+ pr_err("%s: Error getting backend id %d\n", __func__, be_id);
+ goto exit;
+ }
+
+ mutex_lock(&routing_lock);
+ i = find_first_bit(&msm_bedais[be_id].fe_sessions[0],
+ MSM_FRONTEND_DAI_MAX);
+ if (i < MSM_FRONTEND_DAI_MAX)
+ acdb_id = fe_dai_app_type_cfg[i][session][be_id].acdb_dev_id;
+
+ pr_debug("%s: FE[%d] session[%d] BE[%d] acdb_id(%d)\n",
+ __func__, i, session, be_id, acdb_id);
+ mutex_unlock(&routing_lock);
+exit:
+ return acdb_id;
+}
+
+static int msm_routing_get_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = fm_switch_enable;
+ pr_debug("%s: FM Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_dapm_update *update = NULL;
+
+ pr_debug("%s: FM Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, update);
+ else
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, update);
+ fm_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_get_hfp_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hfp_switch_enable;
+ pr_debug("%s: HFP Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_hfp_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_dapm_update *update = NULL;
+
+ pr_debug("%s: HFP Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol,
+ 1, update);
+ else
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol,
+ 0, update);
+ hfp_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_get_int0_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = int0_mi2s_switch_enable;
+ pr_debug("%s: INT0 MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_int0_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_dapm_update *update = NULL;
+
+ pr_debug("%s: INT0 MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+ update);
+ else
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+ update);
+ int0_mi2s_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_get_int4_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = int4_mi2s_switch_enable;
+ pr_debug("%s: INT4 MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_int4_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_dapm_update *update = NULL;
+
+ pr_debug("%s: INT4 MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+ update);
+ else
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+ update);
+ int4_mi2s_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_get_usb_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = usb_switch_enable;
+ pr_debug("%s: HFP Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_usb_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_dapm_update *update = NULL;
+
+ pr_debug("%s: USB Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol,
+ 1, update);
+ else
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol,
+ 0, update);
+ usb_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_get_pri_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = pri_mi2s_switch_enable;
+ pr_debug("%s: PRI MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_pri_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_dapm_update *update = NULL;
+
+ pr_debug("%s: PRI MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+ update);
+ else
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+ update);
+ pri_mi2s_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_get_sec_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = sec_mi2s_switch_enable;
+ pr_debug("%s: SEC MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_sec_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_dapm_update *update = NULL;
+
+ pr_debug("%s: SEC MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+ update);
+ else
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+ update);
+ sec_mi2s_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_get_tert_mi2s_switch_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = tert_mi2s_switch_enable;
+ pr_debug("%s: TERT MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_tert_mi2s_switch_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_dapm_update *update = NULL;
+
+ pr_debug("%s: TERT MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+ update);
+ else
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+ update);
+ tert_mi2s_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_get_quat_mi2s_switch_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = quat_mi2s_switch_enable;
+ pr_debug("%s: QUAT MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_quat_mi2s_switch_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_dapm_update *update = NULL;
+
+ pr_debug("%s: QUAT MI2S Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+ update);
+ else
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+ update);
+ quat_mi2s_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_get_fm_pcmrx_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = fm_pcmrx_switch_enable;
+ pr_debug("%s: FM Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_fm_pcmrx_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_dapm_update *update = NULL;
+
+ pr_debug("%s: FM Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, update);
+ else
+ snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, update);
+ fm_pcmrx_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_lsm_port_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = lsm_port_index;
+ return 0;
+}
+
+static int msm_routing_lsm_port_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int mux = ucontrol->value.enumerated.item[0];
+ int lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX;
+
+ if (mux >= e->items) {
+ pr_err("%s: Invalid mux value %d\n", __func__, mux);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: LSM enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX;
+ break;
+ case 2:
+ lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX;
+ break;
+ case 3:
+ lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX;
+ break;
+ case 4:
+ lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX;
+ break;
+ case 5:
+ lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX;
+ break;
+ case 6:
+ lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX;
+ break;
+ case 7:
+ lsm_port = AFE_PORT_ID_TERTIARY_MI2S_TX;
+ break;
+ case 8:
+ lsm_port = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+ break;
+ case 9:
+ lsm_port = ADM_LSM_PORT_ID;
+ break;
+ case 10:
+ lsm_port = AFE_PORT_ID_INT3_MI2S_TX;
+ break;
+ case 11:
+ lsm_port = AFE_PORT_ID_QUATERNARY_TDM_TX;
+ break;
+ default:
+ pr_err("Default lsm port");
+ break;
+ }
+ set_lsm_port(lsm_port);
+ lsm_port_index = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int msm_routing_lsm_func_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ u16 port_id;
+ enum afe_mad_type mad_type;
+
+ pr_debug("%s: id.name=%s\n", __func__, kcontrol->id.name);
+ for (i = 0; i < ARRAY_SIZE(lsm_port_text); i++)
+ if (!strnstr(kcontrol->id.name, lsm_port_text[i],
+ strlen(lsm_port_text[i])))
+ break;
+
+ if (i-- == ARRAY_SIZE(lsm_port_text)) {
+ WARN(1, "Invalid id name %s\n", kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ port_id = i * 2 + 1 + SLIMBUS_0_RX;
+
+ /*Check for Tertiary/Quaternary/INT3 TX port*/
+ if (strnstr(kcontrol->id.name, lsm_port_text[7],
+ strlen(lsm_port_text[7])))
+ port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+
+ if (strnstr(kcontrol->id.name, lsm_port_text[8],
+ strlen(lsm_port_text[8])))
+ port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+
+ if (strnstr(kcontrol->id.name, lsm_port_text[10],
+ strlen(lsm_port_text[10])))
+ port_id = AFE_PORT_ID_INT3_MI2S_TX;
+
+ if (strnstr(kcontrol->id.name, lsm_port_text[11],
+ strlen(lsm_port_text[11])))
+ port_id = AFE_PORT_ID_QUATERNARY_TDM_TX;
+
+ mad_type = afe_port_get_mad_type(port_id);
+ pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+ mad_type);
+ switch (mad_type) {
+ case MAD_HW_NONE:
+ ucontrol->value.integer.value[0] = MADNONE;
+ break;
+ case MAD_HW_AUDIO:
+ ucontrol->value.integer.value[0] = MADAUDIO;
+ break;
+ case MAD_HW_BEACON:
+ ucontrol->value.integer.value[0] = MADBEACON;
+ break;
+ case MAD_HW_ULTRASOUND:
+ ucontrol->value.integer.value[0] = MADULTRASOUND;
+ break;
+ case MAD_SW_AUDIO:
+ ucontrol->value.integer.value[0] = MADSWAUDIO;
+ break;
+ default:
+ WARN(1, "Unknown\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int msm_routing_lsm_func_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ u16 port_id;
+ enum afe_mad_type mad_type;
+
+ pr_debug("%s: id.name=%s\n", __func__, kcontrol->id.name);
+ for (i = 0; i < ARRAY_SIZE(lsm_port_text); i++)
+ if (strnstr(kcontrol->id.name, lsm_port_text[i],
+ strlen(lsm_port_text[i])))
+ break;
+
+ if (i-- == ARRAY_SIZE(lsm_port_text)) {
+ WARN(1, "Invalid id name %s\n", kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ port_id = i * 2 + 1 + SLIMBUS_0_RX;
+ switch (ucontrol->value.integer.value[0]) {
+ case MADNONE:
+ mad_type = MAD_HW_NONE;
+ break;
+ case MADAUDIO:
+ mad_type = MAD_HW_AUDIO;
+ break;
+ case MADBEACON:
+ mad_type = MAD_HW_BEACON;
+ break;
+ case MADULTRASOUND:
+ mad_type = MAD_HW_ULTRASOUND;
+ break;
+ case MADSWAUDIO:
+ mad_type = MAD_SW_AUDIO;
+ break;
+ default:
+ WARN(1, "Unknown\n");
+ return -EINVAL;
+ }
+
+ /*Check for Tertiary/Quaternary/INT3 TX port*/
+ if (strnstr(kcontrol->id.name, lsm_port_text[7],
+ strlen(lsm_port_text[7])))
+ port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+
+ if (strnstr(kcontrol->id.name, lsm_port_text[8],
+ strlen(lsm_port_text[8])))
+ port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+
+ if (strnstr(kcontrol->id.name, lsm_port_text[10],
+ strlen(lsm_port_text[10])))
+ port_id = AFE_PORT_ID_INT3_MI2S_TX;
+
+ if (strnstr(kcontrol->id.name, lsm_port_text[11],
+ strlen(lsm_port_text[11])))
+ port_id = AFE_PORT_ID_QUATERNARY_TDM_TX;
+
+ pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+ mad_type);
+ return afe_port_set_mad_type(port_id, mad_type);
+}
+
+static const char *const adm_override_chs_text[] = {"Zero", "One", "Two"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(slim_7_rx_adm_override_chs,
+ adm_override_chs_text);
+
+static int msm_routing_adm_get_backend_idx(struct snd_kcontrol *kcontrol)
+{
+ int backend_id;
+
+ if (strnstr(kcontrol->id.name, "SLIM7_RX", sizeof("SLIM7_RX"))) {
+ backend_id = MSM_BACKEND_DAI_SLIMBUS_7_RX;
+ } else {
+ pr_err("%s: unsupported backend id: %s",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ return backend_id;
+}
+static int msm_routing_adm_channel_config_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int backend_id = msm_routing_adm_get_backend_idx(kcontrol);
+
+ if (backend_id >= 0) {
+ mutex_lock(&routing_lock);
+ ucontrol->value.integer.value[0] =
+ msm_bedais[backend_id].adm_override_ch;
+ pr_debug("%s: adm channel count %ld for BE:%d\n", __func__,
+ ucontrol->value.integer.value[0], backend_id);
+ mutex_unlock(&routing_lock);
+ }
+
+ return 0;
+}
+
+static int msm_routing_adm_channel_config_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int backend_id = msm_routing_adm_get_backend_idx(kcontrol);
+
+ if (backend_id >= 0) {
+ mutex_lock(&routing_lock);
+ msm_bedais[backend_id].adm_override_ch =
+ ucontrol->value.integer.value[0];
+ pr_debug("%s:updating BE :%d adm channels: %d\n",
+ __func__, backend_id,
+ msm_bedais[backend_id].adm_override_ch);
+ mutex_unlock(&routing_lock);
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new adm_channel_config_controls[] = {
+ SOC_ENUM_EXT("SLIM7_RX ADM Channels", slim_7_rx_adm_override_chs,
+ msm_routing_adm_channel_config_get,
+ msm_routing_adm_channel_config_put),
+};
+
+static int msm_routing_slim_0_rx_aanc_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ mutex_lock(&routing_lock);
+ ucontrol->value.integer.value[0] = slim0_rx_aanc_fb_port;
+ mutex_unlock(&routing_lock);
+ pr_debug("%s: AANC Mux Port %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+};
+
+static int msm_routing_slim_0_rx_aanc_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct aanc_data aanc_info;
+
+ mutex_lock(&routing_lock);
+ memset(&aanc_info, 0x00, sizeof(aanc_info));
+ pr_debug("%s: AANC Mux Port %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ slim0_rx_aanc_fb_port = ucontrol->value.integer.value[0];
+ if (ucontrol->value.integer.value[0] == 0) {
+ aanc_info.aanc_active = false;
+ aanc_info.aanc_tx_port = 0;
+ aanc_info.aanc_rx_port = 0;
+ } else {
+ aanc_info.aanc_active = true;
+ aanc_info.aanc_rx_port = SLIMBUS_0_RX;
+ aanc_info.aanc_tx_port =
+ (SLIMBUS_0_RX - 1 + (slim0_rx_aanc_fb_port * 2));
+ }
+ afe_set_aanc_info(&aanc_info);
+ mutex_unlock(&routing_lock);
+ return 0;
+};
+static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = 0, shift = 0;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ idx = mc->rshift/(sizeof(msm_bedais[mc->shift].port_sessions[0]) * 8);
+ shift = mc->rshift%(sizeof(msm_bedais[mc->shift].port_sessions[0]) * 8);
+
+ if (idx >= BE_DAI_PORT_SESSIONS_IDX_MAX) {
+ pr_err("%s: Invalid idx = %d\n", __func__, idx);
+ return -EINVAL;
+ }
+
+ if (test_bit(shift,
+ (unsigned long *)&msm_bedais[mc->shift].port_sessions[idx]))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift,
+ mc->rshift, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = 0, shift = 0;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ idx = mc->rshift/(sizeof(msm_bedais[mc->shift].port_sessions[0]) * 8);
+ shift = mc->rshift%(sizeof(msm_bedais[mc->shift].port_sessions[0]) * 8);
+
+ if (idx >= BE_DAI_PORT_SESSIONS_IDX_MAX) {
+ pr_err("%s: Invalid idx = %d\n", __func__, idx);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: reg 0x%x shift 0x%x val %ld idx %d reminder shift %d\n",
+ __func__, mc->shift, mc->rshift,
+ ucontrol->value.integer.value[0], idx, shift);
+
+ if (ucontrol->value.integer.value[0]) {
+ afe_loopback(1, msm_bedais[mc->shift].port_id,
+ msm_bedais[mc->rshift].port_id);
+ set_bit(shift,
+ (unsigned long *)&msm_bedais[mc->shift].port_sessions[idx]);
+ } else {
+ afe_loopback(0, msm_bedais[mc->shift].port_id,
+ msm_bedais[mc->rshift].port_id);
+ clear_bit(shift,
+ (unsigned long *)&msm_bedais[mc->shift].port_sessions[idx]);
+ }
+
+ return 1;
+}
+
+static int msm_pcm_get_channel_rule_index(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_mixer_control *)
+ kcontrol->private_value)->shift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = channel_mixer[fe_id].rule;
+
+ return 0;
+}
+
+static int msm_pcm_put_channel_rule_index(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_mixer_control *)
+ kcontrol->private_value)->shift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ channel_mixer[fe_id].rule = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int msm_pcm_get_out_chs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] =
+ channel_mixer[fe_id].output_channel;
+ return 0;
+}
+
+static int msm_pcm_put_out_chs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: fe_id is %d, output channels = %d\n", __func__,
+ fe_id,
+ (unsigned int)(ucontrol->value.integer.value[0]));
+ channel_mixer[fe_id].output_channel =
+ (unsigned int)(ucontrol->value.integer.value[0]);
+
+ return 1;
+}
+
+static const char *const ch_mixer[] = {"Disable", "Enable"};
+
+/* If new backend is added, need update this array */
+static const char *const be_name[] = {
+"ZERO", "PRI_I2S_RX", "PRI_I2S_TX", "SLIM_0_RX",
+"SLIM_0_TX", "HDMI_RX", "INT_BT_SCO_RX", "INT_BT_SCO_TX",
+"INT_FM_RX", "INT_FM_TX", "AFE_PCM_RX", "AFE_PCM_TX",
+"AUXPCM_RX", "AUXPCM_TX", "VOICE_PLAYBACK_TX", "VOICE2_PLAYBACK_TX",
+"INCALL_RECORD_RX", "INCALL_RECORD_TX", "MI2S_RX", "MI2S_TX",
+"SEC_I2S_RX", "SLIM_1_RX", "SLIM_1_TX", "SLIM_2_RX",
+"SLIM_2_TX", "SLIM_3_RX", "SLIM_3_TX", "SLIM_4_RX",
+"SLIM_4_TX", "SLIM_5_RX", "SLIM_5_TX", "SLIM_6_RX",
+"SLIM_6_TX", "SLIM_7_RX", "SLIM_7_TX", "SLIM_8_RX",
+"SLIM_8_TX", "EXTPROC_RX", "EXTPROC_TX", "EXPROC_EC_TX",
+"QUAT_MI2S_RX", "QUAT_MI2S_TX", "SECOND_MI2S_RX", "SECOND_MI2S_TX",
+"PRI_MI2S_RX", "PRI_MI2S_TX", "TERT_MI2S_RX", "TERT_MI2S_TX",
+"AUDIO_I2S_RX", "SEC_AUXPCM_RX", "SEC_AUXPCM_TX", "SPDIF_RX",
+"SECOND_MI2S_RX_SD1", "QUIN_MI2S_RX", "QUIN_MI2S_TX", "SENARY_MI2S_TX",
+"PRI_TDM_RX_0", "PRI_TDM_TX_0", "PRI_TDM_RX_1", "PRI_TDM_TX_1",
+"PRI_TDM_RX_2", "PRI_TDM_TX_2", "PRI_TDM_RX_3", "PRI_TDM_TX_3",
+"PRI_TDM_RX_4", "PRI_TDM_TX_4", "PRI_TDM_RX_5", "PRI_TDM_TX_5",
+"PRI_TDM_RX_6", "PRI_TDM_TX_6", "PRI_TDM_RX_7", "PRI_TDM_TX_7",
+"SEC_TDM_RX_0", "SEC_TDM_TX_0", "SEC_TDM_RX_1", "SEC_TDM_TX_1",
+"SEC_TDM_RX_2", "SEC_TDM_TX_2", "SEC_TDM_RX_3", "SEC_TDM_TX_3",
+"SEC_TDM_RX_4", "SEC_TDM_TX_4", "SEC_TDM_RX_5", "SEC_TDM_TX_5",
+"SEC_TDM_RX_6", "SEC_TDM_TX_6", "SEC_TDM_RX_7", "SEC_TDM_TX_7",
+"TERT_TDM_RX_0", "TERT_TDM_TX_0", "TERT_TDM_RX_1", "TERT_TDM_TX_1",
+"TERT_TDM_RX_2", "TERT_TDM_TX_2", "TERT_TDM_RX_3", "TERT_TDM_TX_3",
+"TERT_TDM_RX_4", "TERT_TDM_TX_4", "TERT_TDM_RX_5", "TERT_TDM_TX_5",
+"TERT_TDM_RX_6", "TERT_TDM_TX_6", "TERT_TDM_RX_7", "TERT_TDM_TX_7",
+"QUAT_TDM_RX_0", "QUAT_TDM_TX_0", "QUAT_TDM_RX_1", "QUAT_TDM_TX_1",
+"QUAT_TDM_RX_2", "QUAT_TDM_TX_2", "QUAT_TDM_RX_3", "QUAT_TDM_TX_3",
+"QUAT_TDM_RX_4", "QUAT_TDM_TX_4", "QUAT_TDM_RX_5", "QUAT_TDM_TX_5",
+"QUAT_TDM_RX_6", "QUAT_TDM_TX_6", "QUAT_TDM_RX_7", "QUAT_TDM_TX_7",
+"INT_BT_A2DP_RX", "USB_RX", "USB_TX", "DISPLAY_PORT_RX",
+"TERT_AUXPCM_RX", "TERT_AUXPCM_TX", "QUAT_AUXPCM_RX", "QUAT_AUXPCM_TX",
+"INT0_MI2S_RX", "INT0_MI2S_TX", "INT1_MI2S_RX", "INT1_MI2S_TX",
+"INT2_MI2S_RX", "INT2_MI2S_TX", "INT3_MI2S_RX", "INT3_MI2S_TX",
+"INT4_MI2S_RX", "INT4_MI2S_TX", "INT5_MI2S_RX", "INT5_MI2S_TX",
+"INT6_MI2S_RX", "INT6_MI2S_TX", "PROXY_RX", "PROXY_TX"
+};
+
+static SOC_ENUM_SINGLE_DECL(mm1_channel_mux,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, ch_mixer);
+static SOC_ENUM_SINGLE_DECL(mm2_channel_mux,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA2, ch_mixer);
+static SOC_ENUM_SINGLE_DECL(mm3_channel_mux,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA3, ch_mixer);
+static SOC_ENUM_SINGLE_DECL(mm4_channel_mux,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA4, ch_mixer);
+
+static SOC_ENUM_DOUBLE_DECL(mm1_ch1_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 0, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch2_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 1, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch3_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 2, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch4_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 3, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch5_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 4, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch6_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 5, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch7_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 6, be_name);
+static SOC_ENUM_DOUBLE_DECL(mm1_ch8_enum,
+ SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 7, be_name);
+
+static int msm_pcm_get_ctl_enum_info(struct snd_ctl_elem_info *uinfo,
+ unsigned int channels,
+ unsigned int items, const char *const names[])
+{
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+
+ WARN(strlen(names[uinfo->value.enumerated.item]) >=
+ sizeof(uinfo->value.enumerated.name),
+ "ALSA: too long item name '%s'\n",
+ names[uinfo->value.enumerated.item]);
+ strlcpy(uinfo->value.enumerated.name,
+ names[uinfo->value.enumerated.item],
+ sizeof(uinfo->value.enumerated.name));
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ uinfo->value.enumerated.items = ARRAY_SIZE(ch_mixer);
+ msm_pcm_get_ctl_enum_info(uinfo, 1, e->items, e->texts);
+
+ return 0;
+}
+static int msm_pcm_channel_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_enum *)
+ kcontrol->private_value)->shift_l;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: FE %d %s\n", __func__,
+ fe_id,
+ channel_mixer[fe_id].enable ? "Enabled" : "Disabled");
+ ucontrol->value.enumerated.item[0] = channel_mixer[fe_id].enable;
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+
+ fe_id = ((struct soc_enum *)
+ kcontrol->private_value)->shift_l;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ channel_mixer[fe_id].enable = ucontrol->value.enumerated.item[0];
+ pr_debug("%s: %s FE %d\n", __func__,
+ channel_mixer[fe_id].enable ? "Enable" : "Disable",
+ fe_id);
+ return 0;
+}
+
+static int msm_pcm_channel_input_be_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ uinfo->value.enumerated.items = ARRAY_SIZE(be_name);
+ msm_pcm_get_ctl_enum_info(uinfo, 1, e->items, e->texts);
+
+ return 0;
+}
+
+static int msm_pcm_channel_input_be_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ u16 fe_id = 0, in_ch = 0;
+
+ fe_id = e->shift_l;
+ in_ch = e->shift_r;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+ if (in_ch >= ADM_MAX_CHANNELS) {
+ pr_err("%s: invalid input channel %d\n", __func__, in_ch);
+ return -EINVAL;
+ }
+
+ channel_input[fe_id][in_ch] = ucontrol->value.enumerated.item[0];
+ return 1;
+}
+
+static int msm_pcm_channel_input_be_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ u16 fe_id = 0, in_ch = 0;
+
+ fe_id = e->shift_l;
+ in_ch = e->shift_r;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+ if (in_ch >= ADM_MAX_CHANNELS) {
+ pr_err("%s: invalid input channel %d\n", __func__, in_ch);
+ return -EINVAL;
+ }
+
+ ucontrol->value.enumerated.item[0] = channel_input[fe_id][in_ch];
+ return 1;
+}
+
+
+static int msm_pcm_channel_weight_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = ADM_MAX_CHANNELS;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = WEIGHT_0_DB;
+
+ return 0;
+}
+
+static int msm_pcm_channel_weight_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0, out_ch = 0;
+ int i, weight;
+
+ fe_id = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ out_ch = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->rshift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+ if (out_ch >= ADM_MAX_CHANNELS) {
+ pr_err("%s: invalid input channel %d\n", __func__, out_ch);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: FE_ID: %d, channel weight %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld\n",
+ __func__, fe_id,
+ ucontrol->value.integer.value[0],
+ ucontrol->value.integer.value[1],
+ ucontrol->value.integer.value[2],
+ ucontrol->value.integer.value[3],
+ ucontrol->value.integer.value[4],
+ ucontrol->value.integer.value[5],
+ ucontrol->value.integer.value[6],
+ ucontrol->value.integer.value[7]);
+
+ for (i = 0; i < ADM_MAX_CHANNELS; ++i) {
+ weight = ucontrol->value.integer.value[i];
+ channel_mixer[fe_id].channel_weight[out_ch][i] = weight;
+ pr_debug("%s: FE_ID %d, output %d input %d weight %d\n",
+ __func__, fe_id, out_ch, i,
+ channel_mixer[fe_id].channel_weight[out_ch][i]);
+ }
+
+ return 0;
+}
+
+static int msm_pcm_channel_weight_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0, out_ch = 0;
+ int i;
+
+ fe_id = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ out_ch = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->rshift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+ if (out_ch >= ADM_MAX_CHANNELS) {
+ pr_err("%s: invalid input channel %d\n", __func__, out_ch);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ADM_MAX_CHANNELS; ++i)
+ ucontrol->value.integer.value[i] =
+ channel_mixer[fe_id].channel_weight[out_ch][i];
+
+ pr_debug("%s: FE_ID: %d, weight %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld",
+ __func__, fe_id,
+ ucontrol->value.integer.value[0],
+ ucontrol->value.integer.value[1],
+ ucontrol->value.integer.value[2],
+ ucontrol->value.integer.value[3],
+ ucontrol->value.integer.value[4],
+ ucontrol->value.integer.value[5],
+ ucontrol->value.integer.value[6],
+ ucontrol->value.integer.value[7]);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new channel_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+ SOC_SINGLE_EXT("MultiMedia2 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+ SOC_SINGLE_EXT("MultiMedia3 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+ SOC_SINGLE_EXT("MultiMedia4 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+ SOC_SINGLE_EXT("MultiMedia5 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+ SOC_SINGLE_EXT("MultiMedia6 Channel Rule", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 8, 0,
+ msm_pcm_get_channel_rule_index,
+ msm_pcm_put_channel_rule_index),
+
+ SOC_SINGLE_EXT("MultiMedia1 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ SOC_SINGLE_EXT("MultiMedia2 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ SOC_SINGLE_EXT("MultiMedia3 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ SOC_SINGLE_EXT("MultiMedia4 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ SOC_SINGLE_EXT("MultiMedia5 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ SOC_SINGLE_EXT("MultiMedia6 Channels", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 8, 0,
+ msm_pcm_get_out_chs,
+ msm_pcm_put_out_chs),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel Mixer",
+ .info = msm_pcm_channel_mixer_info,
+ .get = msm_pcm_channel_mixer_get,
+ .put = msm_pcm_channel_mixer_put,
+ .private_value = (unsigned long)&(mm1_channel_mux)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia2 Channel Mixer",
+ .info = msm_pcm_channel_mixer_info,
+ .get = msm_pcm_channel_mixer_get,
+ .put = msm_pcm_channel_mixer_put,
+ .private_value = (unsigned long)&(mm2_channel_mux)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia3 Channel Mixer",
+ .info = msm_pcm_channel_mixer_info,
+ .get = msm_pcm_channel_mixer_get,
+ .put = msm_pcm_channel_mixer_put,
+ .private_value = (unsigned long)&(mm3_channel_mux)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia4 Channel Mixer",
+ .info = msm_pcm_channel_mixer_info,
+ .get = msm_pcm_channel_mixer_get,
+ .put = msm_pcm_channel_mixer_put,
+ .private_value = (unsigned long)&(mm4_channel_mux)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel1",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 0,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel2",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 1, }
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel3",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 2,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel4",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 3,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel5",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 4,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel6",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 5,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel7",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 6,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Output Channel8",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 7,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia2 Output Channel1",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 0,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia2 Output Channel2",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 1,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia2 Output Channel3",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 2,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia3 Output Channel1",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ {.shift = MSM_FRONTEND_DAI_MULTIMEDIA3, .rshift = 0,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia3 Output Channel2",
+ .info = msm_pcm_channel_weight_info,
+ .get = msm_pcm_channel_weight_get,
+ .put = msm_pcm_channel_weight_put,
+ .private_value = (unsigned long)&(struct soc_multi_mixer_control)
+ {.shift = MSM_FRONTEND_DAI_MULTIMEDIA3, .rshift = 1,}
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel1",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch1_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel2",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch2_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel3",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch3_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel4",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch4_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel5",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch5_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel6",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch6_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel7",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch7_enum)
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "MultiMedia1 Channel8",
+ .info = msm_pcm_channel_input_be_info,
+ .get = msm_pcm_channel_input_be_get,
+ .put = msm_pcm_channel_input_be_put,
+ .private_value = (unsigned long)&(mm1_ch8_enum)
+ },
+};
+
+static int msm_native_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_native_mode) {
+ case 3:
+ case 2:
+ case 1:
+ ucontrol->value.integer.value[0] = msm_native_mode;
+ break;
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: msm_native_mode = %d ucontrol value %ld\n",
+ __func__, msm_native_mode,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_native_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* native flag shift from bit 11 in flags when open adm
+ * 1 << 11: use bit width native mode
+ * 2 << 11: use channel native mode
+ * 3 << 11: use bit width and channel native mode
+ */
+ switch (ucontrol->value.integer.value[0]) {
+ case 3:
+ case 2:
+ case 1:
+ msm_native_mode = ucontrol->value.integer.value[0];
+ break;
+ default:
+ msm_native_mode = 0;
+ break;
+ }
+
+ pr_debug("%s: msm_native_mode = %d ucontrol value %ld\n",
+ __func__, msm_native_mode,
+ ucontrol->value.integer.value[0]);
+ adm_set_native_mode(msm_native_mode);
+ return 0;
+}
+
+static const char *const native_mode_text[] = {"NonNative", "NativeBit",
+ "NativeCh", "NativeChBit"};
+
+static const struct soc_enum native_mode_enum[] = {
+ SOC_ENUM_SINGLE_EXT(4, native_mode_text),
+};
+
+static const struct snd_kcontrol_new native_mode_controls[] = {
+ SOC_ENUM_EXT("Native Mode Config", native_mode_enum[0],
+ msm_native_mode_get, msm_native_mode_put),
+};
+
+static int msm_ec_ref_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_ec_ref_ch;
+ pr_debug("%s: msm_ec_ref_ch = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_ec_ref_ch = ucontrol->value.integer.value[0];
+ pr_debug("%s: msm_ec_ref_ch = %d\n", __func__, msm_ec_ref_ch);
+ adm_num_ec_ref_rx_chans(msm_ec_ref_ch);
+ return 0;
+}
+
+static const char *const ec_ref_ch_text[] = {"Zero", "One", "Two", "Three",
+ "Four", "Five", "Six", "Seven", "Eight"};
+
+static int msm_ec_ref_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_ec_ref_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_ec_ref_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 bit_width = 0;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 1:
+ msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ default:
+ msm_ec_ref_bit_format = 0;
+ break;
+ }
+
+ if (msm_ec_ref_bit_format == SNDRV_PCM_FORMAT_S16_LE)
+ bit_width = 16;
+ else if (msm_ec_ref_bit_format == SNDRV_PCM_FORMAT_S24_LE)
+ bit_width = 24;
+
+ pr_debug("%s: msm_ec_ref_bit_format = %d\n",
+ __func__, msm_ec_ref_bit_format);
+ adm_ec_ref_rx_bit_width(bit_width);
+ return 0;
+}
+
+static char const *ec_ref_bit_format_text[] = {"0", "S16_LE", "S24_LE"};
+
+static int msm_ec_ref_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_ec_ref_sampling_rate;
+ pr_debug("%s: msm_ec_ref_sampling_rate = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_ec_ref_sampling_rate = 0;
+ break;
+ case 1:
+ msm_ec_ref_sampling_rate = 8000;
+ break;
+ case 2:
+ msm_ec_ref_sampling_rate = 16000;
+ break;
+ case 3:
+ msm_ec_ref_sampling_rate = 32000;
+ break;
+ case 4:
+ msm_ec_ref_sampling_rate = 44100;
+ break;
+ case 5:
+ msm_ec_ref_sampling_rate = 48000;
+ break;
+ case 6:
+ msm_ec_ref_sampling_rate = 96000;
+ break;
+ case 7:
+ msm_ec_ref_sampling_rate = 192000;
+ break;
+ case 8:
+ msm_ec_ref_sampling_rate = 384000;
+ break;
+ default:
+ msm_ec_ref_sampling_rate = 48000;
+ break;
+ }
+ pr_debug("%s: msm_ec_ref_sampling_rate = %d\n",
+ __func__, msm_ec_ref_sampling_rate);
+ adm_ec_ref_rx_sampling_rate(msm_ec_ref_sampling_rate);
+ return 0;
+}
+
+static const char *const ec_ref_rate_text[] = {"0", "8000", "16000",
+ "32000", "44100", "48000", "96000", "192000", "384000"};
+
+static const struct soc_enum msm_route_ec_ref_params_enum[] = {
+ SOC_ENUM_SINGLE_EXT(9, ec_ref_ch_text),
+ SOC_ENUM_SINGLE_EXT(3, ec_ref_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(9, ec_ref_rate_text),
+};
+
+static const struct snd_kcontrol_new ec_ref_param_controls[] = {
+ SOC_ENUM_EXT("EC Reference Channels", msm_route_ec_ref_params_enum[0],
+ msm_ec_ref_ch_get, msm_ec_ref_ch_put),
+ SOC_ENUM_EXT("EC Reference Bit Format", msm_route_ec_ref_params_enum[1],
+ msm_ec_ref_bit_format_get, msm_ec_ref_bit_format_put),
+ SOC_ENUM_EXT("EC Reference SampleRate", msm_route_ec_ref_params_enum[2],
+ msm_ec_ref_rate_get, msm_ec_ref_rate_put),
+};
+
+static int msm_routing_ec_ref_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: ec_ref_rx = %d", __func__, msm_route_ec_ref_rx);
+ mutex_lock(&routing_lock);
+ ucontrol->value.integer.value[0] = msm_route_ec_ref_rx;
+ mutex_unlock(&routing_lock);
+ return 0;
+}
+
+static int msm_routing_ec_ref_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ec_ref_port_id;
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_update *update = NULL;
+
+
+ mutex_lock(&routing_lock);
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_route_ec_ref_rx = 0;
+ ec_ref_port_id = AFE_PORT_INVALID;
+ break;
+ case 1:
+ msm_route_ec_ref_rx = 1;
+ ec_ref_port_id = SLIMBUS_0_RX;
+ break;
+ case 2:
+ msm_route_ec_ref_rx = 2;
+ ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+ break;
+ case 3:
+ msm_route_ec_ref_rx = 3;
+ ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+ break;
+ case 4:
+ msm_route_ec_ref_rx = 4;
+ ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+ break;
+ case 5:
+ msm_route_ec_ref_rx = 5;
+ ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+ break;
+ case 6:
+ msm_route_ec_ref_rx = 6;
+ ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+ break;
+ case 7:
+ msm_route_ec_ref_rx = 7;
+ ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
+ break;
+ case 9:
+ msm_route_ec_ref_rx = 9;
+ ec_ref_port_id = SLIMBUS_5_RX;
+ break;
+ case 10:
+ msm_route_ec_ref_rx = 10;
+ ec_ref_port_id = SLIMBUS_1_TX;
+ break;
+ case 11:
+ msm_route_ec_ref_rx = 11;
+ ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_TX_1;
+ break;
+ case 12:
+ msm_route_ec_ref_rx = 12;
+ ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX;
+ break;
+ case 13:
+ msm_route_ec_ref_rx = 13;
+ ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX_1;
+ break;
+ case 14:
+ msm_route_ec_ref_rx = 14;
+ ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX_2;
+ break;
+ case 15:
+ msm_route_ec_ref_rx = 15;
+ ec_ref_port_id = SLIMBUS_6_RX;
+ break;
+ case 16:
+ msm_route_ec_ref_rx = 16;
+ ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX;
+ break;
+ case 17:
+ msm_route_ec_ref_rx = 17;
+ ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX;
+ break;
+ case 18:
+ msm_route_ec_ref_rx = 18;
+ ec_ref_port_id = AFE_PORT_ID_TERTIARY_TDM_TX;
+ break;
+ case 19:
+ msm_route_ec_ref_rx = 19;
+ ec_ref_port_id = AFE_PORT_ID_USB_RX;
+ break;
+ case 20:
+ msm_route_ec_ref_rx = 20;
+ ec_ref_port_id = AFE_PORT_ID_INT0_MI2S_RX;
+ break;
+ case 21:
+ msm_route_ec_ref_rx = 21;
+ ec_ref_port_id = AFE_PORT_ID_INT4_MI2S_RX;
+ break;
+ case 22:
+ msm_route_ec_ref_rx = 22;
+ ec_ref_port_id = AFE_PORT_ID_INT3_MI2S_TX;
+ break;
+ case 23:
+ msm_route_ec_ref_rx = 23;
+ ec_ref_port_id = AFE_PORT_ID_TERTIARY_TDM_RX;
+ break;
+ case 24:
+ msm_route_ec_ref_rx = 24;
+ ec_ref_port_id = AFE_PORT_ID_TERTIARY_TDM_RX_1;
+ break;
+ case 25:
+ msm_route_ec_ref_rx = 25;
+ ec_ref_port_id = AFE_PORT_ID_TERTIARY_TDM_RX_2;
+ break;
+ case 26:
+ msm_route_ec_ref_rx = 26;
+ ec_ref_port_id = AFE_PORT_ID_SECONDARY_TDM_RX;
+ break;
+ case 27:
+ msm_route_ec_ref_rx = 27;
+ ec_ref_port_id = AFE_PORT_ID_SECONDARY_TDM_RX_1;
+ break;
+ case 28:
+ msm_route_ec_ref_rx = 28;
+ ec_ref_port_id = AFE_PORT_ID_SECONDARY_TDM_RX_2;
+ break;
+ case 29:
+ msm_route_ec_ref_rx = 29;
+ ec_ref_port_id = AFE_PORT_ID_PRIMARY_TDM_RX;
+ break;
+ case 30:
+ msm_route_ec_ref_rx = 30;
+ ec_ref_port_id = AFE_PORT_ID_PRIMARY_TDM_RX_1;
+ break;
+ case 31:
+ msm_route_ec_ref_rx = 31;
+ ec_ref_port_id = AFE_PORT_ID_PRIMARY_TDM_RX_2;
+ break;
+ case 32:
+ msm_route_ec_ref_rx = 32;
+ ec_ref_port_id = AFE_PORT_ID_PRIMARY_TDM_RX_3;
+ break;
+ default:
+ msm_route_ec_ref_rx = 0; /* NONE */
+ pr_err("%s EC ref rx %ld not valid\n",
+ __func__, ucontrol->value.integer.value[0]);
+ ec_ref_port_id = AFE_PORT_INVALID;
+ break;
+ }
+ adm_ec_ref_rx_id(ec_ref_port_id);
+ pr_debug("%s: msm_route_ec_ref_rx = %d\n",
+ __func__, msm_route_ec_ref_rx);
+ mutex_unlock(&routing_lock);
+ snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+ msm_route_ec_ref_rx, e, update);
+ return 0;
+}
+
+static const char *const ec_ref_rx[] = { "None", "SLIM_RX", "I2S_RX",
+ "PRI_MI2S_TX", "SEC_MI2S_TX",
+ "TERT_MI2S_TX", "QUAT_MI2S_TX", "SEC_I2S_RX", "PROXY_RX",
+ "SLIM_5_RX", "SLIM_1_TX", "QUAT_TDM_TX_1",
+ "QUAT_TDM_RX_0", "QUAT_TDM_RX_1", "QUAT_TDM_RX_2", "SLIM_6_RX",
+ "TERT_MI2S_RX", "QUAT_MI2S_RX", "TERT_TDM_TX_0", "USB_AUDIO_RX",
+ "INT0_MI2S_RX", "INT4_MI2S_RX", "INT3_MI2S_TX", "TERT_TDM_RX_0",
+ "TERT_TDM_RX_1", "TERT_TDM_RX_2", "SEC_TDM_RX_0", "SEC_TDM_RX_1",
+ "SEC_TDM_RX_2", "PRI_TDM_RX_0", "PRI_TDM_RX_1", "PRI_TDM_RX_2",
+ "PRI_TDM_RX_3"};
+
+static const struct soc_enum msm_route_ec_ref_rx_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ec_ref_rx), ec_ref_rx),
+};
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul1 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL1 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul2 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL2 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul3 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL3 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul4 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL4 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul5 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL5 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul6 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL6 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul8 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL8 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul9 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL9 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul16 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL16 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul17 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL17 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul18 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL18 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul19 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL19 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul28 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL28 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul29 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL29 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static int msm_routing_ext_ec_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: ext_ec_ref_rx = %x\n", __func__, msm_route_ext_ec_ref);
+
+ mutex_lock(&routing_lock);
+ ucontrol->value.integer.value[0] = msm_route_ext_ec_ref;
+ mutex_unlock(&routing_lock);
+ return 0;
+}
+
+static int msm_routing_ext_ec_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist =
+ dapm_kcontrol_get_wlist(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ int mux = ucontrol->value.enumerated.item[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int ret = 1;
+ bool state = true;
+ uint16_t ext_ec_ref_port_id;
+ struct snd_soc_dapm_update *update = NULL;
+
+ if (mux >= e->items) {
+ pr_err("%s: Invalid mux value %d\n", __func__, mux);
+ return -EINVAL;
+ }
+
+ mutex_lock(&routing_lock);
+ msm_route_ext_ec_ref = ucontrol->value.integer.value[0];
+
+ switch (msm_route_ext_ec_ref) {
+ case EXT_EC_REF_PRI_MI2S_TX:
+ ext_ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+ break;
+ case EXT_EC_REF_SEC_MI2S_TX:
+ ext_ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+ break;
+ case EXT_EC_REF_TERT_MI2S_TX:
+ ext_ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+ break;
+ case EXT_EC_REF_QUAT_MI2S_TX:
+ ext_ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+ break;
+ case EXT_EC_REF_QUIN_MI2S_TX:
+ ext_ec_ref_port_id = AFE_PORT_ID_QUINARY_MI2S_TX;
+ break;
+ case EXT_EC_REF_SLIM_1_TX:
+ ext_ec_ref_port_id = SLIMBUS_1_TX;
+ break;
+ case EXT_EC_REF_NONE:
+ default:
+ ext_ec_ref_port_id = AFE_PORT_INVALID;
+ state = false;
+ break;
+ }
+
+ pr_debug("%s: val = %d ext_ec_ref_port_id = 0x%0x state = %d\n",
+ __func__, msm_route_ext_ec_ref, ext_ec_ref_port_id, state);
+
+ if (!voc_set_ext_ec_ref_port_id(ext_ec_ref_port_id, state)) {
+ mutex_unlock(&routing_lock);
+ snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e, update);
+ } else {
+ ret = -EINVAL;
+ mutex_unlock(&routing_lock);
+ }
+ return ret;
+}
+
+static const char * const ext_ec_ref_rx[] = {"NONE", "PRI_MI2S_TX",
+ "SEC_MI2S_TX", "TERT_MI2S_TX",
+ "QUAT_MI2S_TX", "QUIN_MI2S_TX",
+ "SLIM_1_TX"};
+
+static const struct soc_enum msm_route_ext_ec_ref_rx_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ext_ec_ref_rx), ext_ec_ref_rx),
+};
+
+static const struct snd_kcontrol_new voc_ext_ec_mux =
+ SOC_DAPM_ENUM_EXT("VOC_EXT_EC MUX Mux", msm_route_ext_ec_ref_rx_enum[0],
+ msm_routing_ext_ec_get, msm_routing_ext_ec_put);
+
+
+static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new spdif_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_2_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia23", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new secondary_mi2s_rx2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_SD1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new secondary_mi2s_rx_1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new secondary_mi2s_rx_2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new secondary_mi2s_rx_3_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new secondary_mi2s_rx_4_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tertiary_mi2s_rx_1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tertiary_mi2s_rx_2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tertiary_mi2s_rx_3_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tertiary_mi2s_rx_4_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quaternary_mi2s_rx_1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quaternary_mi2s_rx_2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quaternary_mi2s_rx_3_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quaternary_mi2s_rx_4_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int0_mi2s_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int4_mi2s_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia23", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new display_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+ /* incall music delivery mixer */
+static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new incall_music2_delivery_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_4_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_6_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia23", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_7_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia23", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new usb_audio_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_bt_a2dp_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_auxpcm_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_auxpcm_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_rx_1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_rx_2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_rx_3_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_tx_0_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_tx_1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+
+};
+
+static const struct snd_kcontrol_new pri_tdm_tx_2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+
+};
+
+static const struct snd_kcontrol_new pri_tdm_tx_3_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+
+};
+
+static const struct snd_kcontrol_new sec_tdm_rx_0_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_tdm_rx_1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_tdm_rx_2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_tdm_rx_3_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_tdm_tx_0_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_tx_0_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+
+static const struct snd_kcontrol_new tert_tdm_tx_1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_tx_2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_tx_3_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia32", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia33", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_4_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_tx_0_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia22", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT2_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_4_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT2_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul3_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_AUX_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT2_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul4_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT2_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul5_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_AUX_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT2_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul6_mixer_controls[] = {
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT2_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul8_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT2_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul16_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT2_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul9_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul17_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul18_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul19_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul20_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX_4", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_4,
+ MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul21_mixer_controls[] = {
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul27_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul28_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul29_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul30_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul31_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul32_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA32, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul33_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA33, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new sec_mi2s_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_6_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new usb_audio_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new mi2s_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new pri_mi2s_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new int0_mi2s_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new int4_mi2s_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tert_mi2s_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new quat_mi2s_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new quin_mi2s_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tert_aux_pcm_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new quat_aux_pcm_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_7_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_8_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("CSVoice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_2_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new stub_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_EXTPROC_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_EXTPROC_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_EXTPROC_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_1_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_3_rx_mixer_controls[] = {
+ SOC_DOUBLE_EXT("Voice Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("Voice2 Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("VoLTE Stub", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_CS_VOICE, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX_Voice", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOICE2, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX_Voice2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_volte_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOLTE, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX_VoLTE", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_vowlan_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOWLAN, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX_VoWLAN", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new proxy_rx_voice_mixer_controls[] = {
+ SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PROXY_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PROXY_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voicemmode1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INT_BT_SCO_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1,
+ 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1,
+ 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, MSM_FRONTEND_DAI_VOICEMMODE1,
+ 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("PROXY_TX_MMode1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PROXY_TX, MSM_FRONTEND_DAI_VOICEMMODE1,
+ 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("PROXY_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PROXY_TX, MSM_FRONTEND_DAI_VOICEMMODE2,
+ 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voicemmode2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INT_BT_SCO_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2,
+ 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2,
+ 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX_MMode2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX_Voip", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice_stub_mixer_controls[] = {
+ SOC_DOUBLE_EXT("STUB_TX_HL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_EXTPROC_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("STUB_1_TX_HL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_EXTPROC_EC_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_3_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice2_stub_mixer_controls[] = {
+ SOC_DOUBLE_EXT("STUB_TX_HL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_EXTPROC_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("STUB_1_TX_HL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_EXTPROC_EC_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_3_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new tx_volte_stub_mixer_controls[] = {
+ SOC_DOUBLE_EXT("STUB_TX_HL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_EXTPROC_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("STUB_1_TX_HL", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_EXTPROC_EC_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_3_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new tx_qchat_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_QCHAT, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_DOUBLE_EXT("USB_AUDIO_TX_QCHAT", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new int0_mi2s_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_BACKEND_DAI_INT3_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new int4_mi2s_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_BACKEND_DAI_INT3_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_RX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_PRI_MI2S_RX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_RX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_RX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_RX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new aux_pcm_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_auxpcm_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_auxpcm_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_1_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_3_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_RX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_RX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ MSM_BACKEND_DAI_AFE_PCM_RX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_RX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ MSM_BACKEND_DAI_AUXPCM_RX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_RX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_6_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new bt_sco_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new afe_pcm_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+
+static const struct snd_kcontrol_new hdmi_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new display_port_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sec_i2s_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new mi2s_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new primary_mi2s_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new usb_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_BACKEND_DAI_USB_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_mi2s_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_rx_0_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_rx_1_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_rx_2_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_rx_3_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sec_tdm_rx_0_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sec_tdm_rx_1_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sec_tdm_rx_2_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sec_tdm_rx_3_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_0_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_1_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_2_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_3_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_0_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_1_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_2_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_3_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+ msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_mi2s_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sec_mi2s_rx_port_mixer_controls[] = {
+ SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new lsm1_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+};
+
+static const struct snd_kcontrol_new lsm2_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+};
+
+static const struct snd_kcontrol_new lsm3_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+};
+
+static const struct snd_kcontrol_new lsm4_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+};
+
+static const struct snd_kcontrol_new lsm5_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+};
+
+static const struct snd_kcontrol_new lsm6_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+};
+
+static const struct snd_kcontrol_new lsm7_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+};
+
+static const struct snd_kcontrol_new lsm8_mixer_controls[] = {
+ SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+ SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
+};
+
+
+static const struct snd_kcontrol_new slim_fm_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_switch_mixer,
+ msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new slim1_fm_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_switch_mixer,
+ msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new slim3_fm_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_switch_mixer,
+ msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new slim4_fm_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_switch_mixer,
+ msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new slim6_fm_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_switch_mixer,
+ msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new pcm_rx_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_fm_pcmrx_switch_mixer,
+ msm_routing_put_fm_pcmrx_switch_mixer);
+
+static const struct snd_kcontrol_new int0_mi2s_rx_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_int0_mi2s_switch_mixer,
+ msm_routing_put_int0_mi2s_switch_mixer);
+
+static const struct snd_kcontrol_new int4_mi2s_rx_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_int4_mi2s_switch_mixer,
+ msm_routing_put_int4_mi2s_switch_mixer);
+
+static const struct snd_kcontrol_new pri_mi2s_rx_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_pri_mi2s_switch_mixer,
+ msm_routing_put_pri_mi2s_switch_mixer);
+
+static const struct snd_kcontrol_new sec_mi2s_rx_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_sec_mi2s_switch_mixer,
+ msm_routing_put_sec_mi2s_switch_mixer);
+
+static const struct snd_kcontrol_new tert_mi2s_rx_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_tert_mi2s_switch_mixer,
+ msm_routing_put_tert_mi2s_switch_mixer);
+
+static const struct snd_kcontrol_new quat_mi2s_rx_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_quat_mi2s_switch_mixer,
+ msm_routing_put_quat_mi2s_switch_mixer);
+
+static const struct snd_kcontrol_new hfp_pri_aux_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_hfp_switch_mixer,
+ msm_routing_put_hfp_switch_mixer);
+
+static const struct snd_kcontrol_new hfp_aux_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_hfp_switch_mixer,
+ msm_routing_put_hfp_switch_mixer);
+
+static const struct snd_kcontrol_new hfp_int_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_hfp_switch_mixer,
+ msm_routing_put_hfp_switch_mixer);
+
+static const struct snd_kcontrol_new hfp_slim7_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_hfp_switch_mixer,
+ msm_routing_put_hfp_switch_mixer);
+
+static const struct snd_kcontrol_new usb_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_usb_switch_mixer,
+ msm_routing_put_usb_switch_mixer);
+
+static const struct soc_enum lsm_port_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lsm_port_text), lsm_port_text);
+
+static const char * const lsm_func_text[] = {
+ "None", "AUDIO", "BEACON", "ULTRASOUND", "SWAUDIO",
+};
+static const struct soc_enum lsm_func_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lsm_func_text), lsm_func_text);
+
+static const struct snd_kcontrol_new lsm_controls[] = {
+ /* kcontrol of lsm_function */
+ SOC_ENUM_EXT(SLIMBUS_0_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(SLIMBUS_1_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(SLIMBUS_2_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(SLIMBUS_3_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(SLIMBUS_4_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(SLIMBUS_5_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(TERT_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(QUAT_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(INT3_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(QUAT_TDM_TX_0_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ /* kcontrol of lsm_port */
+ SOC_ENUM_EXT("LSM1 Port", lsm_port_enum,
+ msm_routing_lsm_port_get,
+ msm_routing_lsm_port_put),
+ SOC_ENUM_EXT("LSM2 Port", lsm_port_enum,
+ msm_routing_lsm_port_get,
+ msm_routing_lsm_port_put),
+ SOC_ENUM_EXT("LSM3 Port", lsm_port_enum,
+ msm_routing_lsm_port_get,
+ msm_routing_lsm_port_put),
+ SOC_ENUM_EXT("LSM4 Port", lsm_port_enum,
+ msm_routing_lsm_port_get,
+ msm_routing_lsm_port_put),
+ SOC_ENUM_EXT("LSM5 Port", lsm_port_enum,
+ msm_routing_lsm_port_get,
+ msm_routing_lsm_port_put),
+ SOC_ENUM_EXT("LSM6 Port", lsm_port_enum,
+ msm_routing_lsm_port_get,
+ msm_routing_lsm_port_put),
+ SOC_ENUM_EXT("LSM7 Port", lsm_port_enum,
+ msm_routing_lsm_port_get,
+ msm_routing_lsm_port_put),
+ SOC_ENUM_EXT("LSM8 Port", lsm_port_enum,
+ msm_routing_lsm_port_get,
+ msm_routing_lsm_port_put),
+ SOC_ENUM_EXT("LSM9 Port", lsm_port_enum,
+ msm_routing_lsm_port_get,
+ msm_routing_lsm_port_put),
+};
+
+static const char * const aanc_slim_0_rx_text[] = {
+ "ZERO", "SLIMBUS_0_TX", "SLIMBUS_1_TX", "SLIMBUS_2_TX", "SLIMBUS_3_TX",
+ "SLIMBUS_4_TX", "SLIMBUS_5_TX", "SLIMBUS_6_TX"
+};
+
+static const struct soc_enum aanc_slim_0_rx_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aanc_slim_0_rx_text),
+ aanc_slim_0_rx_text);
+
+static const struct snd_kcontrol_new aanc_slim_0_rx_mux[] = {
+ SOC_ENUM_EXT("AANC_SLIM_0_RX MUX", aanc_slim_0_rx_enum,
+ msm_routing_slim_0_rx_aanc_mux_get,
+ msm_routing_slim_0_rx_aanc_mux_put)
+};
+
+static int msm_routing_get_stereo_to_custom_stereo_control(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = is_custom_stereo_on;
+ return 0;
+}
+
+static int msm_routing_put_stereo_to_custom_stereo_control(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int flag = 0, i = 0, rc = 0, idx = 0;
+ int be_index = 0, port_id, topo_id;
+ unsigned int session_id = 0;
+ uint16_t op_FL_ip_FL_weight = 0;
+ uint16_t op_FL_ip_FR_weight = 0;
+ uint16_t op_FR_ip_FL_weight = 0;
+ uint16_t op_FR_ip_FR_weight = 0;
+ flag = ucontrol->value.integer.value[0];
+ pr_debug("%s E flag %d\n", __func__, flag);
+
+ if ((is_custom_stereo_on && flag) || (!is_custom_stereo_on && !flag)) {
+ pr_err("%s: is_custom_stereo_on %d, flag %d\n",
+ __func__, is_custom_stereo_on, flag);
+ return 0;
+ }
+ is_custom_stereo_on = flag ? true : false;
+ pr_debug("%s:is_custom_stereo_on %d\n", __func__, is_custom_stereo_on);
+ for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) {
+ port_id = msm_bedais[be_index].port_id;
+ if (!msm_bedais[be_index].active)
+ continue;
+ if ((port_id != SLIMBUS_0_RX) &&
+ (port_id != RT_PROXY_PORT_001_RX) &&
+ (port_id != AFE_PORT_ID_PRIMARY_MI2S_RX) &&
+ (port_id != AFE_PORT_ID_INT4_MI2S_RX))
+ continue;
+
+ for_each_set_bit(i, &msm_bedais[be_index].fe_sessions[0],
+ MSM_FRONTEND_DAI_MM_SIZE) {
+ if (fe_dai_map[i][SESSION_TYPE_RX].perf_mode !=
+ LEGACY_PCM_MODE)
+ goto skip_send_custom_stereo;
+ session_id =
+ fe_dai_map[i][SESSION_TYPE_RX].strm_id;
+ if (is_custom_stereo_on) {
+ op_FL_ip_FL_weight =
+ Q14_GAIN_ZERO_POINT_FIVE;
+ op_FL_ip_FR_weight =
+ Q14_GAIN_ZERO_POINT_FIVE;
+ op_FR_ip_FL_weight =
+ Q14_GAIN_ZERO_POINT_FIVE;
+ op_FR_ip_FR_weight =
+ Q14_GAIN_ZERO_POINT_FIVE;
+ } else {
+ op_FL_ip_FL_weight = Q14_GAIN_UNITY;
+ op_FL_ip_FR_weight = 0;
+ op_FR_ip_FL_weight = 0;
+ op_FR_ip_FR_weight = Q14_GAIN_UNITY;
+ }
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+ unsigned long copp =
+ session_copp_map[i]
+ [SESSION_TYPE_RX][be_index];
+ if (!test_bit(idx, &copp))
+ goto skip_send_custom_stereo;
+ topo_id = adm_get_topology_for_port_copp_idx(
+ msm_bedais[be_index].port_id, idx);
+ if (topo_id < 0)
+ pr_debug("%s:Err:custom stereo topo %d",
+ __func__, topo_id);
+ pr_debug("idx %d\n", idx);
+ if (topo_id == DS2_ADM_COPP_TOPOLOGY_ID)
+ rc = msm_ds2_dap_set_custom_stereo_onoff
+ (msm_bedais[be_index].port_id,
+ idx, is_custom_stereo_on);
+ else if (topo_id == DOLBY_ADM_COPP_TOPOLOGY_ID)
+ rc = dolby_dap_set_custom_stereo_onoff(
+ msm_bedais[be_index].port_id,
+ idx, is_custom_stereo_on);
+ else
+ rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd
+ (msm_bedais[be_index].port_id,
+ idx, session_id,
+ op_FL_ip_FL_weight,
+ op_FL_ip_FR_weight,
+ op_FR_ip_FL_weight,
+ op_FR_ip_FR_weight);
+ if (rc < 0)
+skip_send_custom_stereo:
+ pr_err("%s: err setting custom stereo\n",
+ __func__);
+ }
+
+ }
+ }
+ return 0;
+}
+
+static const struct snd_kcontrol_new stereo_to_custom_stereo_controls[] = {
+ SOC_SINGLE_EXT("Set Custom Stereo OnOff", SND_SOC_NOPM, 0,
+ 1, 0, msm_routing_get_stereo_to_custom_stereo_control,
+ msm_routing_put_stereo_to_custom_stereo_control),
+};
+
+static int msm_routing_get_app_type_cfg_control(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int msm_routing_put_app_type_cfg_control(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i = 0, j;
+ int num_app_types = ucontrol->value.integer.value[i++];
+
+ pr_debug("%s\n", __func__);
+
+ memset(app_type_cfg, 0, MAX_APP_TYPES*
+ sizeof(struct msm_pcm_routing_app_type_data));
+ if (num_app_types > MAX_APP_TYPES || num_app_types < 0) {
+ pr_err("%s: number of app types %d is invalid\n",
+ __func__, num_app_types);
+ return -EINVAL;
+ }
+ for (j = 0; j < num_app_types; j++) {
+ app_type_cfg[j].app_type =
+ ucontrol->value.integer.value[i++];
+ app_type_cfg[j].sample_rate =
+ ucontrol->value.integer.value[i++];
+ app_type_cfg[j].bit_width =
+ ucontrol->value.integer.value[i++];
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new app_type_cfg_controls[] = {
+ SOC_SINGLE_MULTI_EXT("App Type Config", SND_SOC_NOPM, 0,
+ 0x7FFFFFFF, 0, 128, msm_routing_get_app_type_cfg_control,
+ msm_routing_put_app_type_cfg_control),
+};
+
+static int msm_routing_get_lsm_app_type_cfg_control(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int msm_routing_put_lsm_app_type_cfg_control(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i = 0, j;
+ int num_app_types;
+
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] > MAX_APP_TYPES) {
+ pr_err("%s: number of app types %ld is invalid\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return -EINVAL;
+ }
+
+ num_app_types = ucontrol->value.integer.value[i++];
+ memset(lsm_app_type_cfg, 0, MAX_APP_TYPES*
+ sizeof(struct msm_pcm_routing_app_type_data));
+
+ for (j = 0; j < num_app_types; j++) {
+ lsm_app_type_cfg[j].app_type =
+ ucontrol->value.integer.value[i++];
+ lsm_app_type_cfg[j].sample_rate =
+ ucontrol->value.integer.value[i++];
+ lsm_app_type_cfg[j].bit_width =
+ ucontrol->value.integer.value[i++];
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new lsm_app_type_cfg_controls[] = {
+ SOC_SINGLE_MULTI_EXT("Listen App Type Config", SND_SOC_NOPM, 0,
+ 0xFFFFFFFF, 0, 128, msm_routing_get_lsm_app_type_cfg_control,
+ msm_routing_put_lsm_app_type_cfg_control),
+};
+
+static int msm_routing_get_use_ds1_or_ds2_control(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = is_ds2_on;
+ return 0;
+}
+
+static int msm_routing_put_use_ds1_or_ds2_control(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ is_ds2_on = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static const struct snd_kcontrol_new use_ds1_or_ds2_controls[] = {
+ SOC_SINGLE_EXT("DS2 OnOff", SND_SOC_NOPM, 0,
+ 1, 0, msm_routing_get_use_ds1_or_ds2_control,
+ msm_routing_put_use_ds1_or_ds2_control),
+};
+
+int msm_routing_get_rms_value_control(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol) {
+ int rc = 0;
+ int be_idx = 0;
+ char *param_value;
+ int *update_param_value;
+ uint32_t param_size = (RMS_PAYLOAD_LEN + 1) * sizeof(uint32_t);
+ struct param_hdr_v3 param_hdr = {0};
+
+ param_value = kzalloc(param_size, GFP_KERNEL);
+ if (!param_value)
+ return -ENOMEM;
+
+ for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++)
+ if (msm_bedais[be_idx].port_id == SLIMBUS_0_TX)
+ break;
+ if ((be_idx < MSM_BACKEND_DAI_MAX) && msm_bedais[be_idx].active) {
+ param_hdr.module_id = RMS_MODULEID_APPI_PASSTHRU;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = RMS_PARAM_FIRST_SAMPLE;
+ param_hdr.param_size = param_size;
+ rc = adm_get_pp_params(SLIMBUS_0_TX, 0, ADM_CLIENT_ID_DEFAULT,
+ NULL, &param_hdr, (u8 *) param_value);
+ if (rc) {
+ pr_err("%s: get parameters failed:%d\n", __func__, rc);
+ kfree(param_value);
+ return -EINVAL;
+ }
+ update_param_value = (int *)param_value;
+ ucontrol->value.integer.value[0] = update_param_value[0];
+
+ pr_debug("%s: FROM DSP value[0] 0x%x\n",
+ __func__, update_param_value[0]);
+ }
+ kfree(param_value);
+ return 0;
+}
+
+static int msm_voc_session_id_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ voc_session_id = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: voc_session_id=%u\n", __func__, voc_session_id);
+
+ return 0;
+}
+
+static int msm_voc_session_id_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = voc_session_id;
+
+ return 0;
+}
+
+static struct snd_kcontrol_new msm_voc_session_controls[] = {
+ SOC_SINGLE_MULTI_EXT("Voc VSID", SND_SOC_NOPM, 0,
+ 0xFFFFFFFF, 0, 1, msm_voc_session_id_get,
+ msm_voc_session_id_put),
+};
+
+static int msm_sound_focus_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(struct sound_focus_param);
+
+ return 0;
+}
+
+static int msm_voice_sound_focus_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct sound_focus_param soundFocusData;
+
+ memcpy((void *)&soundFocusData, ucontrol->value.bytes.data,
+ sizeof(struct sound_focus_param));
+ ret = voc_set_sound_focus(soundFocusData);
+ if (ret) {
+ pr_err("%s: Error setting Sound Focus Params, err=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int msm_voice_sound_focus_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct sound_focus_param soundFocusData;
+
+ memset(&soundFocusData, 0, sizeof(struct sound_focus_param));
+
+ ret = voc_get_sound_focus(&soundFocusData);
+ if (ret) {
+ pr_err("%s: Error getting Sound Focus Params, err=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ memcpy(ucontrol->value.bytes.data, (void *)&soundFocusData,
+ sizeof(struct sound_focus_param));
+
+done:
+ return ret;
+}
+
+static int msm_source_tracking_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(struct source_tracking_param);
+
+ return 0;
+}
+
+static int msm_voice_source_tracking_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct source_tracking_param sourceTrackingData;
+
+ memset(&sourceTrackingData, 0, sizeof(struct source_tracking_param));
+
+ ret = voc_get_source_tracking(&sourceTrackingData);
+ if (ret) {
+ pr_err("%s: Error getting Source Tracking Params, err=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ memcpy(ucontrol->value.bytes.data, (void *)&sourceTrackingData,
+ sizeof(struct source_tracking_param));
+
+done:
+ return ret;
+}
+
+static int msm_audio_get_copp_idx_from_port_id(int port_id, int session_type,
+ int *copp_idx)
+{
+ int i, idx, be_idx;
+ int ret = 0;
+ unsigned long copp;
+
+ pr_debug("%s: Enter, port_id=%d\n", __func__, port_id);
+
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: port validation failed id 0x%x ret %d\n",
+ __func__, port_id, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) {
+ if (msm_bedais[be_idx].port_id == port_id)
+ break;
+ }
+ if (be_idx >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: Invalid be id %d\n", __func__, be_idx);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ for_each_set_bit(i, &msm_bedais[be_idx].fe_sessions[0],
+ MSM_FRONTEND_DAI_MM_SIZE) {
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+ copp = session_copp_map[i]
+ [session_type][be_idx];
+ if (test_bit(idx, &copp))
+ break;
+ }
+ if (idx >= MAX_COPPS_PER_PORT)
+ continue;
+ else
+ break;
+ }
+ if (i >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: Invalid FE, exiting\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ *copp_idx = idx;
+ pr_debug("%s: copp_idx=%d\n", __func__, *copp_idx);
+
+done:
+ return ret;
+}
+
+static int msm_audio_sound_focus_derive_port_id(struct snd_kcontrol *kcontrol,
+ const char *prefix, int *port_id)
+{
+ int ret = 0;
+
+ pr_debug("%s: Enter, prefix:%s\n", __func__, prefix);
+
+ /*
+ * Mixer control name will be like "Sound Focus Audio Tx SLIMBUS_0"
+ * where the prefix is "Sound Focus Audio Tx ". Skip the prefix
+ * and compare the string with the backend name to derive the port id.
+ */
+ if (!strcmp(kcontrol->id.name + strlen(prefix),
+ "SLIMBUS_0")) {
+ *port_id = SLIMBUS_0_TX;
+ } else if (!strcmp(kcontrol->id.name + strlen(prefix),
+ "TERT_MI2S")) {
+ *port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+ } else if (!strcmp(kcontrol->id.name + strlen(prefix),
+ "INT3_MI2S")) {
+ *port_id = AFE_PORT_ID_INT3_MI2S_TX;
+ } else {
+ pr_err("%s: mixer ctl name=%s, could not derive valid port id\n",
+ __func__, kcontrol->id.name);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ pr_debug("%s: mixer ctl name=%s, derived port_id=%d\n",
+ __func__, kcontrol->id.name, *port_id);
+
+done:
+ return ret;
+}
+
+static int msm_audio_sound_focus_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct sound_focus_param soundFocusData;
+ int port_id, copp_idx;
+
+ ret = msm_audio_sound_focus_derive_port_id(kcontrol,
+ "Sound Focus Audio Tx ", &port_id);
+ if (ret != 0) {
+ pr_err("%s: Error in deriving port id, err=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX,
+ &copp_idx);
+ if (ret) {
+ pr_err("%s: Could not get copp idx for port_id=%d\n",
+ __func__, port_id);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy((void *)&soundFocusData, ucontrol->value.bytes.data,
+ sizeof(struct sound_focus_param));
+
+ ret = adm_set_sound_focus(port_id, copp_idx, soundFocusData);
+ if (ret) {
+ pr_err("%s: Error setting Sound Focus Params, err=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int msm_audio_sound_focus_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct sound_focus_param soundFocusData;
+ int port_id, copp_idx;
+
+ ret = msm_audio_sound_focus_derive_port_id(kcontrol,
+ "Sound Focus Audio Tx ", &port_id);
+ if (ret) {
+ pr_err("%s: Error in deriving port id, err=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX,
+ &copp_idx);
+ if (ret) {
+ pr_err("%s: Could not get copp idx for port_id=%d\n",
+ __func__, port_id);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = adm_get_sound_focus(port_id, copp_idx, &soundFocusData);
+ if (ret) {
+ pr_err("%s: Error getting Sound Focus Params, err=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(ucontrol->value.bytes.data, (void *)&soundFocusData,
+ sizeof(struct sound_focus_param));
+
+done:
+ return ret;
+}
+
+static int msm_audio_source_tracking_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct source_tracking_param sourceTrackingData;
+ int port_id, copp_idx;
+
+ ret = msm_audio_sound_focus_derive_port_id(kcontrol,
+ "Source Tracking Audio Tx ", &port_id);
+ if (ret) {
+ pr_err("%s: Error in deriving port id, err=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX,
+ &copp_idx);
+ if (ret) {
+ pr_err("%s: Could not get copp idx for port_id=%d\n",
+ __func__, port_id);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = adm_get_source_tracking(port_id, copp_idx, &sourceTrackingData);
+ if (ret) {
+ pr_err("%s: Error getting Source Tracking Params, err=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(ucontrol->value.bytes.data, (void *)&sourceTrackingData,
+ sizeof(struct source_tracking_param));
+
+done:
+ return ret;
+}
+
+static const struct snd_kcontrol_new msm_source_tracking_controls[] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Sound Focus Voice Tx SLIMBUS_0",
+ .info = msm_sound_focus_info,
+ .get = msm_voice_sound_focus_get,
+ .put = msm_voice_sound_focus_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Source Tracking Voice Tx SLIMBUS_0",
+ .info = msm_source_tracking_info,
+ .get = msm_voice_source_tracking_get,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Sound Focus Audio Tx SLIMBUS_0",
+ .info = msm_sound_focus_info,
+ .get = msm_audio_sound_focus_get,
+ .put = msm_audio_sound_focus_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Source Tracking Audio Tx SLIMBUS_0",
+ .info = msm_source_tracking_info,
+ .get = msm_audio_source_tracking_get,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Sound Focus Voice Tx TERT_MI2S",
+ .info = msm_sound_focus_info,
+ .get = msm_voice_sound_focus_get,
+ .put = msm_voice_sound_focus_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Source Tracking Voice Tx TERT_MI2S",
+ .info = msm_source_tracking_info,
+ .get = msm_voice_source_tracking_get,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Sound Focus Audio Tx TERT_MI2S",
+ .info = msm_sound_focus_info,
+ .get = msm_audio_sound_focus_get,
+ .put = msm_audio_sound_focus_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Source Tracking Audio Tx TERT_MI2S",
+ .info = msm_source_tracking_info,
+ .get = msm_audio_source_tracking_get,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Sound Focus Voice Tx INT3_MI2S",
+ .info = msm_sound_focus_info,
+ .get = msm_voice_sound_focus_get,
+ .put = msm_voice_sound_focus_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Source Tracking Voice Tx INT3_MI2S",
+ .info = msm_source_tracking_info,
+ .get = msm_voice_source_tracking_get,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Sound Focus Audio Tx INT3_MI2S",
+ .info = msm_sound_focus_info,
+ .get = msm_audio_sound_focus_get,
+ .put = msm_audio_sound_focus_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Source Tracking Audio Tx INT3_MI2S",
+ .info = msm_source_tracking_info,
+ .get = msm_audio_source_tracking_get,
+ },
+};
+
+static int spkr_prot_put_vi_lch_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int item;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ pr_debug("%s item is %d\n", __func__,
+ ucontrol->value.enumerated.item[0]);
+ mutex_lock(&routing_lock);
+ item = ucontrol->value.enumerated.item[0];
+ if (item < e->items) {
+ pr_debug("%s RX DAI ID %d TX DAI id %d\n",
+ __func__, e->shift_l , e->values[item]);
+ if (e->shift_l < MSM_BACKEND_DAI_MAX &&
+ e->values[item] < MSM_BACKEND_DAI_MAX)
+ /* Enable feedback TX path */
+ ret = afe_spk_prot_feed_back_cfg(
+ msm_bedais[e->values[item]].port_id,
+ msm_bedais[e->shift_l].port_id, 1, 0, 1);
+ else {
+ pr_debug("%s values are out of range item %d\n",
+ __func__, e->values[item]);
+ /* Disable feedback TX path */
+ if (e->values[item] == MSM_BACKEND_DAI_MAX)
+ ret = afe_spk_prot_feed_back_cfg(0, 0, 0, 0, 0);
+ else
+ ret = -EINVAL;
+ }
+ } else {
+ pr_err("%s item value is out of range item\n", __func__);
+ ret = -EINVAL;
+ }
+ mutex_unlock(&routing_lock);
+ return ret;
+}
+
+static int spkr_prot_put_vi_rch_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int item;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ pr_debug("%s item is %d\n", __func__,
+ ucontrol->value.enumerated.item[0]);
+ mutex_lock(&routing_lock);
+ item = ucontrol->value.enumerated.item[0];
+ if (item < e->items) {
+ pr_debug("%s RX DAI ID %d TX DAI id %d\n",
+ __func__, e->shift_l , e->values[item]);
+ if (e->shift_l < MSM_BACKEND_DAI_MAX &&
+ e->values[item] < MSM_BACKEND_DAI_MAX)
+ /* Enable feedback TX path */
+ ret = afe_spk_prot_feed_back_cfg(
+ msm_bedais[e->values[item]].port_id,
+ msm_bedais[e->shift_l].port_id,
+ 1, 1, 1);
+ else {
+ pr_debug("%s values are out of range item %d\n",
+ __func__, e->values[item]);
+ /* Disable feedback TX path */
+ if (e->values[item] == MSM_BACKEND_DAI_MAX)
+ ret = afe_spk_prot_feed_back_cfg(0,
+ 0, 0, 0, 0);
+ else
+ ret = -EINVAL;
+ }
+ } else {
+ pr_err("%s item value is out of range item\n", __func__);
+ ret = -EINVAL;
+ }
+ mutex_unlock(&routing_lock);
+ return ret;
+}
+
+static int spkr_prot_get_vi_lch_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int spkr_prot_get_vi_rch_port(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s\n", __func__);
+ ucontrol->value.enumerated.item[0] = 0;
+ return 0;
+}
+
+static const char * const slim0_rx_vi_fb_tx_lch_mux_text[] = {
+ "ZERO", "SLIM4_TX"
+};
+
+static const char * const slim0_rx_vi_fb_tx_rch_mux_text[] = {
+ "ZERO", "SLIM4_TX"
+};
+
+static const char * const mi2s_rx_vi_fb_tx_mux_text[] = {
+ "ZERO", "SENARY_TX"
+};
+
+static const char * const int4_mi2s_rx_vi_fb_tx_mono_mux_text[] = {
+ "ZERO", "INT5_MI2S_TX"
+};
+
+static const char * const int4_mi2s_rx_vi_fb_tx_stereo_mux_text[] = {
+ "ZERO", "INT5_MI2S_TX"
+};
+
+static const int const slim0_rx_vi_fb_tx_lch_value[] = {
+ MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SLIMBUS_4_TX
+};
+
+static const int const slim0_rx_vi_fb_tx_rch_value[] = {
+ MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SLIMBUS_4_TX
+};
+
+static const int const mi2s_rx_vi_fb_tx_value[] = {
+ MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SENARY_MI2S_TX
+};
+
+static const int const int4_mi2s_rx_vi_fb_tx_mono_ch_value[] = {
+ MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_INT5_MI2S_TX
+};
+
+static const int const int4_mi2s_rx_vi_fb_tx_stereo_ch_value[] = {
+ MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_INT5_MI2S_TX
+};
+
+static const struct soc_enum slim0_rx_vi_fb_lch_mux_enum =
+ SOC_VALUE_ENUM_DOUBLE(SND_SOC_NOPM, MSM_BACKEND_DAI_SLIMBUS_0_RX, 0, 0,
+ ARRAY_SIZE(slim0_rx_vi_fb_tx_lch_mux_text),
+ slim0_rx_vi_fb_tx_lch_mux_text, slim0_rx_vi_fb_tx_lch_value);
+
+static const struct soc_enum slim0_rx_vi_fb_rch_mux_enum =
+ SOC_VALUE_ENUM_DOUBLE(SND_SOC_NOPM, MSM_BACKEND_DAI_SLIMBUS_0_RX, 0, 0,
+ ARRAY_SIZE(slim0_rx_vi_fb_tx_rch_mux_text),
+ slim0_rx_vi_fb_tx_rch_mux_text, slim0_rx_vi_fb_tx_rch_value);
+
+static const struct soc_enum mi2s_rx_vi_fb_mux_enum =
+ SOC_VALUE_ENUM_DOUBLE(SND_SOC_NOPM, MSM_BACKEND_DAI_PRI_MI2S_RX, 0, 0,
+ ARRAY_SIZE(mi2s_rx_vi_fb_tx_mux_text),
+ mi2s_rx_vi_fb_tx_mux_text, mi2s_rx_vi_fb_tx_value);
+
+static const struct soc_enum int4_mi2s_rx_vi_fb_mono_ch_mux_enum =
+ SOC_VALUE_ENUM_DOUBLE(SND_SOC_NOPM, MSM_BACKEND_DAI_INT4_MI2S_RX, 0, 0,
+ ARRAY_SIZE(int4_mi2s_rx_vi_fb_tx_mono_mux_text),
+ int4_mi2s_rx_vi_fb_tx_mono_mux_text,
+ int4_mi2s_rx_vi_fb_tx_mono_ch_value);
+
+static const struct soc_enum int4_mi2s_rx_vi_fb_stereo_ch_mux_enum =
+ SOC_VALUE_ENUM_DOUBLE(SND_SOC_NOPM, MSM_BACKEND_DAI_INT4_MI2S_RX, 0, 0,
+ ARRAY_SIZE(int4_mi2s_rx_vi_fb_tx_stereo_mux_text),
+ int4_mi2s_rx_vi_fb_tx_stereo_mux_text,
+ int4_mi2s_rx_vi_fb_tx_stereo_ch_value);
+
+static const struct snd_kcontrol_new slim0_rx_vi_fb_lch_mux =
+ SOC_DAPM_ENUM_EXT("SLIM0_RX_VI_FB_LCH_MUX",
+ slim0_rx_vi_fb_lch_mux_enum, spkr_prot_get_vi_lch_port,
+ spkr_prot_put_vi_lch_port);
+
+static const struct snd_kcontrol_new slim0_rx_vi_fb_rch_mux =
+ SOC_DAPM_ENUM_EXT("SLIM0_RX_VI_FB_RCH_MUX",
+ slim0_rx_vi_fb_rch_mux_enum, spkr_prot_get_vi_rch_port,
+ spkr_prot_put_vi_rch_port);
+
+static const struct snd_kcontrol_new mi2s_rx_vi_fb_mux =
+ SOC_DAPM_ENUM_EXT("PRI_MI2S_RX_VI_FB_MUX",
+ mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port,
+ spkr_prot_put_vi_lch_port);
+
+static const struct snd_kcontrol_new int4_mi2s_rx_vi_fb_mono_ch_mux =
+ SOC_DAPM_ENUM_EXT("INT4_MI2S_RX_VI_FB_MONO_CH_MUX",
+ int4_mi2s_rx_vi_fb_mono_ch_mux_enum, spkr_prot_get_vi_lch_port,
+ spkr_prot_put_vi_lch_port);
+
+static const struct snd_kcontrol_new int4_mi2s_rx_vi_fb_stereo_ch_mux =
+ SOC_DAPM_ENUM_EXT("INT4_MI2S_RX_VI_FB_STEREO_CH_MUX",
+ int4_mi2s_rx_vi_fb_stereo_ch_mux_enum, spkr_prot_get_vi_rch_port,
+ spkr_prot_put_vi_rch_port);
+
+static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
+ /* Frontend AIF */
+ /* Widget name equals to Front-End DAI name<Need confirmation>,
+ * Stream name must contains substring of front-end dai name
+ */
+ SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL9", "MultiMedia9 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL10", "MultiMedia10 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL11", "MultiMedia11 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL12", "MultiMedia12 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL13", "MultiMedia13 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL14", "MultiMedia14 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL15", "MultiMedia15 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL16", "MultiMedia16 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL20", "MultiMedia20 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL21", "MultiMedia21 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL22", "MultiMedia22 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL23", "MultiMedia23 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL24", "MultiMedia24 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL25", "MultiMedia25 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL26", "MultiMedia26 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL30", "MultiMedia30 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL31", "MultiMedia31 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL32", "MultiMedia32 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL33", "MultiMedia33 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL3", "MultiMedia3 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL9", "MultiMedia9 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL16", "MultiMedia16 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL17", "MultiMedia17 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL18", "MultiMedia18 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL19", "MultiMedia19 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL20", "MultiMedia20 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL21", "MultiMedia21 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL27", "MultiMedia27 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL30", "MultiMedia30 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL31", "MultiMedia31 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL32", "MultiMedia32 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL33", "MultiMedia33 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOICE2_DL", "Voice2 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOICE2_UL", "Voice2 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VoLTE_DL", "VoLTE Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VoLTE_UL", "VoLTE Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VoWLAN_DL", "VoWLAN Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VoWLAN_UL", "VoWLAN Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOICEMMODE1_DL",
+ "VoiceMMode1 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOICEMMODE1_UL",
+ "VoiceMMode1 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOICEMMODE2_DL",
+ "VoiceMMode2 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOICEMMODE2_UL",
+ "VoiceMMode2 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("CPE_LSM_UL_HL", "CPE LSM capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIM1_DL_HL", "SLIMBUS1_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIM1_UL_HL", "SLIMBUS1_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIM3_DL_HL", "SLIMBUS3_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIM3_UL_HL", "SLIMBUS3_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIM4_DL_HL", "SLIMBUS4_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIM4_UL_HL", "SLIMBUS4_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIM6_DL_HL", "SLIMBUS6_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIM6_UL_HL", "SLIMBUS6_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIM7_DL_HL", "SLIMBUS7_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIM7_UL_HL", "SLIMBUS7_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIM8_DL_HL", "SLIMBUS8_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIM8_UL_HL", "SLIMBUS8_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INTHFP_DL_HL", "INT_HFP_BT_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INTHFP_UL_HL", "INT_HFP_BT_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("USBAUDIO_DL_HL", "USBAUDIO_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("USBAUDIO_UL_HL", "USBAUDIO_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("HDMI_DL_HL", "HDMI_HOSTLESS Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_I2S_DL_HL", "SEC_I2S_RX_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INT0_MI2S_DL_HL",
+ "INT0 MI2S_RX Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INT4_MI2S_DL_HL",
+ "INT4 MI2S_RX Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_MI2S_DL_HL",
+ "Primary MI2S_RX Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_MI2S_DL_HL",
+ "Secondary MI2S_RX Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_MI2S_DL_HL",
+ "Tertiary MI2S_RX Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_MI2S_DL_HL",
+ "Quaternary MI2S_RX Hostless Playback",
+ 0, 0, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("AUXPCM_DL_HL", "AUXPCM_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AUXPCM_UL_HL", "AUXPCM_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MI2S_UL_HL", "MI2S_TX_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT3_MI2S_UL_HL",
+ "INT3 MI2S_TX Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_MI2S_UL_HL",
+ "Tertiary MI2S_TX Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_MI2S_UL_HL",
+ "Secondary MI2S_TX Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_MI2S_UL_HL",
+ "Primary MI2S_TX Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MI2S_DL_HL", "MI2S_RX_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DTMF_DL_HL", "DTMF_RX_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_UL_HL",
+ "Quaternary MI2S_TX Hostless Capture",
+ 0, 0, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_0_DL_HL",
+ "Primary TDM0 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_0_UL_HL",
+ "Primary TDM0 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_1_DL_HL",
+ "Primary TDM1 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_1_UL_HL",
+ "Primary TDM1 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_2_DL_HL",
+ "Primary TDM2 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_2_UL_HL",
+ "Primary TDM2 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_3_DL_HL",
+ "Primary TDM3 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_3_UL_HL",
+ "Primary TDM3 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_4_DL_HL",
+ "Primary TDM4 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_4_UL_HL",
+ "Primary TDM4 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_5_DL_HL",
+ "Primary TDM5 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_5_UL_HL",
+ "Primary TDM5 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_6_DL_HL",
+ "Primary TDM6 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_6_UL_HL",
+ "Primary TDM6 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_7_DL_HL",
+ "Primary TDM7 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_7_UL_HL",
+ "Primary TDM7 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_0_DL_HL",
+ "Secondary TDM0 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_0_UL_HL",
+ "Secondary TDM0 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_1_DL_HL",
+ "Secondary TDM1 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_1_UL_HL",
+ "Secondary TDM1 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_2_DL_HL",
+ "Secondary TDM2 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_2_UL_HL",
+ "Secondary TDM2 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_3_DL_HL",
+ "Secondary TDM3 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_3_UL_HL",
+ "Secondary TDM3 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_4_DL_HL",
+ "Secondary TDM4 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_4_UL_HL",
+ "Secondary TDM4 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_5_DL_HL",
+ "Secondary TDM5 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_5_UL_HL",
+ "Secondary TDM5 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_6_DL_HL",
+ "Secondary TDM6 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_6_UL_HL",
+ "Secondary TDM6 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_7_DL_HL",
+ "Secondary TDM7 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_7_UL_HL",
+ "Secondary TDM7 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_0_DL_HL",
+ "Tertiary TDM0 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_0_UL_HL",
+ "Tertiary TDM0 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_1_DL_HL",
+ "Tertiary TDM1 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_1_UL_HL",
+ "Tertiary TDM1 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_2_DL_HL",
+ "Tertiary TDM2 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_2_UL_HL",
+ "Tertiary TDM2 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_3_DL_HL",
+ "Tertiary TDM3 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_3_UL_HL",
+ "Tertiary TDM3 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_4_DL_HL",
+ "Tertiary TDM4 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_4_UL_HL",
+ "Tertiary TDM4 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_5_DL_HL",
+ "Tertiary TDM5 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_5_UL_HL",
+ "Tertiary TDM5 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_6_DL_HL",
+ "Tertiary TDM6 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_6_UL_HL",
+ "Tertiary TDM6 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_7_DL_HL",
+ "Tertiary TDM7 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_7_UL_HL",
+ "Tertiary TDM7 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_0_DL_HL",
+ "Quaternary TDM0 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_0_UL_HL",
+ "Quaternary TDM0 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_1_DL_HL",
+ "Quaternary TDM1 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_1_UL_HL",
+ "Quaternary TDM1 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_2_DL_HL",
+ "Quaternary TDM2 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_2_UL_HL",
+ "Quaternary TDM2 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_3_DL_HL",
+ "Quaternary TDM3 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_3_UL_HL",
+ "Quaternary TDM3 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_4_DL_HL",
+ "Quaternary TDM4 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_4_UL_HL",
+ "Quaternary TDM4 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_5_DL_HL",
+ "Quaternary TDM5 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_5_UL_HL",
+ "Quaternary TDM5 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_6_DL_HL",
+ "Quaternary TDM6 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_6_UL_HL",
+ "Quaternary TDM6 Hostless Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_7_DL_HL",
+ "Quaternary TDM7 Hostless Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_7_UL_HL",
+ "Quaternary TDM7 Hostless Capture",
+ 0, 0, 0, 0),
+
+ /* LSM */
+ SND_SOC_DAPM_AIF_OUT("LSM1_UL_HL", "Listen 1 Audio Service Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("LSM2_UL_HL", "Listen 2 Audio Service Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("LSM3_UL_HL", "Listen 3 Audio Service Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("LSM4_UL_HL", "Listen 4 Audio Service Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("LSM5_UL_HL", "Listen 5 Audio Service Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("LSM6_UL_HL", "Listen 6 Audio Service Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("LSM7_UL_HL", "Listen 7 Audio Service Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("LSM8_UL_HL", "Listen 8 Audio Service Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QCHAT_DL", "QCHAT Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QCHAT_UL", "QCHAT Capture", 0, 0, 0, 0),
+ /* Backend AIF */
+ /* Stream name equals to backend dai link stream name
+ */
+ SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_I2S_RX", "Secondary I2S Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_OUT("SPDIF_RX", "SPDIF Playback", 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_2_RX", "Slimbus2 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_RX", "Slimbus5 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT", "Display Port Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX", "Tertiary MI2S Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX", "Secondary MI2S Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX_SD1",
+ "Secondary MI2S Playback SD1",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_MI2S_RX", "Primary MI2S Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT0_MI2S_RX", "INT0 MI2S Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT2_MI2S_RX", "INT2 MI2S Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT3_MI2S_RX", "INT3 MI2S Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT5_MI2S_RX", "INT5 MI2S Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT4_MI2S_RX", "INT4 MI2S Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT4_MI2S_TX", "INT4 MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUIN_MI2S_RX", "Quinary MI2S Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MI2S_TX", "MI2S Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_MI2S_TX", "Primary MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX", "Tertiary MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INT0_MI2S_TX", "INT0 MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INT2_MI2S_TX", "INT2 MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INT3_MI2S_TX", "INT3 MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX", "Secondary MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_2_TX", "Slimbus2 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUIN_MI2S_TX", "Quinary MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SENARY_MI2S_TX", "Senary MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT_BT_A2DP_RX", "Internal BT-A2DP Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PCM_RX", "AFE Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("PCM_TX", "AFE Capture",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX_1", "Secondary MI2S1 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX_2", "Secondary MI2S2 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX_3", "Secondary MI2S3 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX_4", "Secondary MI2S4 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX_1", "Tertiary MI2S1 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX_2", "Tertiary MI2S2 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX_3", "Tertiary MI2S3 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX_4", "Tertiary MI2S4 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX_1", "Quaternary MI2S1 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX_2", "Quaternary MI2S2 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX_3", "Quaternary MI2S3 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX_4", "Quaternary MI2S4 Capture",
+ 0, 0, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX_1", "Secondary MI2S1 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX_2", "Secondary MI2S2 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX_3", "Secondary MI2S3 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX_4", "Secondary MI2S4 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX_1", "Tertiary MI2S1 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX_2", "Tertiary MI2S2 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX_3", "Tertiary MI2S3 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX_4", "Tertiary MI2S4 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX_1", "Quaternary MI2S1 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX_2", "Quaternary MI2S2 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX_3", "Quaternary MI2S3 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX_4", "Quaternary MI2S4 Playback",
+ 0, 0, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_0", "Primary TDM0 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_0", "Primary TDM0 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_1", "Primary TDM1 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_1", "Primary TDM1 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_2", "Primary TDM2 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_2", "Primary TDM2 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_3", "Primary TDM3 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_3", "Primary TDM3 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_4", "Primary TDM4 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_4", "Primary TDM4 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_5", "Primary TDM5 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_5", "Primary TDM5 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_6", "Primary TDM6 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_6", "Primary TDM6 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_7", "Primary TDM7 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_7", "Primary TDM7 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_0", "Secondary TDM0 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_0", "Secondary TDM0 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_1", "Secondary TDM1 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_1", "Secondary TDM1 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_2", "Secondary TDM2 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_2", "Secondary TDM2 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_3", "Secondary TDM3 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_3", "Secondary TDM3 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_4", "Secondary TDM4 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_4", "Secondary TDM4 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_5", "Secondary TDM5 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_5", "Secondary TDM5 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_6", "Secondary TDM6 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_6", "Secondary TDM6 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_7", "Secondary TDM7 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_7", "Secondary TDM7 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_0", "Tertiary TDM0 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_0", "Tertiary TDM0 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_1", "Tertiary TDM1 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_1", "Tertiary TDM1 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_2", "Tertiary TDM2 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_2", "Tertiary TDM2 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_3", "Tertiary TDM3 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_3", "Tertiary TDM3 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_4", "Tertiary TDM4 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_4", "Tertiary TDM4 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_5", "Tertiary TDM5 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_5", "Tertiary TDM5 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_6", "Tertiary TDM6 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_6", "Tertiary TDM6 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_7", "Tertiary TDM7 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_7", "Tertiary TDM7 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_0", "Quaternary TDM0 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_0", "Quaternary TDM0 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_1", "Quaternary TDM1 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_1", "Quaternary TDM1 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_2", "Quaternary TDM2 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_2", "Quaternary TDM2 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_3", "Quaternary TDM3 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_3", "Quaternary TDM3 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_4", "Quaternary TDM4 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_4", "Quaternary TDM4 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_5", "Quaternary TDM5 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_5", "Quaternary TDM5 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_6", "Quaternary TDM6 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_6", "Quaternary TDM6 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_7", "Quaternary TDM7 Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_7", "Quaternary TDM7 Capture",
+ 0, 0, 0, 0),
+ /* incall */
+ SND_SOC_DAPM_AIF_OUT("VOICE_PLAYBACK_TX", "Voice Farend Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_OUT("VOICE2_PLAYBACK_TX", "Voice2 Farend Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_RX", "Slimbus4 Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INCALL_RECORD_TX", "Voice Uplink Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INCALL_RECORD_RX", "Voice Downlink Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_4_TX", "Slimbus4 Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SENARY_TX", "Senary_mi2s Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INT5_MI2S_TX", "INT5 MI2S Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_5_TX", "Slimbus5 Capture", 0, 0, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AUX_PCM_RX", "AUX PCM Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AUX_PCM_TX", "AUX PCM Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_AUX_PCM_RX", "Sec AUX PCM Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SEC_AUX_PCM_TX", "Sec AUX PCM Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_AUX_PCM_RX", "Tert AUX PCM Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_AUX_PCM_TX", "Tert AUX PCM Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_AUX_PCM_RX", "Quat AUX PCM Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_AUX_PCM_TX", "Quat AUX PCM Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOICE_STUB_DL", "VOICE_STUB Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOICE_STUB_UL", "VOICE_STUB Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOICE2_STUB_DL", "VOICE2_STUB Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOICE2_STUB_UL", "VOICE2_STUB Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOLTE_STUB_DL", "VOLTE_STUB Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOLTE_STUB_UL", "VOLTE_STUB Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("STUB_RX", "Stub Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("STUB_TX", "Stub Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_1_RX", "Slimbus1 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("STUB_1_TX", "Stub1 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_3_RX", "Slimbus3 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_3_TX", "Slimbus3 Capture", 0, 0, 0, 0),
+ /* In- call recording */
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_RX", "Slimbus6 Playback", 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_6_TX", "Slimbus6 Capture", 0, 0, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_7_RX", "Slimbus7 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_7_TX", "Slimbus7 Capture", 0, 0, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_8_RX", "Slimbus8 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_8_TX", "Slimbus8 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("USB_AUDIO_RX", "USB Audio Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("USB_AUDIO_TX", "USB Audio Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PROXY_RX", "Proxy Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PROXY_TX", "Proxy Capture", 0, 0, 0, 0),
+
+ /* Switch Definitions */
+ SND_SOC_DAPM_SWITCH("SLIMBUS_DL_HL", SND_SOC_NOPM, 0, 0,
+ &slim_fm_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("SLIMBUS1_DL_HL", SND_SOC_NOPM, 0, 0,
+ &slim1_fm_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("SLIMBUS3_DL_HL", SND_SOC_NOPM, 0, 0,
+ &slim3_fm_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("SLIMBUS4_DL_HL", SND_SOC_NOPM, 0, 0,
+ &slim4_fm_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("SLIMBUS6_DL_HL", SND_SOC_NOPM, 0, 0,
+ &slim6_fm_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("PCM_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+ &pcm_rx_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("INT0_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+ &int0_mi2s_rx_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("INT4_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+ &int4_mi2s_rx_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("PRI_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+ &pri_mi2s_rx_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("SEC_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+ &sec_mi2s_rx_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("TERT_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+ &tert_mi2s_rx_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("QUAT_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+ &quat_mi2s_rx_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("HFP_PRI_AUX_UL_HL", SND_SOC_NOPM, 0, 0,
+ &hfp_pri_aux_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("HFP_AUX_UL_HL", SND_SOC_NOPM, 0, 0,
+ &hfp_aux_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("HFP_INT_UL_HL", SND_SOC_NOPM, 0, 0,
+ &hfp_int_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("HFP_SLIM7_UL_HL", SND_SOC_NOPM, 0, 0,
+ &hfp_slim7_switch_mixer_controls),
+ SND_SOC_DAPM_SWITCH("USB_DL_HL", SND_SOC_NOPM, 0, 0,
+ &usb_switch_mixer_controls),
+
+ /* Mixer definitions */
+ SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_i2s_rx_mixer_controls, ARRAY_SIZE(sec_i2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_2_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_2_rx_mixer_controls, ARRAY_SIZE(slimbus_2_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_5_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_5_rx_mixer_controls, ARRAY_SIZE(slimbus_5_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_7_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_7_rx_mixer_controls, ARRAY_SIZE(slimbus_7_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
+ hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DISPLAY_PORT Mixer", SND_SOC_NOPM, 0, 0,
+ display_port_mixer_controls, ARRAY_SIZE(display_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SPDIF_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ spdif_rx_mixer_controls, ARRAY_SIZE(spdif_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ mi2s_rx_mixer_controls, ARRAY_SIZE(mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quaternary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(quaternary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tertiary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(tertiary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_MI2S_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ secondary_mi2s_rx_1_mixer_controls,
+ ARRAY_SIZE(secondary_mi2s_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_MI2S_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ secondary_mi2s_rx_2_mixer_controls,
+ ARRAY_SIZE(secondary_mi2s_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_MI2S_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ secondary_mi2s_rx_3_mixer_controls,
+ ARRAY_SIZE(secondary_mi2s_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_MI2S_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ secondary_mi2s_rx_4_mixer_controls,
+ ARRAY_SIZE(secondary_mi2s_rx_4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_MI2S_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tertiary_mi2s_rx_1_mixer_controls,
+ ARRAY_SIZE(tertiary_mi2s_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_MI2S_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tertiary_mi2s_rx_2_mixer_controls,
+ ARRAY_SIZE(tertiary_mi2s_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_MI2S_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tertiary_mi2s_rx_3_mixer_controls,
+ ARRAY_SIZE(tertiary_mi2s_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_MI2S_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tertiary_mi2s_rx_4_mixer_controls,
+ ARRAY_SIZE(tertiary_mi2s_rx_4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_MI2S_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quaternary_mi2s_rx_1_mixer_controls,
+ ARRAY_SIZE(quaternary_mi2s_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_MI2S_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quaternary_mi2s_rx_2_mixer_controls,
+ ARRAY_SIZE(quaternary_mi2s_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_MI2S_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quaternary_mi2s_rx_3_mixer_controls,
+ ARRAY_SIZE(quaternary_mi2s_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_MI2S_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quaternary_mi2s_rx_4_mixer_controls,
+ ARRAY_SIZE(quaternary_mi2s_rx_4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ secondary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(secondary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_MI2S_RX_SD1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ secondary_mi2s_rx2_mixer_controls,
+ ARRAY_SIZE(secondary_mi2s_rx2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ primary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(primary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INT0_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int0_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(int0_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INT4_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int4_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(int4_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUIN_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quinary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(quinary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_0_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_1_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_2_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_3_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_tx_0_mixer_controls,
+ ARRAY_SIZE(pri_tdm_tx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_TX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_tx_1_mixer_controls,
+ ARRAY_SIZE(pri_tdm_tx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_TX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_tx_2_mixer_controls,
+ ARRAY_SIZE(pri_tdm_tx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_TX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_tx_3_mixer_controls,
+ ARRAY_SIZE(pri_tdm_tx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_0_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_1_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_2_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_3_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_tx_0_mixer_controls,
+ ARRAY_SIZE(sec_tdm_tx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_0_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_tx_0_mixer_controls,
+ ARRAY_SIZE(tert_tdm_tx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_TX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_tx_1_mixer_controls,
+ ARRAY_SIZE(tert_tdm_tx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_TX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_tx_2_mixer_controls,
+ ARRAY_SIZE(tert_tdm_tx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_TX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_tx_3_mixer_controls,
+ ARRAY_SIZE(tert_tdm_tx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_1_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_2_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_3_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_4_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_0_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_tx_0_mixer_controls,
+ ARRAY_SIZE(quat_tdm_tx_0_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_1_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_2_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_3_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia3 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul3_mixer_controls, ARRAY_SIZE(mmul3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia4 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul4_mixer_controls, ARRAY_SIZE(mmul4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia5 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul5_mixer_controls, ARRAY_SIZE(mmul5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia6 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul6_mixer_controls, ARRAY_SIZE(mmul6_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia8 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia9 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul9_mixer_controls, ARRAY_SIZE(mmul9_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia16 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul16_mixer_controls, ARRAY_SIZE(mmul16_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia17 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul17_mixer_controls, ARRAY_SIZE(mmul17_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia18 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul18_mixer_controls, ARRAY_SIZE(mmul18_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia19 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul19_mixer_controls, ARRAY_SIZE(mmul19_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia20 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul20_mixer_controls, ARRAY_SIZE(mmul20_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia21 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul21_mixer_controls, ARRAY_SIZE(mmul21_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia27 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul27_mixer_controls, ARRAY_SIZE(mmul27_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia28 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul28_mixer_controls, ARRAY_SIZE(mmul28_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia29 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul29_mixer_controls, ARRAY_SIZE(mmul29_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia30 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul30_mixer_controls, ARRAY_SIZE(mmul30_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia31 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul31_mixer_controls, ARRAY_SIZE(mmul31_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia32 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul32_mixer_controls, ARRAY_SIZE(mmul32_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia33 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul33_mixer_controls, ARRAY_SIZE(mmul33_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_auxpcm_rx_mixer_controls, ARRAY_SIZE(sec_auxpcm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_auxpcm_rx_mixer_controls,
+ ARRAY_SIZE(tert_auxpcm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_auxpcm_rx_mixer_controls,
+ ARRAY_SIZE(quat_auxpcm_rx_mixer_controls)),
+ /* incall */
+ SND_SOC_DAPM_MIXER("Incall_Music Audio Mixer", SND_SOC_NOPM, 0, 0,
+ incall_music_delivery_mixer_controls,
+ ARRAY_SIZE(incall_music_delivery_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Incall_Music_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+ incall_music2_delivery_mixer_controls,
+ ARRAY_SIZE(incall_music2_delivery_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_4_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_4_rx_mixer_controls,
+ ARRAY_SIZE(slimbus_4_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_6_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_6_rx_mixer_controls,
+ ARRAY_SIZE(slimbus_6_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("USB_AUDIO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ usb_audio_rx_mixer_controls,
+ ARRAY_SIZE(usb_audio_rx_mixer_controls)),
+ /* Voice Mixer */
+ SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
+ ARRAY_SIZE(pri_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ sec_i2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(sec_i2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_MI2S_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ sec_mi2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(sec_mi2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ slimbus_rx_voice_mixer_controls,
+ ARRAY_SIZE(slimbus_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ bt_sco_rx_voice_mixer_controls,
+ ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AFE_PCM_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ afe_pcm_rx_voice_mixer_controls,
+ ARRAY_SIZE(afe_pcm_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AUX_PCM_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ aux_pcm_rx_voice_mixer_controls,
+ ARRAY_SIZE(aux_pcm_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ sec_aux_pcm_rx_voice_mixer_controls,
+ ARRAY_SIZE(sec_aux_pcm_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_AUX_PCM_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ tert_aux_pcm_rx_voice_mixer_controls,
+ ARRAY_SIZE(tert_aux_pcm_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_AUX_PCM_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ quat_aux_pcm_rx_voice_mixer_controls,
+ ARRAY_SIZE(quat_aux_pcm_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HDMI_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ hdmi_rx_voice_mixer_controls,
+ ARRAY_SIZE(hdmi_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MI2S_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ mi2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(mi2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_MI2S_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ pri_mi2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(pri_mi2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INT0_MI2S_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ int0_mi2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(int0_mi2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INT4_MI2S_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ int4_mi2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(int4_mi2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_MI2S_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ tert_mi2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(tert_mi2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_MI2S_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ quat_mi2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(quat_mi2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUIN_MI2S_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ quin_mi2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(quin_mi2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_2_voice_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_2_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
+ ARRAY_SIZE(tx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voice2_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voice2_mixer_controls,
+ ARRAY_SIZE(tx_voice2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PROXY_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ proxy_rx_voice_mixer_controls,
+ ARRAY_SIZE(proxy_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voip_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls,
+ ARRAY_SIZE(tx_voip_mixer_controls)),
+ SND_SOC_DAPM_MIXER("VoLTE_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_volte_mixer_controls,
+ ARRAY_SIZE(tx_volte_mixer_controls)),
+ SND_SOC_DAPM_MIXER("VoWLAN_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_vowlan_mixer_controls,
+ ARRAY_SIZE(tx_vowlan_mixer_controls)),
+ SND_SOC_DAPM_MIXER("VoiceMMode1_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voicemmode1_mixer_controls,
+ ARRAY_SIZE(tx_voicemmode1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("VoiceMMode2_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voicemmode2_mixer_controls,
+ ARRAY_SIZE(tx_voicemmode2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_A2DP_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int_bt_a2dp_rx_mixer_controls,
+ ARRAY_SIZE(int_bt_a2dp_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AFE_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ afe_pcm_rx_mixer_controls, ARRAY_SIZE(afe_pcm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voice Stub Tx Mixer", SND_SOC_NOPM, 0, 0,
+ tx_voice_stub_mixer_controls, ARRAY_SIZE(tx_voice_stub_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voice2 Stub Tx Mixer", SND_SOC_NOPM, 0, 0,
+ tx_voice2_stub_mixer_controls,
+ ARRAY_SIZE(tx_voice2_stub_mixer_controls)),
+ SND_SOC_DAPM_MIXER("VoLTE Stub Tx Mixer", SND_SOC_NOPM, 0, 0,
+ tx_volte_stub_mixer_controls, ARRAY_SIZE(tx_volte_stub_mixer_controls)),
+ SND_SOC_DAPM_MIXER("STUB_RX Mixer", SND_SOC_NOPM, 0, 0,
+ stub_rx_mixer_controls, ARRAY_SIZE(stub_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_1_rx_mixer_controls, ARRAY_SIZE(slimbus_1_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_3_RX_Voice Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_3_rx_mixer_controls, ARRAY_SIZE(slimbus_3_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIM_6_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ slimbus_6_rx_voice_mixer_controls,
+ ARRAY_SIZE(slimbus_6_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIM_7_RX_Voice Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_7_rx_voice_mixer_controls,
+ ARRAY_SIZE(slimbus_7_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIM_8_RX_Voice Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_8_rx_voice_mixer_controls,
+ ARRAY_SIZE(slimbus_8_rx_voice_mixer_controls)),
+ /* port mixer */
+ SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls,
+ ARRAY_SIZE(sbus_0_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AUX_PCM_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, aux_pcm_rx_port_mixer_controls,
+ ARRAY_SIZE(aux_pcm_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_AUXPCM_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, sec_auxpcm_rx_port_mixer_controls,
+ ARRAY_SIZE(sec_auxpcm_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_AUXPCM_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, tert_auxpcm_rx_port_mixer_controls,
+ ARRAY_SIZE(tert_auxpcm_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_AUXPCM_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, quat_auxpcm_rx_port_mixer_controls,
+ ARRAY_SIZE(quat_auxpcm_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ sbus_1_rx_port_mixer_controls,
+ ARRAY_SIZE(sbus_1_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ bt_sco_rx_port_mixer_controls,
+ ARRAY_SIZE(bt_sco_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AFE_PCM_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, afe_pcm_rx_port_mixer_controls,
+ ARRAY_SIZE(afe_pcm_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HDMI_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, hdmi_rx_port_mixer_controls,
+ ARRAY_SIZE(hdmi_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, display_port_rx_port_mixer_controls,
+ ARRAY_SIZE(display_port_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_I2S_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, sec_i2s_rx_port_mixer_controls,
+ ARRAY_SIZE(sec_i2s_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_3_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, sbus_3_rx_port_mixer_controls,
+ ARRAY_SIZE(sbus_3_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_6_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, sbus_6_rx_port_mixer_controls,
+ ARRAY_SIZE(sbus_6_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ mi2s_rx_port_mixer_controls, ARRAY_SIZE(mi2s_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ primary_mi2s_rx_port_mixer_controls,
+ ARRAY_SIZE(primary_mi2s_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ sec_mi2s_rx_port_mixer_controls,
+ ARRAY_SIZE(sec_mi2s_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ tert_mi2s_rx_port_mixer_controls,
+ ARRAY_SIZE(tert_mi2s_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ quat_mi2s_rx_port_mixer_controls,
+ ARRAY_SIZE(quat_mi2s_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_0_port_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_0_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_1_port_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_1_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_2_port_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_2_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("PRI_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0,
+ pri_tdm_rx_3_port_mixer_controls,
+ ARRAY_SIZE(pri_tdm_rx_3_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_0_port_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_0_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_1_port_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_1_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_2_port_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_2_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0,
+ sec_tdm_rx_3_port_mixer_controls,
+ ARRAY_SIZE(sec_tdm_rx_3_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_0_port_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_0_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_1_port_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_1_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_2_port_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_2_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0,
+ tert_tdm_rx_3_port_mixer_controls,
+ ARRAY_SIZE(tert_tdm_rx_3_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_0_port_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_0_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_1_port_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_1_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_2_port_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_2_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_3_port_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_3_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INT0_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ int0_mi2s_rx_port_mixer_controls,
+ ARRAY_SIZE(int0_mi2s_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INT4_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ int4_mi2s_rx_port_mixer_controls,
+ ARRAY_SIZE(int4_mi2s_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QCHAT_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_qchat_mixer_controls,
+ ARRAY_SIZE(tx_qchat_mixer_controls)),
+ SND_SOC_DAPM_MIXER("USB_AUDIO_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0, usb_audio_rx_voice_mixer_controls,
+ ARRAY_SIZE(usb_audio_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("USB_AUDIO_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, usb_rx_port_mixer_controls,
+ ARRAY_SIZE(usb_rx_port_mixer_controls)),
+ /* lsm mixer definitions */
+ SND_SOC_DAPM_MIXER("LSM1 Mixer", SND_SOC_NOPM, 0, 0,
+ lsm1_mixer_controls, ARRAY_SIZE(lsm1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("LSM2 Mixer", SND_SOC_NOPM, 0, 0,
+ lsm2_mixer_controls, ARRAY_SIZE(lsm2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("LSM3 Mixer", SND_SOC_NOPM, 0, 0,
+ lsm3_mixer_controls, ARRAY_SIZE(lsm3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("LSM4 Mixer", SND_SOC_NOPM, 0, 0,
+ lsm4_mixer_controls, ARRAY_SIZE(lsm4_mixer_controls)),
+ SND_SOC_DAPM_MIXER("LSM5 Mixer", SND_SOC_NOPM, 0, 0,
+ lsm5_mixer_controls, ARRAY_SIZE(lsm5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("LSM6 Mixer", SND_SOC_NOPM, 0, 0,
+ lsm6_mixer_controls, ARRAY_SIZE(lsm6_mixer_controls)),
+ SND_SOC_DAPM_MIXER("LSM7 Mixer", SND_SOC_NOPM, 0, 0,
+ lsm7_mixer_controls, ARRAY_SIZE(lsm7_mixer_controls)),
+ SND_SOC_DAPM_MIXER("LSM8 Mixer", SND_SOC_NOPM, 0, 0,
+ lsm8_mixer_controls, ARRAY_SIZE(lsm8_mixer_controls)),
+ /* Virtual Pins to force backends ON atm */
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ SND_SOC_DAPM_INPUT("BE_IN"),
+
+ SND_SOC_DAPM_MUX("SLIM0_RX_VI_FB_LCH_MUX", SND_SOC_NOPM, 0, 0,
+ &slim0_rx_vi_fb_lch_mux),
+ SND_SOC_DAPM_MUX("SLIM0_RX_VI_FB_RCH_MUX", SND_SOC_NOPM, 0, 0,
+ &slim0_rx_vi_fb_rch_mux),
+ SND_SOC_DAPM_MUX("PRI_MI2S_RX_VI_FB_MUX", SND_SOC_NOPM, 0, 0,
+ &mi2s_rx_vi_fb_mux),
+ SND_SOC_DAPM_MUX("INT4_MI2S_RX_VI_FB_MONO_CH_MUX", SND_SOC_NOPM, 0, 0,
+ &int4_mi2s_rx_vi_fb_mono_ch_mux),
+ SND_SOC_DAPM_MUX("INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", SND_SOC_NOPM, 0, 0,
+ &int4_mi2s_rx_vi_fb_stereo_ch_mux),
+
+ SND_SOC_DAPM_MUX("VOC_EXT_EC MUX", SND_SOC_NOPM, 0, 0,
+ &voc_ext_ec_mux),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL1 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul1),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL2 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul2),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL3 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul3),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL4 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul4),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL5 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul5),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL6 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul6),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL8 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul8),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL9 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul9),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL16 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul16),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL17 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul17),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL18 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul18),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL19 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul19),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"PRI_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"PRI_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"PRI_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"PRI_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"PRI_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"PRI_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"PRI_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"PRI_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"PRI_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"PRI_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"PRI_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"PRI_RX Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"PRI_RX Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"PRI_RX Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"PRI_RX Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
+
+ {"SEC_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SEC_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SEC_I2S_RX", NULL, "SEC_RX Audio Mixer"},
+
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia23", "MM_DL23"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia24", "MM_DL24"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia25", "MM_DL25"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
+
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SLIMBUS_2_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SLIMBUS_2_RX", NULL, "SLIMBUS_2_RX Audio Mixer"},
+
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SLIMBUS_5_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SLIMBUS_5_RX", NULL, "SLIMBUS_5_RX Audio Mixer"},
+
+ {"HDMI Mixer", "MultiMedia1", "MM_DL1"},
+ {"HDMI Mixer", "MultiMedia2", "MM_DL2"},
+ {"HDMI Mixer", "MultiMedia3", "MM_DL3"},
+ {"HDMI Mixer", "MultiMedia4", "MM_DL4"},
+ {"HDMI Mixer", "MultiMedia5", "MM_DL5"},
+ {"HDMI Mixer", "MultiMedia6", "MM_DL6"},
+ {"HDMI Mixer", "MultiMedia7", "MM_DL7"},
+ {"HDMI Mixer", "MultiMedia8", "MM_DL8"},
+ {"HDMI Mixer", "MultiMedia9", "MM_DL9"},
+ {"HDMI Mixer", "MultiMedia10", "MM_DL10"},
+ {"HDMI Mixer", "MultiMedia11", "MM_DL11"},
+ {"HDMI Mixer", "MultiMedia12", "MM_DL12"},
+ {"HDMI Mixer", "MultiMedia13", "MM_DL13"},
+ {"HDMI Mixer", "MultiMedia14", "MM_DL14"},
+ {"HDMI Mixer", "MultiMedia15", "MM_DL15"},
+ {"HDMI Mixer", "MultiMedia16", "MM_DL16"},
+ {"HDMI Mixer", "MultiMedia21", "MM_DL21"},
+ {"HDMI Mixer", "MultiMedia22", "MM_DL22"},
+ {"HDMI Mixer", "MultiMedia23", "MM_DL23"},
+ {"HDMI Mixer", "MultiMedia24", "MM_DL24"},
+ {"HDMI Mixer", "MultiMedia25", "MM_DL25"},
+ {"HDMI Mixer", "MultiMedia26", "MM_DL26"},
+ {"HDMI", NULL, "HDMI Mixer"},
+
+ {"DISPLAY_PORT Mixer", "MultiMedia1", "MM_DL1"},
+ {"DISPLAY_PORT Mixer", "MultiMedia2", "MM_DL2"},
+ {"DISPLAY_PORT Mixer", "MultiMedia3", "MM_DL3"},
+ {"DISPLAY_PORT Mixer", "MultiMedia4", "MM_DL4"},
+ {"DISPLAY_PORT Mixer", "MultiMedia5", "MM_DL5"},
+ {"DISPLAY_PORT Mixer", "MultiMedia6", "MM_DL6"},
+ {"DISPLAY_PORT Mixer", "MultiMedia7", "MM_DL7"},
+ {"DISPLAY_PORT Mixer", "MultiMedia8", "MM_DL8"},
+ {"DISPLAY_PORT Mixer", "MultiMedia9", "MM_DL9"},
+ {"DISPLAY_PORT Mixer", "MultiMedia10", "MM_DL10"},
+ {"DISPLAY_PORT Mixer", "MultiMedia11", "MM_DL11"},
+ {"DISPLAY_PORT Mixer", "MultiMedia12", "MM_DL12"},
+ {"DISPLAY_PORT Mixer", "MultiMedia13", "MM_DL13"},
+ {"DISPLAY_PORT Mixer", "MultiMedia14", "MM_DL14"},
+ {"DISPLAY_PORT Mixer", "MultiMedia15", "MM_DL15"},
+ {"DISPLAY_PORT Mixer", "MultiMedia16", "MM_DL16"},
+ {"DISPLAY_PORT Mixer", "MultiMedia26", "MM_DL26"},
+ {"DISPLAY_PORT", NULL, "DISPLAY_PORT Mixer"},
+
+ {"SPDIF_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SPDIF_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SPDIF_RX", NULL, "SPDIF_RX Audio Mixer"},
+
+ /* incall */
+ {"Incall_Music Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"Incall_Music Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"Incall_Music Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"Incall_Music Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"VOICE_PLAYBACK_TX", NULL, "Incall_Music Audio Mixer"},
+ {"Incall_Music_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"Incall_Music_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"Incall_Music_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"Incall_Music_2 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"VOICE2_PLAYBACK_TX", NULL, "Incall_Music_2 Audio Mixer"},
+ {"SLIMBUS_4_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SLIMBUS_4_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SLIMBUS_4_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SLIMBUS_4_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SLIMBUS_4_RX", NULL, "SLIMBUS_4_RX Audio Mixer"},
+
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia23", "MM_DL23"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia24", "MM_DL24"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia25", "MM_DL25"},
+ {"SLIMBUS_6_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SLIMBUS_6_RX", NULL, "SLIMBUS_6_RX Audio Mixer"},
+
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia23", "MM_DL23"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia24", "MM_DL24"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia25", "MM_DL25"},
+ {"SLIMBUS_7_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SLIMBUS_7_RX", NULL, "SLIMBUS_7_RX Audio Mixer"},
+
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"USB_AUDIO_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"USB_AUDIO_RX", NULL, "USB_AUDIO_RX Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+ {"MultiMedia4 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+ {"MultiMedia8 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+ {"MultiMedia9 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+ {"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+ {"MultiMedia4 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+ {"MultiMedia8 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+ {"MultiMedia9 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+ {"MultiMedia1 Mixer", "SLIM_4_TX", "SLIMBUS_4_TX"},
+ {"MultiMedia1 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"},
+ {"MultiMedia1 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+ {"MultiMedia1 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"MultiMedia8 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"},
+ {"MultiMedia8 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+ {"MultiMedia4 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia17 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia18 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia19 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia28 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia29 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia8 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia2 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia4 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia17 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia18 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia19 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia28 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia29 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia8 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia17 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"MultiMedia18 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"MultiMedia19 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"MultiMedia28 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"MultiMedia29 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"MultiMedia17 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia18 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia19 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia28 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia29 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia8 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"MultiMedia3 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia16 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia5 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+ {"MultiMedia5 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"MI2S_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"MI2S_RX Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"MI2S_RX Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"MI2S_RX Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"MI2S_RX Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
+
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"QUAT_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Audio Mixer"},
+
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Audio Mixer"},
+
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Audio Mixer"},
+
+ {"SEC_MI2S_RX_SD1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_MI2S_RX_SD1", NULL, "SEC_MI2S_RX_SD1 Audio Mixer"},
+
+ {"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"SEC_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"PRI_MI2S_RX Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Audio Mixer"},
+
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"INT0_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX Audio Mixer"},
+
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"INT4_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX Audio Mixer"},
+
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUIN_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX Audio Mixer"},
+
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Audio Mixer"},
+
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1 Audio Mixer"},
+
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2 Audio Mixer"},
+
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3 Audio Mixer"},
+
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"PRI_TDM_TX_0", NULL, "PRI_TDM_TX_0 Audio Mixer"},
+
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"PRI_TDM_TX_1 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"PRI_TDM_TX_1", NULL, "PRI_TDM_TX_1 Audio Mixer"},
+
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"PRI_TDM_TX_2 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"PRI_TDM_TX_2", NULL, "PRI_TDM_TX_2 Audio Mixer"},
+
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"PRI_TDM_TX_3 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"PRI_TDM_TX_3", NULL, "PRI_TDM_TX_3 Audio Mixer"},
+
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Audio Mixer"},
+
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1 Audio Mixer"},
+
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2 Audio Mixer"},
+
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3 Audio Mixer"},
+
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_TDM_TX_0", NULL, "SEC_TDM_TX_0 Audio Mixer"},
+
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Audio Mixer"},
+
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"TERT_TDM_TX_0", NULL, "TERT_TDM_TX_0 Audio Mixer"},
+
+
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"TERT_TDM_TX_1 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"TERT_TDM_TX_1", NULL, "TERT_TDM_TX_1 Audio Mixer"},
+
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"TERT_TDM_TX_2 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"TERT_TDM_TX_2", NULL, "TERT_TDM_TX_2 Audio Mixer"},
+
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia30", "MM_DL30"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia31", "MM_DL31"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia32", "MM_DL32"},
+ {"TERT_TDM_TX_3 Audio Mixer", "MultiMedia33", "MM_DL33"},
+ {"TERT_TDM_TX_3", NULL, "TERT_TDM_TX_3 Audio Mixer"},
+
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1 Audio Mixer"},
+
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2 Audio Mixer"},
+
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3 Audio Mixer"},
+
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"TERT_TDM_RX_4", NULL, "TERT_TDM_RX_4 Audio Mixer"},
+
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia20", "MM_DL20"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Audio Mixer"},
+
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_TDM_TX_0", NULL, "QUAT_TDM_TX_0 Audio Mixer"},
+
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia20", "MM_DL20"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Audio Mixer"},
+
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia20", "MM_DL20"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Audio Mixer"},
+
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia20", "MM_DL20"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia22", "MM_DL22"},
+ {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Audio Mixer"},
+
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_MI2S_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SEC_MI2S_RX_1", NULL, "SEC_MI2S_RX_1 Audio Mixer"},
+
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_MI2S_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SEC_MI2S_RX_2", NULL, "SEC_MI2S_RX_2 Audio Mixer"},
+
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_MI2S_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SEC_MI2S_RX_3", NULL, "SEC_MI2S_RX_3 Audio Mixer"},
+
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_MI2S_RX_4 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"SEC_MI2S_RX_4", NULL, "SEC_MI2S_RX_4 Audio Mixer"},
+
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_MI2S_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"TERT_MI2S_RX_1", NULL, "TERT_MI2S_RX_1 Audio Mixer"},
+
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_MI2S_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"TERT_MI2S_RX_2", NULL, "TERT_MI2S_RX_2 Audio Mixer"},
+
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_MI2S_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"TERT_MI2S_RX_3", NULL, "TERT_MI2S_RX_3 Audio Mixer"},
+
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_MI2S_RX_4 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"TERT_MI2S_RX_4", NULL, "TERT_MI2S_RX_4 Audio Mixer"},
+
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_MI2S_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"QUAT_MI2S_RX_1", NULL, "QUAT_MI2S_RX_1 Audio Mixer"},
+
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_MI2S_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"QUAT_MI2S_RX_2", NULL, "QUAT_MI2S_RX_2 Audio Mixer"},
+
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_MI2S_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"QUAT_MI2S_RX_3", NULL, "QUAT_MI2S_RX_3 Audio Mixer"},
+
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_MI2S_RX_4 Audio Mixer", "MultiMedia26", "MM_DL26"},
+ {"QUAT_MI2S_RX_4", NULL, "QUAT_MI2S_RX_4 Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
+ {"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"},
+ {"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
+ {"MultiMedia3 Mixer", "MI2S_TX", "MI2S_TX"},
+ {"MultiMedia5 Mixer", "MI2S_TX", "MI2S_TX"},
+ {"MultiMedia16 Mixer", "MI2S_TX", "MI2S_TX"},
+ {"MultiMedia1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia27 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia1 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"},
+ {"MultiMedia2 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"},
+ {"MultiMedia1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"MultiMedia2 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"MultiMedia1 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+ {"MultiMedia2 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+ {"MultiMedia1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"MultiMedia2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia27 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"MultiMedia3 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"},
+ {"MultiMedia5 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"MultiMedia1 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"MultiMedia3 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"},
+ {"MultiMedia5 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"},
+ {"MultiMedia16 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"},
+ {"MultiMedia16 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"},
+ {"MultiMedia1 Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"},
+ {"MultiMedia3 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"},
+ {"MultiMedia5 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"},
+ {"MultiMedia1 Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"},
+ {"MultiMedia3 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"},
+ {"MultiMedia5 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"},
+ {"MultiMedia16 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"},
+ {"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia2 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"},
+ {"MultiMedia2 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"MultiMedia2 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"MultiMedia1 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"MultiMedia1 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia27 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia2 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"MultiMedia3 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"MultiMedia5 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"MultiMedia21 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"MultiMedia3 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia5 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia21 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia6 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia6 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"MultiMedia3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"MultiMedia5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"MultiMedia6 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+ {"MultiMedia3 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+ {"MultiMedia5 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+ {"MultiMedia16 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+ {"MultiMedia6 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"MultiMedia3 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"MultiMedia5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"MultiMedia16 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"MultiMedia6 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia6 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"MultiMedia6 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"MultiMedia6 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+
+ {"MultiMedia1 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia1 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia1 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia1 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia1 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia1 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia1 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia1 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"MultiMedia1 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia1 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia1 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia1 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia1 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia1 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia1 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia1 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+ {"MultiMedia1 Mixer", "SEC_MI2S_TX_1", "SEC_MI2S_TX_1"},
+ {"MultiMedia1 Mixer", "SEC_MI2S_TX_2", "SEC_MI2S_TX_2"},
+ {"MultiMedia1 Mixer", "SEC_MI2S_TX_3", "SEC_MI2S_TX_3"},
+ {"MultiMedia1 Mixer", "SEC_MI2S_TX_4", "SEC_MI2S_TX_4"},
+ {"MultiMedia1 Mixer", "TERT_MI2S_TX_1", "TERT_MI2S_TX_1"},
+ {"MultiMedia1 Mixer", "TERT_MI2S_TX_2", "TERT_MI2S_TX_2"},
+ {"MultiMedia1 Mixer", "TERT_MI2S_TX_3", "TERT_MI2S_TX_3"},
+ {"MultiMedia1 Mixer", "TERT_MI2S_TX_4", "TERT_MI2S_TX_4"},
+ {"MultiMedia1 Mixer", "QUAT_MI2S_TX_1", "QUAT_MI2S_TX_1"},
+ {"MultiMedia1 Mixer", "QUAT_MI2S_TX_2", "QUAT_MI2S_TX_2"},
+ {"MultiMedia1 Mixer", "QUAT_MI2S_TX_3", "QUAT_MI2S_TX_3"},
+ {"MultiMedia1 Mixer", "QUAT_MI2S_TX_4", "QUAT_MI2S_TX_4"},
+
+ {"MultiMedia2 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia2 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia2 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia2 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia2 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia2 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia2 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia2 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"MultiMedia2 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia2 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia2 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia2 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia2 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia2 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia2 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia2 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"MultiMedia2 Mixer", "SEC_MI2S_TX_1", "SEC_MI2S_TX_1"},
+ {"MultiMedia2 Mixer", "SEC_MI2S_TX_2", "SEC_MI2S_TX_2"},
+ {"MultiMedia2 Mixer", "SEC_MI2S_TX_3", "SEC_MI2S_TX_3"},
+ {"MultiMedia2 Mixer", "SEC_MI2S_TX_4", "SEC_MI2S_TX_4"},
+ {"MultiMedia2 Mixer", "TERT_MI2S_TX_1", "TERT_MI2S_TX_1"},
+ {"MultiMedia2 Mixer", "TERT_MI2S_TX_2", "TERT_MI2S_TX_2"},
+ {"MultiMedia2 Mixer", "TERT_MI2S_TX_3", "TERT_MI2S_TX_3"},
+ {"MultiMedia2 Mixer", "TERT_MI2S_TX_4", "TERT_MI2S_TX_4"},
+ {"MultiMedia2 Mixer", "QUAT_MI2S_TX_1", "QUAT_MI2S_TX_1"},
+ {"MultiMedia2 Mixer", "QUAT_MI2S_TX_2", "QUAT_MI2S_TX_2"},
+ {"MultiMedia2 Mixer", "QUAT_MI2S_TX_3", "QUAT_MI2S_TX_3"},
+ {"MultiMedia2 Mixer", "QUAT_MI2S_TX_4", "QUAT_MI2S_TX_4"},
+
+ {"MultiMedia3 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia3 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia3 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia3 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia3 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia3 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia3 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia3 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"MultiMedia3 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia3 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia3 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia3 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia3 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia3 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia3 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia3 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"MultiMedia3 Mixer", "SEC_MI2S_TX_1", "SEC_MI2S_TX_1"},
+ {"MultiMedia3 Mixer", "SEC_MI2S_TX_2", "SEC_MI2S_TX_2"},
+ {"MultiMedia3 Mixer", "SEC_MI2S_TX_3", "SEC_MI2S_TX_3"},
+ {"MultiMedia3 Mixer", "SEC_MI2S_TX_4", "SEC_MI2S_TX_4"},
+ {"MultiMedia3 Mixer", "TERT_MI2S_TX_1", "TERT_MI2S_TX_1"},
+ {"MultiMedia3 Mixer", "TERT_MI2S_TX_2", "TERT_MI2S_TX_2"},
+ {"MultiMedia3 Mixer", "TERT_MI2S_TX_3", "TERT_MI2S_TX_3"},
+ {"MultiMedia3 Mixer", "TERT_MI2S_TX_4", "TERT_MI2S_TX_4"},
+ {"MultiMedia3 Mixer", "QUAT_MI2S_TX_1", "QUAT_MI2S_TX_1"},
+ {"MultiMedia3 Mixer", "QUAT_MI2S_TX_2", "QUAT_MI2S_TX_2"},
+ {"MultiMedia3 Mixer", "QUAT_MI2S_TX_3", "QUAT_MI2S_TX_3"},
+ {"MultiMedia3 Mixer", "QUAT_MI2S_TX_4", "QUAT_MI2S_TX_4"},
+
+ {"MultiMedia4 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia4 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia4 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia4 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia4 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia4 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia4 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia4 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"MultiMedia4 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia4 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia4 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia4 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia4 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia4 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia4 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia4 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+ {"MultiMedia5 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia5 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia5 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia5 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia5 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia5 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia5 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia5 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"MultiMedia5 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia5 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia5 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia5 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia5 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia5 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia5 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia5 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"MultiMedia5 Mixer", "SEC_MI2S_TX_1", "SEC_MI2S_TX_1"},
+ {"MultiMedia5 Mixer", "SEC_MI2S_TX_2", "SEC_MI2S_TX_2"},
+ {"MultiMedia5 Mixer", "SEC_MI2S_TX_3", "SEC_MI2S_TX_3"},
+ {"MultiMedia5 Mixer", "SEC_MI2S_TX_4", "SEC_MI2S_TX_4"},
+ {"MultiMedia5 Mixer", "TERT_MI2S_TX_1", "TERT_MI2S_TX_1"},
+ {"MultiMedia5 Mixer", "TERT_MI2S_TX_2", "TERT_MI2S_TX_2"},
+ {"MultiMedia5 Mixer", "TERT_MI2S_TX_3", "TERT_MI2S_TX_3"},
+ {"MultiMedia5 Mixer", "TERT_MI2S_TX_4", "TERT_MI2S_TX_4"},
+ {"MultiMedia5 Mixer", "QUAT_MI2S_TX_1", "QUAT_MI2S_TX_1"},
+ {"MultiMedia5 Mixer", "QUAT_MI2S_TX_2", "QUAT_MI2S_TX_2"},
+ {"MultiMedia5 Mixer", "QUAT_MI2S_TX_3", "QUAT_MI2S_TX_3"},
+ {"MultiMedia5 Mixer", "QUAT_MI2S_TX_4", "QUAT_MI2S_TX_4"},
+
+ {"MultiMedia6 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia6 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia6 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia6 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia6 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia6 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia6 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia6 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"MultiMedia6 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia6 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia6 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia6 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia6 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia6 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia6 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia6 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+ {"MultiMedia8 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia8 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia8 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia8 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia8 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia8 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia8 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia8 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"MultiMedia8 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia8 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia8 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia8 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia8 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia8 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia8 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia8 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+ {"MultiMedia9 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia9 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia9 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia9 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia9 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia9 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia9 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia9 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia9 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia9 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia9 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia9 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"MultiMedia9 Mixer", "SEC_MI2S_TX_1", "SEC_MI2S_TX_1"},
+ {"MultiMedia9 Mixer", "SEC_MI2S_TX_2", "SEC_MI2S_TX_2"},
+ {"MultiMedia9 Mixer", "SEC_MI2S_TX_3", "SEC_MI2S_TX_3"},
+ {"MultiMedia9 Mixer", "SEC_MI2S_TX_4", "SEC_MI2S_TX_4"},
+ {"MultiMedia9 Mixer", "TERT_MI2S_TX_1", "TERT_MI2S_TX_1"},
+ {"MultiMedia9 Mixer", "TERT_MI2S_TX_2", "TERT_MI2S_TX_2"},
+ {"MultiMedia9 Mixer", "TERT_MI2S_TX_3", "TERT_MI2S_TX_3"},
+ {"MultiMedia9 Mixer", "TERT_MI2S_TX_4", "TERT_MI2S_TX_4"},
+ {"MultiMedia9 Mixer", "QUAT_MI2S_TX_1", "QUAT_MI2S_TX_1"},
+ {"MultiMedia9 Mixer", "QUAT_MI2S_TX_2", "QUAT_MI2S_TX_2"},
+ {"MultiMedia9 Mixer", "QUAT_MI2S_TX_3", "QUAT_MI2S_TX_3"},
+ {"MultiMedia9 Mixer", "QUAT_MI2S_TX_4", "QUAT_MI2S_TX_4"},
+
+ {"MultiMedia20 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia20 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"MultiMedia20 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"MultiMedia20 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia20 Mixer", "SEC_MI2S_TX_1", "SEC_MI2S_TX_1"},
+ {"MultiMedia20 Mixer", "SEC_MI2S_TX_2", "SEC_MI2S_TX_2"},
+ {"MultiMedia20 Mixer", "SEC_MI2S_TX_3", "SEC_MI2S_TX_3"},
+ {"MultiMedia20 Mixer", "SEC_MI2S_TX_4", "SEC_MI2S_TX_4"},
+ {"MultiMedia20 Mixer", "TERT_MI2S_TX_1", "TERT_MI2S_TX_1"},
+ {"MultiMedia20 Mixer", "TERT_MI2S_TX_2", "TERT_MI2S_TX_2"},
+ {"MultiMedia20 Mixer", "TERT_MI2S_TX_3", "TERT_MI2S_TX_3"},
+ {"MultiMedia20 Mixer", "TERT_MI2S_TX_4", "TERT_MI2S_TX_4"},
+ {"MultiMedia20 Mixer", "QUAT_MI2S_TX_1", "QUAT_MI2S_TX_1"},
+ {"MultiMedia20 Mixer", "QUAT_MI2S_TX_2", "QUAT_MI2S_TX_2"},
+ {"MultiMedia20 Mixer", "QUAT_MI2S_TX_3", "QUAT_MI2S_TX_3"},
+ {"MultiMedia20 Mixer", "QUAT_MI2S_TX_4", "QUAT_MI2S_TX_4"},
+ {"MultiMedia20 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia20 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia20 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia20 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia20 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia20 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia20 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia20 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"MultiMedia20 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia20 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia20 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia20 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia20 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia20 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia20 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia20 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+ {"MultiMedia21 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia21 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia21 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia21 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia21 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia21 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia21 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia21 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"MultiMedia21 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia21 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia21 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia21 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia21 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia21 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia21 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia21 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"MultiMedia21 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"MultiMedia21 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+
+ {"MultiMedia1 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+ {"MultiMedia2 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+ {"MultiMedia4 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+ {"MultiMedia5 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+ {"MultiMedia6 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+ {"MultiMedia8 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+
+ {"MultiMedia16 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia16 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia16 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia16 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"MultiMedia16 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia16 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia16 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia16 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"MultiMedia16 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia16 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia16 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia16 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia16 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia16 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia16 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia16 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"MultiMedia16 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+
+ {"MultiMedia30 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia30 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia30 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia30 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+
+ {"MultiMedia31 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia31 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia31 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia31 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+
+ {"MultiMedia32 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia32 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia32 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia32 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+
+ {"MultiMedia33 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"MultiMedia33 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"MultiMedia33 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"MultiMedia33 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+
+ {"MultiMedia30 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia30 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia30 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia30 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+
+ {"MultiMedia31 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia31 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia31 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia31 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+
+ {"MultiMedia32 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia32 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia32 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia32 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+
+ {"MultiMedia33 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"MultiMedia33 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"MultiMedia33 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"MultiMedia33 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+
+ {"MultiMedia30 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia30 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia30 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia30 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+
+ {"MultiMedia31 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia31 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia31 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia31 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+
+ {"MultiMedia32 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia32 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia32 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia32 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+
+ {"MultiMedia33 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia33 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia33 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia33 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_UL6"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
+
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia6", "MM_UL6"},
+ {"INT_BT_A2DP_RX", NULL, "INTERNAL_A2DP_RX Audio Mixer"},
+
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
+
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia3 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia4 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia17 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia18 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia19 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia28 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia29 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia5 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia8 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia16 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia4 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia16 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia17 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia18 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia19 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia28 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia29 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia5 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia6 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia8 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+
+ {"MultiMedia1 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MultiMedia3 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MultiMedia4 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MultiMedia17 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MultiMedia18 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MultiMedia19 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MultiMedia28 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MultiMedia29 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MultiMedia8 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MultiMedia16 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MM_UL1", NULL, "MultiMedia1 Mixer"},
+ {"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MM_UL2", NULL, "MultiMedia2 Mixer"},
+ {"MM_UL3", NULL, "MultiMedia3 Mixer"},
+ {"MM_UL4", NULL, "MultiMedia4 Mixer"},
+ {"MM_UL5", NULL, "MultiMedia5 Mixer"},
+ {"MM_UL6", NULL, "MultiMedia6 Mixer"},
+ {"MM_UL8", NULL, "MultiMedia8 Mixer"},
+ {"MM_UL9", NULL, "MultiMedia9 Mixer"},
+ {"MM_UL16", NULL, "MultiMedia16 Mixer"},
+ {"MM_UL17", NULL, "MultiMedia17 Mixer"},
+ {"MM_UL18", NULL, "MultiMedia18 Mixer"},
+ {"MM_UL19", NULL, "MultiMedia19 Mixer"},
+ {"MM_UL20", NULL, "MultiMedia20 Mixer"},
+ {"MM_UL21", NULL, "MultiMedia21 Mixer"},
+ {"MM_UL27", NULL, "MultiMedia27 Mixer"},
+ {"MM_UL28", NULL, "MultiMedia28 Mixer"},
+ {"MM_UL29", NULL, "MultiMedia29 Mixer"},
+ {"MM_UL30", NULL, "MultiMedia30 Mixer"},
+ {"MM_UL31", NULL, "MultiMedia31 Mixer"},
+ {"MM_UL32", NULL, "MultiMedia32 Mixer"},
+ {"MM_UL33", NULL, "MultiMedia33 Mixer"},
+
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_UL6"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia21", "MM_UL21"},
+ {"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
+
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia21", "MM_DL21"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_UL6"},
+ {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia21", "MM_UL21"},
+ {"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX Audio Mixer"},
+
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_AUX_PCM_RX", NULL, "TERT_AUX_PCM_RX Audio Mixer"},
+
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX Audio Mixer"},
+
+ {"MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"},
+
+ {"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"PRI_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"PRI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"PRI_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"PRI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"PRI_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"PRI_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"PRI_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
+
+ {"SEC_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SEC_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"SEC_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SEC_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"SEC_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SEC_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"SEC_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"},
+
+ {"SEC_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SEC_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"SEC_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SEC_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"SEC_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SEC_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"SEC_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"SEC_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"SEC_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX_Voice Mixer"},
+
+ {"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SLIM_0_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"SLIM_0_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SLIM_0_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SLIM_0_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"SLIM_0_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"SLIM_0_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"SLIM_0_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"SLIM_0_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"SLIM_0_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"SLIM_0_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
+
+ {"SLIM_6_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SLIM_6_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"SLIM_6_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SLIM_6_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"SLIM_6_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SLIM_6_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"SLIM_6_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"SLIM_6_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"SLIM_6_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"SLIM_6_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"SLIM_6_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"SLIM_6_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"SLIMBUS_6_RX", NULL, "SLIM_6_RX_Voice Mixer"},
+
+ {"USB_AUDIO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"USB_AUDIO_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"USB_AUDIO_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"USB_AUDIO_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"USB_AUDIO_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"USB_AUDIO_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"USB_AUDIO_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"USB_AUDIO_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"USB_AUDIO_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"USB_AUDIO_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"USB_AUDIO_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"USB_AUDIO_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"USB_AUDIO_RX", NULL, "USB_AUDIO_RX_Voice Mixer"},
+
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
+
+ {"AFE_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"AFE_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"AFE_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"},
+
+ {"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
+
+ {"SEC_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX_Voice Mixer"},
+
+ {"TERT_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"TERT_AUX_PCM_RX", NULL, "TERT_AUX_PCM_RX_Voice Mixer"},
+
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX_Voice Mixer"},
+
+ {"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"HDMI_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"HDMI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"HDMI_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"HDMI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"HDMI_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"HDMI_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"HDMI_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"HDMI", NULL, "HDMI_RX_Voice Mixer"},
+ {"HDMI", NULL, "HDMI_DL_HL"},
+
+ {"MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"},
+
+ {"PRI_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"PRI_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"PRI_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"PRI_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"PRI_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"PRI_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"PRI_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"PRI_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"PRI_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"PRI_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"PRI_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"PRI_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_Voice Mixer"},
+
+ {"INT0_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"INT0_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"INT0_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"INT0_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"INT0_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX_Voice Mixer"},
+
+ {"INT4_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"INT4_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"INT4_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"INT4_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"INT4_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_Voice Mixer"},
+
+ {"TERT_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"TERT_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"TERT_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"TERT_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"TERT_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"TERT_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"TERT_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"TERT_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"TERT_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"TERT_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"TERT_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"TERT_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX_Voice Mixer"},
+
+ {"QUAT_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"QUAT_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"QUAT_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"QUAT_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"QUAT_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"QUAT_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"QUAT_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"QUAT_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"QUAT_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"QUAT_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"QUAT_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX_Voice Mixer"},
+
+ {"QUIN_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"QUIN_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"QUIN_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"QUIN_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"QUIN_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"QUIN_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"QUIN_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"QUIN_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"QUIN_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"QUIN_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"QUIN_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX_Voice Mixer"},
+
+ {"QUAT_TDM_RX_2_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"QUAT_TDM_RX_2_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2_Voice Mixer"},
+
+ {"PROXY_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"PROXY_RX", NULL, "PROXY_RX_Voice Mixer"},
+
+ {"PROXY_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"PROXY_RX", NULL, "PROXY_RX_Voice Mixer"},
+
+ {"VOC_EXT_EC MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"VOC_EXT_EC MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"VOC_EXT_EC MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"VOC_EXT_EC MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+ {"VOC_EXT_EC MUX", "SLIM_1_TX" , "SLIMBUS_1_TX"},
+ {"CS-VOICE_UL1", NULL, "VOC_EXT_EC MUX"},
+ {"VOIP_UL", NULL, "VOC_EXT_EC MUX"},
+ {"VoLTE_UL", NULL, "VOC_EXT_EC MUX"},
+ {"VOICE2_UL", NULL, "VOC_EXT_EC MUX"},
+ {"VoWLAN_UL", NULL, "VOC_EXT_EC MUX"},
+ {"VOICEMMODE1_UL", NULL, "VOC_EXT_EC MUX"},
+ {"VOICEMMODE2_UL", NULL, "VOC_EXT_EC MUX"},
+
+ {"AUDIO_REF_EC_UL1 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL1 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL1 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL1 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL1 MUX", "SLIM_1_TX" , "SLIMBUS_1_TX"},
+ {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_TX_1" , "QUAT_TDM_TX_1"},
+ {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_0" , "QUAT_TDM_RX_0"},
+ {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_1" , "QUAT_TDM_RX_1"},
+ {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_2" , "QUAT_TDM_RX_2"},
+ {"AUDIO_REF_EC_UL1 MUX", "TERT_TDM_TX_0" , "TERT_TDM_TX_0"},
+ {"AUDIO_REF_EC_UL1 MUX", "TERT_TDM_RX_0", "TERT_TDM_RX_0"},
+ {"AUDIO_REF_EC_UL1 MUX", "TERT_TDM_RX_1", "TERT_TDM_RX_1"},
+ {"AUDIO_REF_EC_UL1 MUX", "TERT_TDM_RX_2", "TERT_TDM_RX_2"},
+ {"AUDIO_REF_EC_UL1 MUX", "SEC_TDM_RX_0", "SEC_TDM_RX_0"},
+ {"AUDIO_REF_EC_UL1 MUX", "SEC_TDM_RX_1", "SEC_TDM_RX_1"},
+ {"AUDIO_REF_EC_UL1 MUX", "SEC_TDM_RX_2", "SEC_TDM_RX_2"},
+ {"AUDIO_REF_EC_UL1 MUX", "PRI_TDM_RX_0", "PRI_TDM_RX_0"},
+ {"AUDIO_REF_EC_UL1 MUX", "PRI_TDM_RX_1", "PRI_TDM_RX_1"},
+ {"AUDIO_REF_EC_UL1 MUX", "PRI_TDM_RX_2", "PRI_TDM_RX_2"},
+ {"AUDIO_REF_EC_UL1 MUX", "PRI_TDM_RX_3", "PRI_TDM_RX_3"},
+
+ {"AUDIO_REF_EC_UL2 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL2 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL2 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL2 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+
+ {"AUDIO_REF_EC_UL3 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL3 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL3 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL3 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+ {"AUDIO_REF_EC_UL4 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL4 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL4 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL4 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+
+ {"AUDIO_REF_EC_UL5 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL5 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL5 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL5 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+
+ {"AUDIO_REF_EC_UL6 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL6 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL6 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL6 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+
+ {"AUDIO_REF_EC_UL8 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL8 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL8 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL8 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+
+ {"AUDIO_REF_EC_UL9 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL9 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL9 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL9 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+
+ {"AUDIO_REF_EC_UL17 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL17 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL17 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL17 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+ {"AUDIO_REF_EC_UL18 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL18 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL18 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL18 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+ {"AUDIO_REF_EC_UL19 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL19 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL19 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL19 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+ {"AUDIO_REF_EC_UL28 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL28 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL28 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL28 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+ {"AUDIO_REF_EC_UL29 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL29 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL29 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL29 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+ {"MM_UL1", NULL, "AUDIO_REF_EC_UL1 MUX"},
+ {"MM_UL2", NULL, "AUDIO_REF_EC_UL2 MUX"},
+ {"MM_UL3", NULL, "AUDIO_REF_EC_UL3 MUX"},
+ {"MM_UL4", NULL, "AUDIO_REF_EC_UL4 MUX"},
+ {"MM_UL5", NULL, "AUDIO_REF_EC_UL5 MUX"},
+ {"MM_UL6", NULL, "AUDIO_REF_EC_UL6 MUX"},
+ {"MM_UL8", NULL, "AUDIO_REF_EC_UL8 MUX"},
+ {"MM_UL9", NULL, "AUDIO_REF_EC_UL9 MUX"},
+ {"MM_UL16", NULL, "AUDIO_REF_EC_UL16 MUX"},
+ {"MM_UL17", NULL, "AUDIO_REF_EC_UL17 MUX"},
+ {"MM_UL18", NULL, "AUDIO_REF_EC_UL18 MUX"},
+ {"MM_UL19", NULL, "AUDIO_REF_EC_UL19 MUX"},
+ {"MM_UL28", NULL, "AUDIO_REF_EC_UL28 MUX"},
+ {"MM_UL29", NULL, "AUDIO_REF_EC_UL29 MUX"},
+
+ {"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
+ {"Voice_Tx Mixer", "PRI_MI2S_TX_Voice", "PRI_MI2S_TX"},
+ {"Voice_Tx Mixer", "MI2S_TX_Voice", "MI2S_TX"},
+ {"Voice_Tx Mixer", "TERT_MI2S_TX_Voice", "TERT_MI2S_TX"},
+ {"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
+ {"Voice_Tx Mixer", "SLIM_7_TX_Voice", "SLIMBUS_7_TX"},
+ {"Voice_Tx Mixer", "SLIM_8_TX_Voice", "SLIMBUS_8_TX"},
+ {"Voice_Tx Mixer", "USB_AUDIO_TX_Voice", "USB_AUDIO_TX"},
+ {"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
+ {"Voice_Tx Mixer", "AFE_PCM_TX_Voice", "PCM_TX"},
+ {"Voice_Tx Mixer", "AUX_PCM_TX_Voice", "AUX_PCM_TX"},
+ {"Voice_Tx Mixer", "SEC_AUX_PCM_TX_Voice", "SEC_AUX_PCM_TX"},
+ {"Voice_Tx Mixer", "TERT_AUX_PCM_TX_Voice", "TERT_AUX_PCM_TX"},
+ {"Voice_Tx Mixer", "QUAT_AUX_PCM_TX_Voice", "QUAT_AUX_PCM_TX"},
+ {"Voice_Tx Mixer", "SEC_MI2S_TX_Voice", "SEC_MI2S_TX"},
+ {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
+
+ {"Voice2_Tx Mixer", "PRI_TX_Voice2", "PRI_I2S_TX"},
+ {"Voice2_Tx Mixer", "PRI_MI2S_TX_Voice2", "PRI_MI2S_TX"},
+ {"Voice2_Tx Mixer", "MI2S_TX_Voice2", "MI2S_TX"},
+ {"Voice2_Tx Mixer", "TERT_MI2S_TX_Voice2", "TERT_MI2S_TX"},
+ {"Voice2_Tx Mixer", "SLIM_0_TX_Voice2", "SLIMBUS_0_TX"},
+ {"Voice2_Tx Mixer", "SLIM_7_TX_Voice2", "SLIMBUS_7_TX"},
+ {"Voice2_Tx Mixer", "SLIM_8_TX_Voice2", "SLIMBUS_8_TX"},
+ {"Voice2_Tx Mixer", "USB_AUDIO_TX_Voice2", "USB_AUDIO_TX"},
+ {"Voice2_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice2", "INT_BT_SCO_TX"},
+ {"Voice2_Tx Mixer", "AFE_PCM_TX_Voice2", "PCM_TX"},
+ {"Voice2_Tx Mixer", "AUX_PCM_TX_Voice2", "AUX_PCM_TX"},
+ {"Voice2_Tx Mixer", "SEC_AUX_PCM_TX_Voice2", "SEC_AUX_PCM_TX"},
+ {"Voice2_Tx Mixer", "TERT_AUX_PCM_TX_Voice2", "TERT_AUX_PCM_TX"},
+ {"Voice2_Tx Mixer", "QUAT_AUX_PCM_TX_Voice2", "QUAT_AUX_PCM_TX"},
+ {"VOICE2_UL", NULL, "Voice2_Tx Mixer"},
+
+ {"VoLTE_Tx Mixer", "PRI_TX_VoLTE", "PRI_I2S_TX"},
+ {"VoLTE_Tx Mixer", "SLIM_0_TX_VoLTE", "SLIMBUS_0_TX"},
+ {"VoLTE_Tx Mixer", "SLIM_7_TX_VoLTE", "SLIMBUS_7_TX"},
+ {"VoLTE_Tx Mixer", "SLIM_8_TX_VoLTE", "SLIMBUS_8_TX"},
+ {"VoLTE_Tx Mixer", "USB_AUDIO_TX_VoLTE", "USB_AUDIO_TX"},
+ {"VoLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_VoLTE", "INT_BT_SCO_TX"},
+ {"VoLTE_Tx Mixer", "AFE_PCM_TX_VoLTE", "PCM_TX"},
+ {"VoLTE_Tx Mixer", "AUX_PCM_TX_VoLTE", "AUX_PCM_TX"},
+ {"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"},
+ {"VoLTE_Tx Mixer", "TERT_AUX_PCM_TX_VoLTE", "TERT_AUX_PCM_TX"},
+ {"VoLTE_Tx Mixer", "QUAT_AUX_PCM_TX_VoLTE", "QUAT_AUX_PCM_TX"},
+ {"VoLTE_Tx Mixer", "MI2S_TX_VoLTE", "MI2S_TX"},
+ {"VoLTE_Tx Mixer", "PRI_MI2S_TX_VoLTE", "PRI_MI2S_TX"},
+ {"VoLTE_Tx Mixer", "TERT_MI2S_TX_VoLTE", "TERT_MI2S_TX"},
+ {"VoLTE_UL", NULL, "VoLTE_Tx Mixer"},
+
+ {"VoWLAN_Tx Mixer", "PRI_TX_VoWLAN", "PRI_I2S_TX"},
+ {"VoWLAN_Tx Mixer", "SLIM_0_TX_VoWLAN", "SLIMBUS_0_TX"},
+ {"VoWLAN_Tx Mixer", "SLIM_7_TX_VoWLAN", "SLIMBUS_7_TX"},
+ {"VoWLAN_Tx Mixer", "SLIM_8_TX_VoWLAN", "SLIMBUS_8_TX"},
+ {"VoWLAN_Tx Mixer", "USB_AUDIO_TX_VoWLAN", "USB_AUDIO_TX"},
+ {"VoWLAN_Tx Mixer", "INTERNAL_BT_SCO_TX_VoWLAN", "INT_BT_SCO_TX"},
+ {"VoWLAN_Tx Mixer", "AFE_PCM_TX_VoWLAN", "PCM_TX"},
+ {"VoWLAN_Tx Mixer", "AUX_PCM_TX_VoWLAN", "AUX_PCM_TX"},
+ {"VoWLAN_Tx Mixer", "SEC_AUX_PCM_TX_VoWLAN", "SEC_AUX_PCM_TX"},
+ {"VoWLAN_Tx Mixer", "TERT_AUX_PCM_TX_VoWLAN", "TERT_AUX_PCM_TX"},
+ {"VoWLAN_Tx Mixer", "QUAT_AUX_PCM_TX_VoWLAN", "QUAT_AUX_PCM_TX"},
+ {"VoWLAN_Tx Mixer", "MI2S_TX_VoWLAN", "MI2S_TX"},
+ {"VoWLAN_Tx Mixer", "PRI_MI2S_TX_VoWLAN", "PRI_MI2S_TX"},
+ {"VoWLAN_Tx Mixer", "TERT_MI2S_TX_VoWLAN", "TERT_MI2S_TX"},
+ {"VoWLAN_UL", NULL, "VoWLAN_Tx Mixer"},
+
+ {"VoiceMMode1_Tx Mixer", "PRI_TX_MMode1", "PRI_I2S_TX"},
+ {"VoiceMMode1_Tx Mixer", "PRI_MI2S_TX_MMode1", "PRI_MI2S_TX"},
+ {"VoiceMMode1_Tx Mixer", "MI2S_TX_MMode1", "MI2S_TX"},
+ {"VoiceMMode1_Tx Mixer", "TERT_MI2S_TX_MMode1", "TERT_MI2S_TX"},
+ {"VoiceMMode1_Tx Mixer", "INT3_MI2S_TX_MMode1", "INT3_MI2S_TX"},
+ {"VoiceMMode1_Tx Mixer", "SLIM_0_TX_MMode1", "SLIMBUS_0_TX"},
+ {"VoiceMMode1_Tx Mixer", "SLIM_7_TX_MMode1", "SLIMBUS_7_TX"},
+ {"VoiceMMode1_Tx Mixer", "SLIM_8_TX_MMode1", "SLIMBUS_8_TX"},
+ {"VoiceMMode1_Tx Mixer", "USB_AUDIO_TX_MMode1", "USB_AUDIO_TX"},
+ {"VoiceMMode1_Tx Mixer", "INT_BT_SCO_TX_MMode1", "INT_BT_SCO_TX"},
+ {"VoiceMMode1_Tx Mixer", "AFE_PCM_TX_MMode1", "PCM_TX"},
+ {"VoiceMMode1_Tx Mixer", "AUX_PCM_TX_MMode1", "AUX_PCM_TX"},
+ {"VoiceMMode1_Tx Mixer", "SEC_AUX_PCM_TX_MMode1", "SEC_AUX_PCM_TX"},
+ {"VoiceMMode1_Tx Mixer", "TERT_AUX_PCM_TX_MMode1", "TERT_AUX_PCM_TX"},
+ {"VoiceMMode1_Tx Mixer", "QUAT_AUX_PCM_TX_MMode1", "QUAT_AUX_PCM_TX"},
+ {"VoiceMMode1_Tx Mixer", "QUAT_TDM_TX_0_MMode1", "QUAT_TDM_TX_0"},
+ {"VoiceMMode1_Tx Mixer", "PROXY_TX_MMode1", "PROXY_TX"},
+ {"VOICEMMODE1_UL", NULL, "VoiceMMode1_Tx Mixer"},
+
+ {"VoiceMMode2_Tx Mixer", "PRI_TX_MMode2", "PRI_I2S_TX"},
+ {"VoiceMMode2_Tx Mixer", "PRI_MI2S_TX_MMode2", "PRI_MI2S_TX"},
+ {"VoiceMMode2_Tx Mixer", "MI2S_TX_MMode2", "MI2S_TX"},
+ {"VoiceMMode2_Tx Mixer", "TERT_MI2S_TX_MMode2", "TERT_MI2S_TX"},
+ {"VoiceMMode2_Tx Mixer", "INT3_MI2S_TX_MMode2", "INT3_MI2S_TX"},
+ {"VoiceMMode2_Tx Mixer", "SLIM_0_TX_MMode2", "SLIMBUS_0_TX"},
+ {"VoiceMMode2_Tx Mixer", "SLIM_7_TX_MMode2", "SLIMBUS_7_TX"},
+ {"VoiceMMode2_Tx Mixer", "SLIM_8_TX_MMode2", "SLIMBUS_8_TX"},
+ {"VoiceMMode2_Tx Mixer", "USB_AUDIO_TX_MMode2", "USB_AUDIO_TX"},
+ {"VoiceMMode2_Tx Mixer", "INT_BT_SCO_TX_MMode2", "INT_BT_SCO_TX"},
+ {"VoiceMMode2_Tx Mixer", "AFE_PCM_TX_MMode2", "PCM_TX"},
+ {"VoiceMMode2_Tx Mixer", "AUX_PCM_TX_MMode2", "AUX_PCM_TX"},
+ {"VoiceMMode2_Tx Mixer", "SEC_AUX_PCM_TX_MMode2", "SEC_AUX_PCM_TX"},
+ {"VoiceMMode2_Tx Mixer", "TERT_AUX_PCM_TX_MMode2", "TERT_AUX_PCM_TX"},
+ {"VoiceMMode2_Tx Mixer", "QUAT_AUX_PCM_TX_MMode2", "QUAT_AUX_PCM_TX"},
+ {"VoiceMMode2_Tx Mixer", "PROXY_TX_MMode2", "PROXY_TX"},
+ {"VOICEMMODE2_UL", NULL, "VoiceMMode2_Tx Mixer"},
+
+ {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
+ {"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"},
+ {"Voip_Tx Mixer", "TERT_MI2S_TX_Voip", "TERT_MI2S_TX"},
+ {"Voip_Tx Mixer", "INT3_MI2S_TX_Voip", "INT3_MI2S_TX"},
+ {"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
+ {"Voip_Tx Mixer", "SLIM_7_TX_Voip", "SLIMBUS_7_TX"},
+ {"Voip_Tx Mixer", "SLIM_8_TX_Voip", "SLIMBUS_8_TX"},
+ {"Voip_Tx Mixer", "USB_AUDIO_TX_Voip", "USB_AUDIO_TX"},
+ {"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
+ {"Voip_Tx Mixer", "AFE_PCM_TX_Voip", "PCM_TX"},
+ {"Voip_Tx Mixer", "AUX_PCM_TX_Voip", "AUX_PCM_TX"},
+ {"Voip_Tx Mixer", "SEC_AUX_PCM_TX_Voip", "SEC_AUX_PCM_TX"},
+ {"Voip_Tx Mixer", "TERT_AUX_PCM_TX_Voip", "TERT_AUX_PCM_TX"},
+ {"Voip_Tx Mixer", "QUAT_AUX_PCM_TX_Voip", "QUAT_AUX_PCM_TX"},
+ {"Voip_Tx Mixer", "PRI_MI2S_TX_Voip", "PRI_MI2S_TX"},
+ {"VOIP_UL", NULL, "Voip_Tx Mixer"},
+
+ {"SLIMBUS_DL_HL", "Switch", "SLIM0_DL_HL"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_DL_HL"},
+ {"SLIMBUS1_DL_HL", "Switch", "SLIM1_DL_HL"},
+ {"SLIMBUS_1_RX", NULL, "SLIMBUS1_DL_HL"},
+ {"SLIMBUS3_DL_HL", "Switch", "SLIM3_DL_HL"},
+ {"SLIMBUS_3_RX", NULL, "SLIMBUS3_DL_HL"},
+ {"SLIMBUS4_DL_HL", "Switch", "SLIM4_DL_HL"},
+ {"SLIMBUS_4_RX", NULL, "SLIMBUS4_DL_HL"},
+ {"SLIMBUS6_DL_HL", "Switch", "SLIM0_DL_HL"},
+ {"SLIMBUS_6_RX", NULL, "SLIMBUS6_DL_HL"},
+ {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
+ {"SLIM1_UL_HL", NULL, "SLIMBUS_1_TX"},
+ {"SLIM3_UL_HL", NULL, "SLIMBUS_3_TX"},
+ {"SLIM4_UL_HL", NULL, "SLIMBUS_4_TX"},
+ {"SLIM8_UL_HL", NULL, "SLIMBUS_8_TX"},
+
+ {"LSM1 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+ {"LSM1 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+ {"LSM1 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+ {"LSM1 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+ {"LSM1 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+ {"LSM1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"LSM1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM1 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"LSM1_UL_HL", NULL, "LSM1 Mixer"},
+
+ {"LSM2 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+ {"LSM2 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+ {"LSM2 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+ {"LSM2 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+ {"LSM2 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+ {"LSM2 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"LSM2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM2 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"LSM2_UL_HL", NULL, "LSM2 Mixer"},
+
+
+ {"LSM3 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+ {"LSM3 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+ {"LSM3 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+ {"LSM3 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+ {"LSM3 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+ {"LSM3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"LSM3 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM3 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM3 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"LSM3_UL_HL", NULL, "LSM3 Mixer"},
+
+
+ {"LSM4 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+ {"LSM4 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+ {"LSM4 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+ {"LSM4 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+ {"LSM4 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+ {"LSM4 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"LSM4 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM4 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM4 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"LSM4_UL_HL", NULL, "LSM4 Mixer"},
+
+ {"LSM5 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+ {"LSM5 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+ {"LSM5 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+ {"LSM5 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+ {"LSM5 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+ {"LSM5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"LSM5 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM5 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"LSM5_UL_HL", NULL, "LSM5 Mixer"},
+
+ {"LSM6 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+ {"LSM6 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+ {"LSM6 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+ {"LSM6 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+ {"LSM6 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+ {"LSM6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM6 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"LSM6_UL_HL", NULL, "LSM6 Mixer"},
+
+ {"LSM7 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+ {"LSM7 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+ {"LSM7 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+ {"LSM7 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+ {"LSM7 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+ {"LSM7 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM7 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"LSM7_UL_HL", NULL, "LSM7 Mixer"},
+
+ {"LSM8 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+ {"LSM8 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+ {"LSM8 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+ {"LSM8 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+ {"LSM8 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+ {"LSM8 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM8 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"LSM8_UL_HL", NULL, "LSM8 Mixer"},
+
+
+ {"CPE_LSM_UL_HL", NULL, "BE_IN"},
+ {"QCHAT_Tx Mixer", "PRI_TX_QCHAT", "PRI_I2S_TX"},
+ {"QCHAT_Tx Mixer", "SLIM_0_TX_QCHAT", "SLIMBUS_0_TX"},
+ {"QCHAT_Tx Mixer", "SLIM_7_TX_QCHAT", "SLIMBUS_7_TX"},
+ {"QCHAT_Tx Mixer", "SLIM_8_TX_QCHAT", "SLIMBUS_8_TX"},
+ {"QCHAT_Tx Mixer", "INTERNAL_BT_SCO_TX_QCHAT", "INT_BT_SCO_TX"},
+ {"QCHAT_Tx Mixer", "AFE_PCM_TX_QCHAT", "PCM_TX"},
+ {"QCHAT_Tx Mixer", "AUX_PCM_TX_QCHAT", "AUX_PCM_TX"},
+ {"QCHAT_Tx Mixer", "SEC_AUX_PCM_TX_QCHAT", "SEC_AUX_PCM_TX"},
+ {"QCHAT_Tx Mixer", "TERT_AUX_PCM_TX_QCHAT", "TERT_AUX_PCM_TX"},
+ {"QCHAT_Tx Mixer", "QUAT_AUX_PCM_TX_QCHAT", "QUAT_AUX_PCM_TX"},
+ {"QCHAT_Tx Mixer", "MI2S_TX_QCHAT", "MI2S_TX"},
+ {"QCHAT_Tx Mixer", "PRI_MI2S_TX_QCHAT", "PRI_MI2S_TX"},
+ {"QCHAT_Tx Mixer", "TERT_MI2S_TX_QCHAT", "TERT_MI2S_TX"},
+ {"QCHAT_Tx Mixer", "INT3_MI2S_TX_QCHAT", "INT3_MI2S_TX"},
+ {"QCHAT_Tx Mixer", "USB_AUDIO_TX_QCHAT", "USB_AUDIO_TX"},
+ {"QCHAT_UL", NULL, "QCHAT_Tx Mixer"},
+
+ {"INT_FM_RX", NULL, "INTFM_DL_HL"},
+ {"INTFM_UL_HL", NULL, "INT_FM_TX"},
+ {"INTHFP_UL_HL", NULL, "HFP_PRI_AUX_UL_HL"},
+ {"HFP_PRI_AUX_UL_HL", "Switch", "AUX_PCM_TX"},
+ {"INTHFP_UL_HL", NULL, "HFP_AUX_UL_HL"},
+ {"HFP_AUX_UL_HL", "Switch", "SEC_AUX_PCM_TX"},
+ {"INTHFP_UL_HL", NULL, "HFP_INT_UL_HL"},
+ {"HFP_INT_UL_HL", "Switch", "INT_BT_SCO_TX"},
+ {"SLIM7_UL_HL", NULL, "HFP_SLIM7_UL_HL"},
+ {"HFP_SLIM7_UL_HL", "Switch", "SLIMBUS_7_TX"},
+ {"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"},
+ {"AUX_PCM_RX", NULL, "INTHFP_DL_HL"},
+ {"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"},
+ {"MI2S_RX", NULL, "MI2S_DL_HL"},
+ {"MI2S_UL_HL", NULL, "MI2S_TX"},
+ {"PCM_RX_DL_HL", "Switch", "SLIM0_DL_HL"},
+ {"PCM_RX", NULL, "PCM_RX_DL_HL"},
+
+ /* connect to INT4_MI2S_DL_HL since same pcm_id */
+ {"INT0_MI2S_RX_DL_HL", "Switch", "INT4_MI2S_DL_HL"},
+ {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX_DL_HL"},
+ {"INT4_MI2S_RX_DL_HL", "Switch", "INT4_MI2S_DL_HL"},
+ {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_DL_HL"},
+ {"PRI_MI2S_RX_DL_HL", "Switch", "PRI_MI2S_DL_HL"},
+ {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_DL_HL"},
+ {"SEC_MI2S_RX_DL_HL", "Switch", "SEC_MI2S_DL_HL"},
+ {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX_DL_HL"},
+ {"TERT_MI2S_RX_DL_HL", "Switch", "TERT_MI2S_DL_HL"},
+ {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX_DL_HL"},
+
+ {"QUAT_MI2S_RX_DL_HL", "Switch", "QUAT_MI2S_DL_HL"},
+ {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX_DL_HL"},
+ {"MI2S_UL_HL", NULL, "TERT_MI2S_TX"},
+ {"INT3_MI2S_UL_HL", NULL, "INT3_MI2S_TX"},
+ {"TERT_MI2S_UL_HL", NULL, "TERT_MI2S_TX"},
+ {"SEC_I2S_RX", NULL, "SEC_I2S_DL_HL"},
+ {"PRI_MI2S_UL_HL", NULL, "PRI_MI2S_TX"},
+ {"SEC_MI2S_UL_HL", NULL, "SEC_MI2S_TX"},
+ {"SEC_MI2S_RX", NULL, "SEC_MI2S_DL_HL"},
+ {"PRI_MI2S_RX", NULL, "PRI_MI2S_DL_HL"},
+ {"TERT_MI2S_RX", NULL, "TERT_MI2S_DL_HL"},
+ {"QUAT_MI2S_UL_HL", NULL, "QUAT_MI2S_TX"},
+
+ {"PRI_TDM_TX_0_UL_HL", NULL, "PRI_TDM_TX_0"},
+ {"PRI_TDM_TX_1_UL_HL", NULL, "PRI_TDM_TX_1"},
+ {"PRI_TDM_TX_2_UL_HL", NULL, "PRI_TDM_TX_2"},
+ {"PRI_TDM_TX_3_UL_HL", NULL, "PRI_TDM_TX_3"},
+ {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0_DL_HL"},
+ {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1_DL_HL"},
+ {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2_DL_HL"},
+ {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3_DL_HL"},
+ {"SEC_TDM_TX_0_UL_HL", NULL, "SEC_TDM_TX_0"},
+ {"SEC_TDM_TX_1_UL_HL", NULL, "SEC_TDM_TX_1"},
+ {"SEC_TDM_TX_2_UL_HL", NULL, "SEC_TDM_TX_2"},
+ {"SEC_TDM_TX_3_UL_HL", NULL, "SEC_TDM_TX_3"},
+ {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0_DL_HL"},
+ {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1_DL_HL"},
+ {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2_DL_HL"},
+ {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3_DL_HL"},
+ {"TERT_TDM_TX_0_UL_HL", NULL, "TERT_TDM_TX_0"},
+ {"TERT_TDM_TX_1_UL_HL", NULL, "TERT_TDM_TX_1"},
+ {"TERT_TDM_TX_2_UL_HL", NULL, "TERT_TDM_TX_2"},
+ {"TERT_TDM_TX_3_UL_HL", NULL, "TERT_TDM_TX_3"},
+ {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0_DL_HL"},
+ {"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1_DL_HL"},
+ {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2_DL_HL"},
+ {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3_DL_HL"},
+ {"QUAT_TDM_TX_0_UL_HL", NULL, "QUAT_TDM_TX_0"},
+ {"QUAT_TDM_TX_1_UL_HL", NULL, "QUAT_TDM_TX_1"},
+ {"QUAT_TDM_TX_2_UL_HL", NULL, "QUAT_TDM_TX_2"},
+ {"QUAT_TDM_TX_3_UL_HL", NULL, "QUAT_TDM_TX_3"},
+ {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0_DL_HL"},
+ {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1_DL_HL"},
+ {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2_DL_HL"},
+ {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3_DL_HL"},
+
+ {"PRI_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"PRI_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"PRI_TDM_RX_0 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"PRI_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"PRI_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"PRI_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"PRI_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"PRI_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Port Mixer"},
+
+ {"PRI_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"PRI_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"PRI_TDM_RX_1 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"PRI_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"PRI_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"PRI_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"PRI_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"PRI_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1 Port Mixer"},
+
+ {"PRI_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"PRI_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"PRI_TDM_RX_2 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"PRI_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"PRI_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"PRI_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"PRI_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"PRI_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2 Port Mixer"},
+
+ {"PRI_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"PRI_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"PRI_TDM_RX_3 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"PRI_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"PRI_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"PRI_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"PRI_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"PRI_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3 Port Mixer"},
+
+ {"SEC_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"SEC_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"SEC_TDM_RX_0 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"SEC_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SEC_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"SEC_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"SEC_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"SEC_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Port Mixer"},
+
+ {"SEC_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"SEC_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"SEC_TDM_RX_1 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"SEC_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SEC_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"SEC_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"SEC_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"SEC_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1 Port Mixer"},
+
+ {"SEC_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"SEC_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"SEC_TDM_RX_2 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"SEC_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SEC_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"SEC_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"SEC_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"SEC_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2 Port Mixer"},
+
+ {"SEC_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"SEC_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"SEC_TDM_RX_3 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"SEC_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SEC_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"SEC_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"SEC_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"SEC_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"},
+ {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"},
+ {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"},
+ {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"},
+ {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3 Port Mixer"},
+
+ {"TERT_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"TERT_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"TERT_TDM_RX_0 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"TERT_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"TERT_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"TERT_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"TERT_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"TERT_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Port Mixer"},
+
+ {"TERT_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"TERT_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"TERT_TDM_RX_1 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"TERT_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"TERT_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"TERT_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"TERT_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"TERT_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1 Port Mixer"},
+
+ {"TERT_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"TERT_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"TERT_TDM_RX_2 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"TERT_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"TERT_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"TERT_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"TERT_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"TERT_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2 Port Mixer"},
+
+ {"TERT_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"TERT_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"TERT_TDM_RX_3 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"TERT_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"TERT_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"TERT_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"TERT_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"TERT_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3 Port Mixer"},
+
+ {"QUAT_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"QUAT_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"QUAT_TDM_RX_0 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"QUAT_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"QUAT_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"QUAT_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"QUAT_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"QUAT_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"QUAT_TDM_RX_0 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"QUAT_TDM_RX_0 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"QUAT_TDM_RX_0 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"QUAT_TDM_RX_0 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Port Mixer"},
+
+ {"QUAT_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"QUAT_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"QUAT_TDM_RX_1 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"QUAT_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"QUAT_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"QUAT_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"QUAT_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"QUAT_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"QUAT_TDM_RX_1 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"QUAT_TDM_RX_1 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"QUAT_TDM_RX_1 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"QUAT_TDM_RX_1 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Port Mixer"},
+
+ {"QUAT_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"QUAT_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"QUAT_TDM_RX_2 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"QUAT_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"QUAT_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"QUAT_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"QUAT_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"QUAT_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"QUAT_TDM_RX_2 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"QUAT_TDM_RX_2 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"QUAT_TDM_RX_2 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"QUAT_TDM_RX_2 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Port Mixer"},
+
+ {"QUAT_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"QUAT_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"QUAT_TDM_RX_3 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"QUAT_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"QUAT_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"QUAT_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"QUAT_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"QUAT_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"QUAT_TDM_RX_3 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"},
+ {"QUAT_TDM_RX_3 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"},
+ {"QUAT_TDM_RX_3 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"},
+ {"QUAT_TDM_RX_3 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"},
+ {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+ {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Port Mixer"},
+
+ {"INT0_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"INT0_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"INT0_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"INT0_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"INT0_MI2S_RX Port Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"INT0_MI2S_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+ {"INT0_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"INT0_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"INT0_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX Port Mixer"},
+
+ {"INT4_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"INT4_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"INT4_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"INT4_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"INT4_MI2S_RX Port Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"INT4_MI2S_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+ {"INT4_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"INT4_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"INT4_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX Port Mixer"},
+
+ {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"},
+ {"AFE_PCM_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"AFE_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"PCM_RX", NULL, "AFE_PCM_RX Port Mixer"},
+ {"USB_AUDIO_RX Port Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+ {"USB_AUDIO_RX", NULL, "USB_AUDIO_RX Port Mixer"},
+ {"USB_DL_HL", "Switch", "USBAUDIO_DL_HL"},
+ {"USB_AUDIO_RX", NULL, "USB_DL_HL"},
+ {"USBAUDIO_UL_HL", NULL, "USB_AUDIO_TX"},
+
+
+ {"AUX_PCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"AUX_PCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"AUX_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"AUX_PCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"AUX_PCM_RX Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"AUX_PCM_RX", NULL, "AUX_PCM_RX Port Mixer"},
+
+ {"SEC_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"SEC_AUXPCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"SEC_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_RX Port Mixer"},
+
+ {"TERT_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"TERT_AUXPCM_RX Port Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"},
+ {"TERT_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"TERT_AUX_PCM_RX", NULL, "TERT_AUXPCM_RX Port Mixer"},
+
+ {"QUAT_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"QUAT_AUXPCM_RX Port Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"},
+ {"QUAT_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUXPCM_RX Port Mixer"},
+
+ {"Voice Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"},
+ {"Voice Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"Voice Stub Tx Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"Voice Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"},
+ {"Voice Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"Voice Stub Tx Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"Voice Stub Tx Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"},
+ {"Voice Stub Tx Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"},
+ {"Voice Stub Tx Mixer", "MI2S_TX", "MI2S_TX"},
+ {"Voice Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"Voice Stub Tx Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"Voice Stub Tx Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"Voice Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"Voice Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"Voice Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
+ {"Voice Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+ {"Voice Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"Voice Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"},
+
+ {"VoLTE Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"},
+ {"VoLTE Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"VoLTE Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"},
+ {"VoLTE Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"VoLTE Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"VoLTE Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
+ {"VoLTE Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+ {"VoLTE Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"VoLTE Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"VoLTE Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"VoLTE Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"VOLTE_STUB_UL", NULL, "VoLTE Stub Tx Mixer"},
+
+ {"Voice2 Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"},
+ {"Voice2 Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"Voice2 Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"},
+ {"Voice2 Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"Voice2 Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"Voice2 Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
+ {"Voice2 Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+ {"Voice2 Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"Voice2 Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"Voice2 Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"Voice2 Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"VOICE2_STUB_UL", NULL, "Voice2 Stub Tx Mixer"},
+
+ {"STUB_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"STUB_RX Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"STUB_RX Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"STUB_RX", NULL, "STUB_RX Mixer"},
+ {"SLIMBUS_1_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"SLIMBUS_1_RX Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"SLIMBUS_1_RX Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Mixer"},
+ {"AFE_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"SLIMBUS_3_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"SLIMBUS_3_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"SLIMBUS_3_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"SLIMBUS_3_RX", NULL, "SLIMBUS_3_RX_Voice Mixer"},
+
+ {"SLIM_7_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SLIM_7_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"SLIM_7_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SLIM_7_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"SLIM_7_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SLIM_7_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"SLIM_7_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"SLIM_7_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"SLIM_7_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"SLIM_7_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"SLIM_7_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"SLIM_7_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"SLIMBUS_7_RX", NULL, "SLIM_7_RX_Voice Mixer"},
+
+ {"SLIM_8_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SLIM_8_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"SLIM_8_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SLIM_8_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"SLIM_8_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SLIM_8_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"SLIM_8_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"SLIM_8_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+ {"SLIM_8_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+ {"SLIM_8_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"SLIM_8_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"SLIM_8_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"SLIMBUS_8_RX", NULL, "SLIM_8_RX_Voice Mixer"},
+
+ {"SLIMBUS_1_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"SLIMBUS_1_RX Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"SLIMBUS_1_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Port Mixer"},
+ {"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Port Mixer"},
+ {"SLIMBUS_3_RX Port Mixer", "INTERNAL_BT_SCO_RX", "INT_BT_SCO_RX"},
+ {"SLIMBUS_3_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+ {"SLIMBUS_3_RX Port Mixer", "AFE_PCM_RX", "PCM_RX"},
+ {"SLIMBUS_3_RX Port Mixer", "AUX_PCM_RX", "AUX_PCM_RX"},
+ {"SLIMBUS_3_RX Port Mixer", "SLIM_0_RX", "SLIMBUS_0_RX"},
+ {"SLIMBUS_3_RX", NULL, "SLIMBUS_3_RX Port Mixer"},
+
+ {"SLIMBUS_6_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SLIMBUS_6_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"SLIMBUS_6_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"SLIMBUS_6_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+ {"SLIMBUS_6_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"SLIMBUS_6_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"SLIMBUS_6_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"SLIMBUS_6_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+ {"SLIMBUS_6_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"SLIMBUS_6_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"SLIMBUS_6_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"SLIMBUS_6_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"SLIMBUS_6_RX", NULL, "SLIMBUS_6_RX Port Mixer"},
+
+ {"HDMI_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+ {"HDMI", NULL, "HDMI_RX Port Mixer"},
+
+ {"DISPLAY_PORT_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+ {"DISPLAY_PORT", NULL, "DISPLAY_PORT_RX Port Mixer"},
+
+ {"SEC_I2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+ {"SEC_I2S_RX", NULL, "SEC_I2S_RX Port Mixer"},
+
+ {"MI2S_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"MI2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+ {"MI2S_RX", NULL, "MI2S_RX Port Mixer"},
+
+ {"PRI_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"PRI_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"PRI_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"PRI_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"PRI_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"PRI_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"PRI_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"PRI_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Port Mixer"},
+
+ {"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"SEC_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"SEC_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"SEC_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"SEC_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"SEC_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SEC_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Port Mixer"},
+
+ {"TERT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"TERT_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"TERT_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"TERT_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"TERT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"TERT_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Port Mixer"},
+
+ {"QUAT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"QUAT_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"QUAT_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"QUAT_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"QUAT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"QUAT_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"QUAT_MI2S_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"QUAT_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+ {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Port Mixer"},
+
+ /* Backend Enablement */
+
+ {"BE_OUT", NULL, "PRI_I2S_RX"},
+ {"BE_OUT", NULL, "SEC_I2S_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_0_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_1_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_2_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_3_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_4_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_5_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_6_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_7_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_8_RX"},
+ {"BE_OUT", NULL, "USB_AUDIO_RX"},
+ {"BE_OUT", NULL, "HDMI"},
+ {"BE_OUT", NULL, "DISPLAY_PORT"},
+ {"BE_OUT", NULL, "SPDIF_RX"},
+ {"BE_OUT", NULL, "MI2S_RX"},
+ {"BE_OUT", NULL, "QUAT_MI2S_RX"},
+ {"BE_OUT", NULL, "QUIN_MI2S_RX"},
+ {"BE_OUT", NULL, "TERT_MI2S_RX"},
+ {"BE_OUT", NULL, "SEC_MI2S_RX"},
+ {"BE_OUT", NULL, "SEC_MI2S_RX_SD1"},
+ {"BE_OUT", NULL, "PRI_MI2S_RX"},
+ {"BE_OUT", NULL, "INT0_MI2S_RX"},
+ {"BE_OUT", NULL, "INT4_MI2S_RX"},
+ {"BE_OUT", NULL, "INT2_MI2S_RX"},
+ {"BE_OUT", NULL, "INT3_MI2S_RX"},
+ {"BE_OUT", NULL, "INT5_MI2S_RX"},
+ {"BE_OUT", NULL, "INT_BT_SCO_RX"},
+ {"BE_OUT", NULL, "INT_BT_A2DP_RX"},
+ {"BE_OUT", NULL, "INT_FM_RX"},
+ {"BE_OUT", NULL, "PCM_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_3_RX"},
+ {"BE_OUT", NULL, "AUX_PCM_RX"},
+ {"BE_OUT", NULL, "SEC_AUX_PCM_RX"},
+ {"BE_OUT", NULL, "TERT_AUX_PCM_RX"},
+ {"BE_OUT", NULL, "QUAT_AUX_PCM_RX"},
+ {"BE_OUT", NULL, "INT_BT_SCO_RX"},
+ {"BE_OUT", NULL, "INT_FM_RX"},
+ {"BE_OUT", NULL, "PCM_RX"},
+ {"BE_OUT", NULL, "SLIMBUS_3_RX"},
+ {"BE_OUT", NULL, "VOICE_PLAYBACK_TX"},
+ {"BE_OUT", NULL, "VOICE2_PLAYBACK_TX"},
+ {"BE_OUT", NULL, "PRI_TDM_RX_0"},
+ {"BE_OUT", NULL, "PRI_TDM_RX_1"},
+ {"BE_OUT", NULL, "PRI_TDM_RX_2"},
+ {"BE_OUT", NULL, "PRI_TDM_RX_3"},
+ {"BE_OUT", NULL, "SEC_TDM_RX_0"},
+ {"BE_OUT", NULL, "SEC_TDM_RX_1"},
+ {"BE_OUT", NULL, "SEC_TDM_RX_2"},
+ {"BE_OUT", NULL, "SEC_TDM_RX_3"},
+ {"BE_OUT", NULL, "TERT_TDM_RX_0"},
+ {"BE_OUT", NULL, "TERT_TDM_RX_1"},
+ {"BE_OUT", NULL, "TERT_TDM_RX_2"},
+ {"BE_OUT", NULL, "TERT_TDM_RX_3"},
+ {"BE_OUT", NULL, "TERT_TDM_RX_4"},
+ {"BE_OUT", NULL, "QUAT_TDM_RX_0"},
+ {"BE_OUT", NULL, "QUAT_TDM_RX_1"},
+ {"BE_OUT", NULL, "QUAT_TDM_RX_2"},
+ {"BE_OUT", NULL, "QUAT_TDM_RX_3"},
+ {"BE_OUT", NULL, "SEC_MI2S_RX_1"},
+ {"BE_OUT", NULL, "SEC_MI2S_RX_2"},
+ {"BE_OUT", NULL, "SEC_MI2S_RX_3"},
+ {"BE_OUT", NULL, "SEC_MI2S_RX_4"},
+ {"BE_OUT", NULL, "TERT_MI2S_RX_1"},
+ {"BE_OUT", NULL, "TERT_MI2S_RX_2"},
+ {"BE_OUT", NULL, "TERT_MI2S_RX_3"},
+ {"BE_OUT", NULL, "TERT_MI2S_RX_4"},
+ {"BE_OUT", NULL, "QUAT_MI2S_RX_1"},
+ {"BE_OUT", NULL, "QUAT_MI2S_RX_2"},
+ {"BE_OUT", NULL, "QUAT_MI2S_RX_3"},
+ {"BE_OUT", NULL, "QUAT_MI2S_RX_4"},
+ {"BE_OUT", NULL, "PROXY_RX"},
+
+ {"PRI_I2S_TX", NULL, "BE_IN"},
+ {"MI2S_TX", NULL, "BE_IN"},
+ {"QUAT_MI2S_TX", NULL, "BE_IN"},
+ {"QUIN_MI2S_TX", NULL, "BE_IN"},
+ {"PRI_MI2S_TX", NULL, "BE_IN"},
+ {"TERT_MI2S_TX", NULL, "BE_IN"},
+ {"INT0_MI2S_TX", NULL, "BE_IN"},
+ {"INT2_MI2S_TX", NULL, "BE_IN"},
+ {"INT3_MI2S_TX", NULL, "BE_IN"},
+ {"INT4_MI2S_TX", NULL, "BE_IN"},
+ {"INT5_MI2S_TX", NULL, "BE_IN"},
+ {"SEC_MI2S_TX", NULL, "BE_IN"},
+ {"SENARY_MI2S_TX", NULL, "BE_IN" },
+ {"SEC_MI2S_TX_1", NULL, "BE_IN"},
+ {"SEC_MI2S_TX_2", NULL, "BE_IN"},
+ {"SEC_MI2S_TX_3", NULL, "BE_IN"},
+ {"SEC_MI2S_TX_4", NULL, "BE_IN"},
+ {"TERT_MI2S_TX_1", NULL, "BE_IN"},
+ {"TERT_MI2S_TX_2", NULL, "BE_IN"},
+ {"TERT_MI2S_TX_3", NULL, "BE_IN"},
+ {"TERT_MI2S_TX_4", NULL, "BE_IN"},
+ {"QUAT_MI2S_TX_1", NULL, "BE_IN"},
+ {"QUAT_MI2S_TX_2", NULL, "BE_IN"},
+ {"QUAT_MI2S_TX_3", NULL, "BE_IN"},
+ {"QUAT_MI2S_TX_4", NULL, "BE_IN"},
+ {"SLIMBUS_0_TX", NULL, "BE_IN" },
+ {"SLIMBUS_1_TX", NULL, "BE_IN" },
+ {"SLIMBUS_3_TX", NULL, "BE_IN" },
+ {"SLIMBUS_4_TX", NULL, "BE_IN" },
+ {"SLIMBUS_5_TX", NULL, "BE_IN" },
+ {"SLIMBUS_6_TX", NULL, "BE_IN" },
+ {"SLIMBUS_7_TX", NULL, "BE_IN" },
+ {"SLIMBUS_8_TX", NULL, "BE_IN" },
+ {"USB_AUDIO_TX", NULL, "BE_IN" },
+ {"INT_BT_SCO_TX", NULL, "BE_IN"},
+ {"INT_FM_TX", NULL, "BE_IN"},
+ {"PCM_TX", NULL, "BE_IN"},
+ {"BE_OUT", NULL, "SLIMBUS_3_RX"},
+ {"BE_OUT", NULL, "STUB_RX"},
+ {"STUB_TX", NULL, "BE_IN"},
+ {"STUB_1_TX", NULL, "BE_IN"},
+ {"BE_OUT", NULL, "AUX_PCM_RX"},
+ {"AUX_PCM_TX", NULL, "BE_IN"},
+ {"SEC_AUX_PCM_TX", NULL, "BE_IN"},
+ {"TERT_AUX_PCM_TX", NULL, "BE_IN"},
+ {"QUAT_AUX_PCM_TX", NULL, "BE_IN"},
+ {"INCALL_RECORD_TX", NULL, "BE_IN"},
+ {"INCALL_RECORD_RX", NULL, "BE_IN"},
+ {"SLIM0_RX_VI_FB_LCH_MUX", "SLIM4_TX", "SLIMBUS_4_TX"},
+ {"SLIM0_RX_VI_FB_RCH_MUX", "SLIM4_TX", "SLIMBUS_4_TX"},
+ {"PRI_MI2S_RX_VI_FB_MUX", "SENARY_TX", "SENARY_TX"},
+ {"INT4_MI2S_RX_VI_FB_MONO_CH_MUX", "INT5_MI2S_TX", "INT5_MI2S_TX"},
+ {"INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", "INT5_MI2S_TX", "INT5_MI2S_TX"},
+ {"SLIMBUS_0_RX", NULL, "SLIM0_RX_VI_FB_LCH_MUX"},
+ {"SLIMBUS_0_RX", NULL, "SLIM0_RX_VI_FB_RCH_MUX"},
+ {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_VI_FB_MUX"},
+ {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_VI_FB_MONO_CH_MUX"},
+ {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_VI_FB_STEREO_CH_MUX"},
+ {"PRI_TDM_TX_0", NULL, "BE_IN"},
+ {"PRI_TDM_TX_1", NULL, "BE_IN"},
+ {"PRI_TDM_TX_2", NULL, "BE_IN"},
+ {"PRI_TDM_TX_3", NULL, "BE_IN"},
+ {"SEC_TDM_TX_0", NULL, "BE_IN"},
+ {"SEC_TDM_TX_1", NULL, "BE_IN"},
+ {"SEC_TDM_TX_2", NULL, "BE_IN"},
+ {"SEC_TDM_TX_3", NULL, "BE_IN"},
+ {"TERT_TDM_TX_0", NULL, "BE_IN"},
+ {"TERT_TDM_TX_1", NULL, "BE_IN"},
+ {"TERT_TDM_TX_2", NULL, "BE_IN"},
+ {"TERT_TDM_TX_3", NULL, "BE_IN"},
+ {"QUAT_TDM_TX_0", NULL, "BE_IN"},
+ {"QUAT_TDM_TX_1", NULL, "BE_IN"},
+ {"QUAT_TDM_TX_2", NULL, "BE_IN"},
+ {"QUAT_TDM_TX_3", NULL, "BE_IN"},
+ {"PROXY_TX", NULL, "BE_IN"},
+};
+
+static int msm_pcm_routing_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&routing_lock);
+ msm_bedais[be_id].sample_rate = params_rate(params);
+ msm_bedais[be_id].channel = params_channels(params);
+ msm_bedais[be_id].format = params_format(params);
+ pr_debug("%s: BE Sample Rate (%d) format (%d) be_id %d\n",
+ __func__, msm_bedais[be_id].sample_rate,
+ msm_bedais[be_id].format, be_id);
+ mutex_unlock(&routing_lock);
+ return 0;
+}
+
+static int msm_pcm_routing_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+ int i, session_type, path_type, topology;
+ struct msm_pcm_routing_bdai_data *bedai;
+ struct msm_pcm_routing_fdai_data *fdai;
+
+ pr_debug("%s: substream->pcm->id:%s\n",
+ __func__, substream->pcm->id);
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+ bedai = &msm_bedais[be_id];
+ session_type = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ 0 : 1);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ path_type = ADM_PATH_PLAYBACK;
+ else
+ path_type = ADM_PATH_LIVE_REC;
+
+ mutex_lock(&routing_lock);
+ for_each_set_bit(i, &bedai->fe_sessions[0], MSM_FRONTEND_DAI_MAX) {
+ if (!is_mm_lsm_fe_id(i))
+ continue;
+ fdai = &fe_dai_map[i][session_type];
+ if (fdai->strm_id != INVALID_SESSION) {
+ int idx;
+ int port_id;
+ unsigned long copp =
+ session_copp_map[i][session_type][be_id];
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+ if (test_bit(idx, &copp))
+ break;
+ fdai->be_srate = bedai->sample_rate;
+ port_id = bedai->port_id;
+ topology = adm_get_topology_for_port_copp_idx(port_id,
+ idx);
+ adm_close(bedai->port_id, fdai->perf_mode, idx);
+ pr_debug("%s: copp:%ld,idx bit fe:%d, type:%d,be:%d topology=0x%x\n",
+ __func__, copp, i, session_type, be_id,
+ topology);
+ clear_bit(idx,
+ &session_copp_map[i][session_type][be_id]);
+ if ((fdai->perf_mode == LEGACY_PCM_MODE) &&
+ (bedai->passthr_mode[i] == LEGACY_PCM))
+ msm_pcm_routing_deinit_pp(bedai->port_id,
+ topology);
+ }
+ }
+
+ bedai->active = 0;
+ bedai->sample_rate = 0;
+ bedai->channel = 0;
+ mutex_unlock(&routing_lock);
+
+ return 0;
+}
+
+static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+ int i, path_type, session_type, topology;
+ struct msm_pcm_routing_bdai_data *bedai;
+ u32 channels, sample_rate;
+ uint16_t bits_per_sample = 16, voc_path_type;
+ struct msm_pcm_routing_fdai_data *fdai;
+ u32 session_id;
+ struct media_format_info voc_be_media_format;
+ bool is_lsm;
+ u32 copp_token = 0;
+
+ pr_debug("%s: substream->pcm->id:%s\n",
+ __func__, substream->pcm->id);
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+ bedai = &msm_bedais[be_id];
+
+ mutex_lock(&routing_lock);
+ if (bedai->active == 1)
+ goto done; /* Ignore prepare if back-end already active */
+
+ /* AFE port is not active at this point. However, still
+ * go ahead setting active flag under the notion that
+ * QDSP6 is able to handle ADM starting before AFE port
+ * is started.
+ */
+ bedai->active = 1;
+
+ for_each_set_bit(i, &bedai->fe_sessions[0], MSM_FRONTEND_DAI_MAX) {
+ if (!(is_mm_lsm_fe_id(i) &&
+ route_check_fe_id_adm_support(i)))
+ continue;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (bedai->passthr_mode[i] != LEGACY_PCM)
+ path_type = ADM_PATH_COMPRESSED_RX;
+ else
+ path_type = ADM_PATH_PLAYBACK;
+ session_type = SESSION_TYPE_RX;
+ } else {
+ path_type = ADM_PATH_LIVE_REC;
+ session_type = SESSION_TYPE_TX;
+ }
+
+ is_lsm = (i >= MSM_FRONTEND_DAI_LSM1) &&
+ (i <= MSM_FRONTEND_DAI_LSM8);
+ fdai = &fe_dai_map[i][session_type];
+ if (fdai->strm_id != INVALID_SESSION) {
+ int app_type, app_type_idx, copp_idx, acdb_dev_id;
+ if (session_type == SESSION_TYPE_TX &&
+ fdai->be_srate &&
+ (fdai->be_srate != bedai->sample_rate)) {
+ pr_debug("%s: flush strm %d diff BE rates\n",
+ __func__,
+ fdai->strm_id);
+
+ if (fdai->event_info.event_func)
+ fdai->event_info.event_func(
+ MSM_PCM_RT_EVT_BUF_RECFG,
+ fdai->event_info.priv_data);
+ fdai->be_srate = 0; /* might not need it */
+ }
+ bits_per_sample = msm_routing_get_bit_width(
+ bedai->format);
+
+ app_type =
+ fe_dai_app_type_cfg[i][session_type][be_id].app_type;
+ if (app_type && is_lsm) {
+ app_type_idx =
+ msm_pcm_routing_get_lsm_app_type_idx(app_type);
+ sample_rate =
+ fe_dai_app_type_cfg[i][session_type][be_id]
+ .sample_rate;
+ bits_per_sample =
+ lsm_app_type_cfg[app_type_idx].bit_width;
+ } else if (app_type) {
+ app_type_idx =
+ msm_pcm_routing_get_app_type_idx(app_type);
+ sample_rate =
+ fe_dai_app_type_cfg[i][session_type]
+ [be_id].sample_rate;
+ bits_per_sample =
+ app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[i][session_type]
+ [be_id].copp_token;
+ } else
+ sample_rate = bedai->sample_rate;
+ /*
+ * check if ADM needs to be configured with different
+ * channel mapping than backend
+ */
+ if (!bedai->adm_override_ch)
+ channels = bedai->channel;
+ else
+ channels = bedai->adm_override_ch;
+ acdb_dev_id =
+ fe_dai_app_type_cfg[i][session_type][be_id].acdb_dev_id;
+ topology = msm_routing_get_adm_topology(i, session_type,
+ be_id);
+ copp_idx = adm_open(bedai->port_id, path_type,
+ sample_rate, channels, topology,
+ fdai->perf_mode, bits_per_sample,
+ app_type, acdb_dev_id, copp_token);
+ if ((copp_idx < 0) ||
+ (copp_idx >= MAX_COPPS_PER_PORT)) {
+ pr_err("%s: adm open failed\n", __func__);
+ mutex_unlock(&routing_lock);
+ return -EINVAL;
+ }
+ pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n",
+ __func__, i, session_type, be_id);
+ set_bit(copp_idx,
+ &session_copp_map[i][session_type][be_id]);
+
+ if (msm_is_resample_needed(
+ sample_rate,
+ bedai->sample_rate))
+ adm_copp_mfc_cfg(
+ bedai->port_id, copp_idx,
+ bedai->sample_rate);
+
+ msm_pcm_routing_build_matrix(i, session_type, path_type,
+ fdai->perf_mode,
+ bedai->passthr_mode[i]);
+ if ((fdai->perf_mode == LEGACY_PCM_MODE) &&
+ (bedai->passthr_mode[i] == LEGACY_PCM))
+ msm_pcm_routing_cfg_pp(bedai->port_id, copp_idx,
+ topology, channels);
+ }
+ }
+
+ for_each_set_bit(i, &bedai->fe_sessions[0], MSM_FRONTEND_DAI_MAX) {
+ session_id = msm_pcm_routing_get_voc_sessionid(i);
+ if (session_id) {
+ pr_debug("%s voice session_id: 0x%x\n", __func__,
+ session_id);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ voc_path_type = RX_PATH;
+ else
+ voc_path_type = TX_PATH;
+
+ voc_set_route_flag(session_id, voc_path_type, 1);
+
+ memset(&voc_be_media_format, 0,
+ sizeof(struct media_format_info));
+
+ voc_be_media_format.port_id = bedai->port_id;
+ voc_be_media_format.num_channels = bedai->channel;
+ voc_be_media_format.sample_rate = bedai->sample_rate;
+ voc_be_media_format.bits_per_sample = bedai->format;
+ /* Defaulting this to 1 for voice call usecases */
+ voc_be_media_format.channel_mapping[0] = 1;
+
+ voc_set_device_config(session_id, voc_path_type,
+ &voc_be_media_format);
+
+ if (voc_get_route_flag(session_id, RX_PATH) &&
+ voc_get_route_flag(session_id, TX_PATH))
+ voc_enable_device(session_id);
+ }
+ }
+
+ /* Check if backend is an external ec ref port and set as needed */
+ if (unlikely(bedai->port_id == voc_get_ext_ec_ref_port_id())) {
+
+ memset(&voc_be_media_format, 0,
+ sizeof(struct media_format_info));
+
+ /* Get format info for ec ref port from msm_bedais[] */
+ voc_be_media_format.port_id = bedai->port_id;
+ voc_be_media_format.num_channels = bedai->channel;
+ voc_be_media_format.bits_per_sample = bedai->format;
+ voc_be_media_format.sample_rate = bedai->sample_rate;
+ /* Defaulting this to 1 for voice call usecases */
+ voc_be_media_format.channel_mapping[0] = 1;
+ voc_set_ext_ec_ref_media_fmt_info(&voc_be_media_format);
+ pr_debug("%s: EC Ref media format info set to port_id=%d, num_channels=%d, bits_per_sample=%d, sample_rate=%d\n",
+ __func__, voc_be_media_format.port_id,
+ voc_be_media_format.num_channels,
+ voc_be_media_format.bits_per_sample,
+ voc_be_media_format.sample_rate);
+ }
+
+done:
+ mutex_unlock(&routing_lock);
+
+ return 0;
+}
+
+static int msm_routing_send_device_pp_params(int port_id, int copp_idx,
+ int fe_id)
+{
+ int topo_id, be_idx;
+ unsigned long pp_config = 0;
+ bool mute_on;
+ int latency;
+ bool compr_passthr_mode = true;
+
+ pr_debug("%s: port_id %d, copp_idx %d\n", __func__, port_id, copp_idx);
+
+ if (port_id != HDMI_RX && port_id != DISPLAY_PORT_RX) {
+ pr_err("%s: Device pp params on invalid port %d\n",
+ __func__, port_id);
+ return -EINVAL;
+ }
+
+ for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) {
+ if (port_id == msm_bedais[be_idx].port_id)
+ break;
+ }
+
+ if (be_idx >= MSM_BACKEND_DAI_MAX) {
+ pr_debug("%s: Invalid be id %d\n", __func__, be_idx);
+ return -EINVAL;
+ }
+
+ topo_id = adm_get_topology_for_port_copp_idx(port_id, copp_idx);
+ if (topo_id != COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY) {
+ pr_err("%s: Invalid passthrough topology 0x%x\n",
+ __func__, topo_id);
+ return -EINVAL;
+ }
+
+ if ((msm_bedais[be_idx].passthr_mode[fe_id] == LEGACY_PCM) ||
+ (msm_bedais[be_idx].passthr_mode[fe_id] == LISTEN))
+ compr_passthr_mode = false;
+
+ pp_config = msm_bedais_pp_params[be_idx].pp_params_config;
+ if (test_bit(ADM_PP_PARAM_MUTE_BIT, &pp_config)) {
+ pr_debug("%s: ADM_PP_PARAM_MUTE\n", __func__);
+ clear_bit(ADM_PP_PARAM_MUTE_BIT, &pp_config);
+ mute_on = msm_bedais_pp_params[be_idx].mute_on;
+ if ((msm_bedais[be_idx].active) && compr_passthr_mode)
+ adm_send_compressed_device_mute(port_id,
+ copp_idx,
+ mute_on);
+ }
+ if (test_bit(ADM_PP_PARAM_LATENCY_BIT, &pp_config)) {
+ pr_debug("%s: ADM_PP_PARAM_LATENCY\n", __func__);
+ clear_bit(ADM_PP_PARAM_LATENCY_BIT,
+ &pp_config);
+ latency = msm_bedais_pp_params[be_idx].latency;
+ if ((msm_bedais[be_idx].active) && compr_passthr_mode)
+ adm_send_compressed_device_latency(port_id,
+ copp_idx,
+ latency);
+ }
+ return 0;
+}
+
+static bool msm_pcm_routing_test_pp_param(int be_idx, long param_bit)
+{
+ return test_bit(param_bit,
+ &msm_bedais_pp_params[be_idx].pp_params_config);
+}
+
+static void msm_routing_set_pp_param(long param_bit, int value)
+{
+ int be_idx;
+
+ if (value) {
+ for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++)
+ set_bit(param_bit,
+ &msm_bedais_pp_params[be_idx].
+ pp_params_config);
+ } else {
+ for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++)
+ clear_bit(param_bit,
+ &msm_bedais_pp_params[be_idx].
+ pp_params_config);
+ }
+}
+
+static int msm_routing_put_device_pp_params_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int pp_id = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.integer.value[1];
+ int port_id = 0;
+ int be_idx, i, topo_id, idx;
+ bool mute;
+ int latency;
+ bool compr_passthr_mode = true;
+
+ pr_debug("%s: pp_id: 0x%x\n", __func__, pp_id);
+
+ if (pp_id == ADM_PP_PARAM_LIMITER_ID) {
+ msm_routing_set_pp_param(ADM_PP_PARAM_LIMITER_BIT, value);
+ goto done;
+ }
+
+ for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) {
+ port_id = msm_bedais[be_idx].port_id;
+ if (port_id == HDMI_RX || port_id == DISPLAY_PORT_RX)
+ break;
+ }
+
+ if (be_idx >= MSM_BACKEND_DAI_MAX) {
+ pr_debug("%s: Invalid be id %d\n", __func__, be_idx);
+ return -EINVAL;
+ }
+
+ for_each_set_bit(i, &msm_bedais[be_idx].fe_sessions[0],
+ MSM_FRONTEND_DAI_MM_SIZE) {
+ if ((msm_bedais[be_idx].passthr_mode[i] == LEGACY_PCM) ||
+ (msm_bedais[be_idx].passthr_mode[i] == LISTEN))
+ compr_passthr_mode = false;
+
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+ unsigned long copp =
+ session_copp_map[i]
+ [SESSION_TYPE_RX][be_idx];
+ if (!test_bit(idx, &copp))
+ continue;
+ topo_id = adm_get_topology_for_port_copp_idx(port_id,
+ idx);
+ if (topo_id != COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY)
+ continue;
+ pr_debug("%s: port: 0x%x, copp %ld, be active: %d, passt: %d\n",
+ __func__, port_id, copp, msm_bedais[be_idx].active,
+ msm_bedais[be_idx].passthr_mode[i]);
+ switch (pp_id) {
+ case ADM_PP_PARAM_MUTE_ID:
+ pr_debug("%s: ADM_PP_PARAM_MUTE\n", __func__);
+ mute = value ? true : false;
+ msm_bedais_pp_params[be_idx].mute_on = mute;
+ set_bit(ADM_PP_PARAM_MUTE_BIT,
+ &msm_bedais_pp_params[be_idx].pp_params_config);
+ if ((msm_bedais[be_idx].active) && compr_passthr_mode)
+ adm_send_compressed_device_mute(port_id,
+ idx, mute);
+ break;
+ case ADM_PP_PARAM_LATENCY_ID:
+ pr_debug("%s: ADM_PP_PARAM_LATENCY\n", __func__);
+ msm_bedais_pp_params[be_idx].latency = value;
+ set_bit(ADM_PP_PARAM_LATENCY_BIT,
+ &msm_bedais_pp_params[be_idx].pp_params_config);
+ latency = msm_bedais_pp_params[be_idx].latency = value;
+ if ((msm_bedais[be_idx].active) && compr_passthr_mode)
+ adm_send_compressed_device_latency(port_id,
+ idx, latency);
+ break;
+ default:
+ pr_info("%s, device pp param %d not supported\n",
+ __func__, pp_id);
+ break;
+ }
+ }
+ }
+done:
+ return 0;
+}
+
+static int msm_routing_get_device_pp_params_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s:msm_routing_get_device_pp_params_mixer", __func__);
+ return 0;
+}
+
+static const struct snd_kcontrol_new device_pp_params_mixer_controls[] = {
+ SOC_SINGLE_MULTI_EXT("Device PP Params", SND_SOC_NOPM, 0, 0xFFFFFFFF,
+ 0, 3, msm_routing_get_device_pp_params_mixer,
+ msm_routing_put_device_pp_params_mixer),
+};
+
+static int msm_aptx_dec_license_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ core_get_license_status(ASM_MEDIA_FMT_APTX);
+ pr_debug("%s: status %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_aptx_dec_license_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int32_t status = 0;
+
+ status = core_set_license(ucontrol->value.integer.value[0],
+ APTX_CLASSIC_DEC_LICENSE_ID);
+ pr_debug("%s: status %d\n", __func__, status);
+ return status;
+}
+
+static const struct snd_kcontrol_new aptx_dec_license_controls[] = {
+ SOC_SINGLE_EXT("APTX Dec License", SND_SOC_NOPM, 0,
+ 0xFFFF, 0, msm_aptx_dec_license_control_get,
+ msm_aptx_dec_license_control_put),
+};
+
+static int msm_routing_be_dai_name_table_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(be_dai_name_table);
+ return 0;
+}
+
+static int msm_routing_be_dai_name_table_tlv_get(unsigned int __user *bytes,
+ unsigned int size)
+{
+ int i;
+ int ret;
+
+ if (size < sizeof(be_dai_name_table)) {
+ pr_err("%s: invalid size %d requested, returning\n",
+ __func__, size);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /*
+ * Fill be_dai_name_table from msm_bedais table to reduce code changes
+ * needed when adding new backends
+ */
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ be_dai_name_table[i].be_id = i;
+ strlcpy(be_dai_name_table[i].be_name,
+ msm_bedais[i].name,
+ LPASS_BE_NAME_MAX_LENGTH);
+ }
+
+ ret = copy_to_user(bytes, &be_dai_name_table,
+ sizeof(be_dai_name_table));
+ if (ret) {
+ pr_err("%s: failed to copy be_dai_name_table\n", __func__);
+ ret = -EFAULT;
+ }
+
+done:
+ return ret;
+}
+
+static const struct snd_kcontrol_new
+ msm_routing_be_dai_name_table_mixer_controls[] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+ .info = msm_routing_be_dai_name_table_info,
+ .name = "Backend DAI Name Table",
+ .tlv.c = snd_soc_bytes_tlv_callback,
+ .private_value = (unsigned long) &(struct soc_bytes_ext) {
+ .max = sizeof(be_dai_name_table),
+ .get = msm_routing_be_dai_name_table_tlv_get,
+ }
+ },
+};
+
+static int msm_routing_stereo_channel_reverse_control_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = swap_ch;
+ pr_debug("%s: Swap channel value: %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_stereo_channel_reverse_control_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i, idx, be_index, port_id;
+ int ret = 0;
+ unsigned long copp;
+
+ pr_debug("%s Swap channel value:%ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ swap_ch = ucontrol->value.integer.value[0];
+
+ mutex_lock(&routing_lock);
+ for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) {
+ port_id = msm_bedais[be_index].port_id;
+ if (!msm_bedais[be_index].active)
+ continue;
+
+ for_each_set_bit(i, &msm_bedais[be_index].fe_sessions[0],
+ MSM_FRONTEND_DAI_MM_SIZE) {
+ copp = session_copp_map[i][SESSION_TYPE_RX][be_index];
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+ if (!test_bit(idx, &copp))
+ continue;
+
+ pr_debug("%s: swap channel control of portid:%d, coppid:%d\n",
+ __func__, port_id, idx);
+ ret = adm_swap_speaker_channels(
+ port_id, idx,
+ msm_bedais[be_index].sample_rate,
+ swap_ch);
+ if (ret) {
+ pr_err("%s:Swap_channel failed, err=%d\n",
+ __func__, ret);
+ goto done;
+ }
+ }
+ }
+ }
+done:
+ mutex_unlock(&routing_lock);
+ return ret;
+}
+
+static const struct snd_kcontrol_new stereo_channel_reverse_control[] = {
+ SOC_SINGLE_EXT("Swap channel", SND_SOC_NOPM, 0,
+ 1, 0, msm_routing_stereo_channel_reverse_control_get,
+ msm_routing_stereo_channel_reverse_control_put),
+};
+
+static int msm_routing_instance_id_support_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int msm_routing_instance_id_support_put(
+ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ bool supported = ucontrol->value.integer.value[0] ? true : false;
+
+ q6common_update_instance_id_support(supported);
+
+ return 0;
+}
+
+static int msm_routing_instance_id_support_get(
+ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ bool supported = false;
+
+ supported = q6common_is_instance_id_supported();
+ ucontrol->value.integer.value[0] = supported ? 1 : 0;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new
+ msm_routing_feature_support_mixer_controls[] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE,
+ .info = msm_routing_instance_id_support_info,
+ .name = "Instance ID Support",
+ .put = msm_routing_instance_id_support_put,
+ .get = msm_routing_instance_id_support_get,
+ },
+};
+
+static struct snd_pcm_ops msm_routing_pcm_ops = {
+ .hw_params = msm_pcm_routing_hw_params,
+ .close = msm_pcm_routing_close,
+ .prepare = msm_pcm_routing_prepare,
+};
+
+/**
+ * msm_pcm_routing_set_channel_mixer_runtime - apply channel mixer
+ * setting during runtime.
+ *
+ * @be_id: backend index
+ * @session_id: session index
+ * @session_type: session type
+ * @params: parameters for channel mixer
+ *
+ * Retuen: 0 for success, else error
+ */
+int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id,
+ int session_type,
+ struct msm_pcm_channel_mixer *params)
+{
+ int i, j, rc = 0;
+ struct adm_pspd_param_data_t data;
+ struct audproc_chmixer_param_coeff chmixer_cfg;
+ uint16_t variable_payload = 0;
+ char *adm_params = NULL;
+ int port_id, copp_idx = 0;
+ uint32_t params_length = 0;
+ uint16_t *dst_gain_ptr = NULL;
+
+ be_id--;
+ if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: invalid backend id %d\n", __func__,
+ be_id);
+ return -EINVAL;
+ }
+
+ memset(&data, 0, sizeof(struct adm_pspd_param_data_t));
+ memset(&chmixer_cfg, 0, sizeof(struct audproc_chmixer_param_coeff));
+ port_id = msm_bedais[be_id].port_id;
+ copp_idx = adm_get_default_copp_idx(port_id);
+ pr_debug("%s: port_id - %d, copp_idx %d session id - %d\n",
+ __func__, port_id, copp_idx, session_id);
+
+ if ((params->input_channel < 0) ||
+ (params->input_channel > ADM_MAX_CHANNELS)) {
+ pr_err("%s: invalid input channel %d\n", __func__,
+ params->input_channel);
+ return -EINVAL;
+ }
+
+ if ((params->output_channel < 0) ||
+ (params->output_channel > ADM_MAX_CHANNELS)) {
+ pr_err("%s: invalid output channel %d\n", __func__,
+ params->output_channel);
+ return -EINVAL;
+ }
+ variable_payload = params->output_channel * sizeof(uint16_t)+
+ params->input_channel * sizeof(uint16_t) +
+ params->output_channel *
+ params->input_channel * sizeof(uint16_t);
+ variable_payload = roundup(variable_payload, 4);
+
+ params_length = variable_payload +
+ sizeof(struct adm_pspd_param_data_t) +
+ sizeof(struct audproc_chmixer_param_coeff);
+ adm_params = kzalloc(params_length, GFP_KERNEL);
+ if (!adm_params)
+ return -ENOMEM;
+
+ data.module_id = AUDPROC_MODULE_ID_CHMIXER;
+ data.param_id = AUDPROC_CHMIXER_PARAM_ID_COEFF;
+ data.param_size = sizeof(struct audproc_chmixer_param_coeff) +
+ variable_payload;
+ data.reserved = 0;
+ memcpy((u8 *)adm_params, &data, sizeof(struct adm_pspd_param_data_t));
+
+ chmixer_cfg.index = params->rule;
+ chmixer_cfg.num_output_channels = params->output_channel;
+ chmixer_cfg.num_input_channels = params->input_channel;
+ memcpy(((u8 *)adm_params +
+ sizeof(struct adm_pspd_param_data_t)),
+ &chmixer_cfg, sizeof(struct audproc_chmixer_param_coeff));
+
+ memcpy(((u8 *)adm_params +
+ sizeof(struct adm_pspd_param_data_t) +
+ sizeof(struct audproc_chmixer_param_coeff)),
+ params->out_ch_map,
+ params->output_channel * sizeof(uint16_t));
+ memcpy(((u8 *)adm_params +
+ sizeof(struct adm_pspd_param_data_t) +
+ sizeof(struct audproc_chmixer_param_coeff) +
+ params->output_channel * sizeof(uint16_t)),
+ params->in_ch_map,
+ params->input_channel * sizeof(uint16_t));
+
+ dst_gain_ptr = (uint16_t *) ((u8 *)adm_params +
+ sizeof(struct adm_pspd_param_data_t) +
+ sizeof(struct audproc_chmixer_param_coeff) +
+ (params->output_channel * sizeof(uint16_t)) +
+ (params->input_channel * sizeof(uint16_t)));
+
+ for (i = 0; i < params->output_channel; i++) {
+ for (j = 0; j < params->input_channel; j++) {
+ *dst_gain_ptr = (uint16_t) params->channel_weight[i][j];
+ pr_debug("%s: ptr[%d][%d] = %d\n",
+ __func__, i, j, *dst_gain_ptr);
+ dst_gain_ptr++;
+ }
+ }
+
+ if (params_length) {
+ rc = adm_set_pspd_matrix_params(port_id,
+ copp_idx,
+ session_id,
+ adm_params,
+ params_length,
+ session_type);
+ if (rc) {
+ pr_err("%s: send params failed rc=%d\n", __func__, rc);
+ rc = -EINVAL;
+ }
+ }
+
+ kfree(adm_params);
+ return rc;
+}
+EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_runtime);
+
+int msm_routing_set_downmix_control_data(int be_id, int session_id,
+ struct asm_stream_pan_ctrl_params *dnmix_param)
+{
+ int i, rc = 0;
+ struct adm_pspd_param_data_t data;
+ struct audproc_chmixer_param_coeff dnmix_cfg = {0,};
+ uint16_t variable_payload = 0;
+ char *adm_params = NULL;
+ int port_id, copp_idx = 0;
+ uint32_t params_length = 0;
+ uint16_t ii;
+ uint16_t *dst_gain_ptr = NULL;
+
+ if (be_id < MSM_BACKEND_DAI_PRI_I2S_RX ||
+ be_id >= MSM_BACKEND_DAI_MAX) {
+ rc = -EINVAL;
+ return rc;
+ }
+ port_id = msm_bedais[be_id].port_id;
+ copp_idx = adm_get_default_copp_idx(port_id);
+ pr_debug("%s: port_id - %d, copp_idx %d session id - %d\n",
+ __func__, port_id, copp_idx, session_id);
+
+ variable_payload = dnmix_param->num_output_channels * sizeof(uint16_t)+
+ dnmix_param->num_input_channels * sizeof(uint16_t) +
+ dnmix_param->num_output_channels *
+ dnmix_param->num_input_channels * sizeof(uint16_t);
+ i = (variable_payload % sizeof(uint32_t));
+ variable_payload += (i == 0) ? 0 : sizeof(uint32_t) - i;
+
+ params_length = variable_payload +
+ sizeof(struct adm_pspd_param_data_t) +
+ sizeof(struct audproc_chmixer_param_coeff);
+ adm_params = kzalloc(params_length, GFP_KERNEL);
+ if (!adm_params) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ data.module_id = AUDPROC_MODULE_ID_CHMIXER;
+ data.param_id = AUDPROC_CHMIXER_PARAM_ID_COEFF;
+ data.param_size = sizeof(struct audproc_chmixer_param_coeff) +
+ variable_payload;
+ data.reserved = 0;
+ memcpy((u8 *)adm_params, &data, sizeof(struct adm_pspd_param_data_t));
+
+ dnmix_cfg.index = 0;
+ dnmix_cfg.num_output_channels = dnmix_param->num_output_channels;
+ dnmix_cfg.num_input_channels = dnmix_param->num_input_channels;
+ memcpy(((u8 *)adm_params +
+ sizeof(struct adm_pspd_param_data_t)),
+ &dnmix_cfg, sizeof(struct audproc_chmixer_param_coeff));
+
+ memcpy(((u8 *)adm_params +
+ sizeof(struct adm_pspd_param_data_t) +
+ sizeof(struct audproc_chmixer_param_coeff)),
+ dnmix_param->output_channel_map,
+ dnmix_param->num_output_channels * sizeof(uint16_t));
+ memcpy(((u8 *)adm_params +
+ sizeof(struct adm_pspd_param_data_t) +
+ sizeof(struct audproc_chmixer_param_coeff) +
+ dnmix_param->num_output_channels * sizeof(uint16_t)),
+ dnmix_param->input_channel_map,
+ dnmix_param->num_input_channels * sizeof(uint16_t));
+
+ dst_gain_ptr = (uint16_t *) ((u8 *)adm_params +
+ sizeof(struct adm_pspd_param_data_t) +
+ sizeof(struct audproc_chmixer_param_coeff) +
+ (dnmix_param->num_output_channels * sizeof(uint16_t)) +
+ (dnmix_param->num_input_channels * sizeof(uint16_t)));
+ for (ii = 0; ii < dnmix_param->num_output_channels *
+ dnmix_param->num_input_channels; ii++)
+ dst_gain_ptr[ii] = (uint16_t) dnmix_param->gain[ii];
+
+ if (params_length) {
+ rc = adm_set_pspd_matrix_params(port_id,
+ copp_idx,
+ session_id,
+ adm_params,
+ params_length,
+ ADM_MATRIX_ID_AUDIO_RX);
+ if (rc) {
+ pr_err("%s: send params failed rc=%d\n", __func__, rc);
+ rc = -EINVAL;
+ }
+ }
+end:
+ kfree(adm_params);
+ return rc;
+}
+
+
+/* Not used but frame seems to require it */
+static int msm_routing_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_dapm_new_controls(&platform->component.dapm, msm_qdsp6_widgets,
+ ARRAY_SIZE(msm_qdsp6_widgets));
+ snd_soc_dapm_add_routes(&platform->component.dapm, intercon,
+ ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(platform->component.dapm.card);
+
+ snd_soc_add_platform_controls(platform, lsm_controls,
+ ARRAY_SIZE(lsm_controls));
+
+ snd_soc_add_platform_controls(platform, aanc_slim_0_rx_mux,
+ ARRAY_SIZE(aanc_slim_0_rx_mux));
+
+ snd_soc_add_platform_controls(platform, msm_voc_session_controls,
+ ARRAY_SIZE(msm_voc_session_controls));
+
+ snd_soc_add_platform_controls(platform, app_type_cfg_controls,
+ ARRAY_SIZE(app_type_cfg_controls));
+
+ snd_soc_add_platform_controls(platform, lsm_app_type_cfg_controls,
+ ARRAY_SIZE(lsm_app_type_cfg_controls));
+
+ snd_soc_add_platform_controls(platform,
+ stereo_to_custom_stereo_controls,
+ ARRAY_SIZE(stereo_to_custom_stereo_controls));
+
+ snd_soc_add_platform_controls(platform, ec_ref_param_controls,
+ ARRAY_SIZE(ec_ref_param_controls));
+
+ snd_soc_add_platform_controls(platform, channel_mixer_controls,
+ ARRAY_SIZE(channel_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, native_mode_controls,
+ ARRAY_SIZE(native_mode_controls));
+
+ msm_qti_pp_add_controls(platform);
+
+ msm_dts_srs_tm_add_controls(platform);
+
+ msm_dolby_dap_add_controls(platform);
+
+ snd_soc_add_platform_controls(platform,
+ use_ds1_or_ds2_controls,
+ ARRAY_SIZE(use_ds1_or_ds2_controls));
+
+ snd_soc_add_platform_controls(platform,
+ device_pp_params_mixer_controls,
+ ARRAY_SIZE(device_pp_params_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ msm_routing_be_dai_name_table_mixer_controls,
+ ARRAY_SIZE(msm_routing_be_dai_name_table_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, msm_source_tracking_controls,
+ ARRAY_SIZE(msm_source_tracking_controls));
+ snd_soc_add_platform_controls(platform, adm_channel_config_controls,
+ ARRAY_SIZE(adm_channel_config_controls));
+
+ snd_soc_add_platform_controls(platform, aptx_dec_license_controls,
+ ARRAY_SIZE(aptx_dec_license_controls));
+ snd_soc_add_platform_controls(platform, stereo_channel_reverse_control,
+ ARRAY_SIZE(stereo_channel_reverse_control));
+ snd_soc_add_platform_controls(
+ platform, msm_routing_feature_support_mixer_controls,
+ ARRAY_SIZE(msm_routing_feature_support_mixer_controls));
+
+ return 0;
+}
+
+int msm_routing_pcm_new(struct snd_soc_pcm_runtime *runtime)
+{
+ return msm_pcm_routing_hwdep_new(runtime, msm_bedais);
+}
+
+void msm_routing_pcm_free(struct snd_pcm *pcm)
+{
+ msm_pcm_routing_hwdep_free(pcm);
+}
+
+static struct snd_soc_platform_driver msm_soc_routing_platform = {
+ .ops = &msm_routing_pcm_ops,
+ .probe = msm_routing_probe,
+ .pcm_new = msm_routing_pcm_new,
+ .pcm_free = msm_routing_pcm_free,
+};
+
+static int msm_routing_pcm_probe(struct platform_device *pdev)
+{
+
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_routing_platform);
+}
+
+static int msm_routing_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_pcm_routing_dt_match[] = {
+ {.compatible = "qcom,msm-pcm-routing"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_pcm_routing_dt_match);
+
+static struct platform_driver msm_routing_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-routing",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_pcm_routing_dt_match,
+ },
+ .probe = msm_routing_pcm_probe,
+ .remove = msm_routing_pcm_remove,
+};
+
+int msm_routing_check_backend_enabled(int fedai_id)
+{
+ int i;
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return 0;
+ }
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (test_bit(fedai_id, &msm_bedais[i].fe_sessions[0]))
+ return msm_bedais[i].active;
+ }
+ return 0;
+}
+
+static int msm_routing_set_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ pr_debug("%s\n", __func__);
+
+ ret = cal_utils_set_cal(data_size, data, cal_data, 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static void msm_routing_delete_cal_data(void)
+{
+ pr_debug("%s\n", __func__);
+
+ cal_utils_destroy_cal_types(1, &cal_data);
+
+ return;
+}
+
+static int msm_routing_init_cal_data(void)
+{
+ int ret = 0;
+ struct cal_type_info cal_type_info = {
+ {ADM_TOPOLOGY_CAL_TYPE,
+ {NULL, NULL, NULL,
+ msm_routing_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num}
+ };
+ pr_debug("%s\n", __func__);
+
+ ret = cal_utils_create_cal_types(1, &cal_data,
+ &cal_type_info);
+ if (ret < 0) {
+ pr_err("%s: could not create cal type!\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return ret;
+err:
+ msm_routing_delete_cal_data();
+ return ret;
+}
+
+static int __init msm_soc_routing_platform_init(void)
+{
+ mutex_init(&routing_lock);
+ if (msm_routing_init_cal_data())
+ pr_err("%s: could not init cal data!\n", __func__);
+
+ afe_set_routing_callback(
+ (routing_cb)msm_pcm_get_dev_acdb_id_by_port_id);
+
+ memset(&be_dai_name_table, 0, sizeof(be_dai_name_table));
+ memset(&last_be_id_configured, 0, sizeof(last_be_id_configured));
+
+ return platform_driver_register(&msm_routing_pcm_driver);
+}
+module_init(msm_soc_routing_platform_init);
+
+static void __exit msm_soc_routing_platform_exit(void)
+{
+ msm_routing_delete_cal_data();
+ memset(&be_dai_name_table, 0, sizeof(be_dai_name_table));
+ mutex_destroy(&routing_lock);
+ platform_driver_unregister(&msm_routing_pcm_driver);
+}
+module_exit(msm_soc_routing_platform_exit);
+
+MODULE_DESCRIPTION("MSM routing platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
new file mode 100644
index 000000000000..30b66bb7ae63
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -0,0 +1,575 @@
+/* Copyright (c) 2012-2018, 2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_PCM_ROUTING_H
+#define _MSM_PCM_ROUTING_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6adm-v2.h>
+
+/*
+ * These names are used by HAL to specify the BE. If any changes are
+ * made to the string names or the max name length corresponding
+ * changes need to be made in the HAL to ensure they still match.
+ */
+#define LPASS_BE_NAME_MAX_LENGTH 24
+#define LPASS_BE_PRI_I2S_RX "PRIMARY_I2S_RX"
+#define LPASS_BE_PRI_I2S_TX "PRIMARY_I2S_TX"
+#define LPASS_BE_SLIMBUS_0_RX "SLIMBUS_0_RX"
+#define LPASS_BE_SLIMBUS_0_TX "SLIMBUS_0_TX"
+#define LPASS_BE_HDMI "HDMI"
+#define LPASS_BE_DISPLAY_PORT "DISPLAY_PORT"
+#define LPASS_BE_INT_BT_SCO_RX "INT_BT_SCO_RX"
+#define LPASS_BE_INT_BT_SCO_TX "INT_BT_SCO_TX"
+#define LPASS_BE_INT_BT_A2DP_RX "INT_BT_A2DP_RX"
+#define LPASS_BE_INT_FM_RX "INT_FM_RX"
+#define LPASS_BE_INT_FM_TX "INT_FM_TX"
+#define LPASS_BE_AFE_PCM_RX "RT_PROXY_DAI_001_RX"
+#define LPASS_BE_AFE_PCM_TX "RT_PROXY_DAI_002_TX"
+#define LPASS_BE_AUXPCM_RX "AUX_PCM_RX"
+#define LPASS_BE_AUXPCM_TX "AUX_PCM_TX"
+#define LPASS_BE_SEC_AUXPCM_RX "SEC_AUX_PCM_RX"
+#define LPASS_BE_SEC_AUXPCM_TX "SEC_AUX_PCM_TX"
+#define LPASS_BE_TERT_AUXPCM_RX "TERT_AUX_PCM_RX"
+#define LPASS_BE_TERT_AUXPCM_TX "TERT_AUX_PCM_TX"
+#define LPASS_BE_QUAT_AUXPCM_RX "QUAT_AUX_PCM_RX"
+#define LPASS_BE_QUAT_AUXPCM_TX "QUAT_AUX_PCM_TX"
+#define LPASS_BE_VOICE_PLAYBACK_TX "VOICE_PLAYBACK_TX"
+#define LPASS_BE_VOICE2_PLAYBACK_TX "VOICE2_PLAYBACK_TX"
+#define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_RX"
+#define LPASS_BE_INCALL_RECORD_TX "INCALL_RECORD_TX"
+#define LPASS_BE_PROXY_RX "PROXY_RX"
+#define LPASS_BE_PROXY_TX "PROXY_TX"
+#define LPASS_BE_SEC_I2S_RX "SECONDARY_I2S_RX"
+#define LPASS_BE_SPDIF_RX "SPDIF_RX"
+
+#define LPASS_BE_MI2S_RX "MI2S_RX"
+#define LPASS_BE_MI2S_TX "MI2S_TX"
+#define LPASS_BE_QUAT_MI2S_RX "QUAT_MI2S_RX"
+#define LPASS_BE_QUAT_MI2S_TX "QUAT_MI2S_TX"
+#define LPASS_BE_SEC_MI2S_RX "SEC_MI2S_RX"
+#define LPASS_BE_SEC_MI2S_RX_SD1 "SEC_MI2S_RX_SD1"
+#define LPASS_BE_SEC_MI2S_TX "SEC_MI2S_TX"
+#define LPASS_BE_PRI_MI2S_RX "PRI_MI2S_RX"
+#define LPASS_BE_PRI_MI2S_TX "PRI_MI2S_TX"
+#define LPASS_BE_TERT_MI2S_RX "TERT_MI2S_RX"
+#define LPASS_BE_TERT_MI2S_TX "TERT_MI2S_TX"
+#define LPASS_BE_AUDIO_I2S_RX "AUDIO_I2S_RX"
+#define LPASS_BE_STUB_RX "STUB_RX"
+#define LPASS_BE_STUB_TX "STUB_TX"
+#define LPASS_BE_SLIMBUS_1_RX "SLIMBUS_1_RX"
+#define LPASS_BE_SLIMBUS_1_TX "SLIMBUS_1_TX"
+#define LPASS_BE_STUB_1_TX "STUB_1_TX"
+#define LPASS_BE_SLIMBUS_2_RX "SLIMBUS_2_RX"
+#define LPASS_BE_SLIMBUS_2_TX "SLIMBUS_2_TX"
+#define LPASS_BE_SLIMBUS_3_RX "SLIMBUS_3_RX"
+#define LPASS_BE_SLIMBUS_3_TX "SLIMBUS_3_TX"
+#define LPASS_BE_SLIMBUS_4_RX "SLIMBUS_4_RX"
+#define LPASS_BE_SLIMBUS_4_TX "SLIMBUS_4_TX"
+#define LPASS_BE_SLIMBUS_TX_VI "SLIMBUS_TX_VI"
+#define LPASS_BE_SLIMBUS_5_RX "SLIMBUS_5_RX"
+#define LPASS_BE_SLIMBUS_5_TX "SLIMBUS_5_TX"
+#define LPASS_BE_SLIMBUS_6_RX "SLIMBUS_6_RX"
+#define LPASS_BE_SLIMBUS_6_TX "SLIMBUS_6_TX"
+#define LPASS_BE_QUIN_MI2S_RX "QUIN_MI2S_RX"
+#define LPASS_BE_QUIN_MI2S_TX "QUIN_MI2S_TX"
+#define LPASS_BE_SENARY_MI2S_TX "SENARY_MI2S_TX"
+
+#define LPASS_BE_PRI_TDM_RX_0 "PRI_TDM_RX_0"
+#define LPASS_BE_PRI_TDM_TX_0 "PRI_TDM_TX_0"
+#define LPASS_BE_PRI_TDM_RX_1 "PRI_TDM_RX_1"
+#define LPASS_BE_PRI_TDM_TX_1 "PRI_TDM_TX_1"
+#define LPASS_BE_PRI_TDM_RX_2 "PRI_TDM_RX_2"
+#define LPASS_BE_PRI_TDM_TX_2 "PRI_TDM_TX_2"
+#define LPASS_BE_PRI_TDM_RX_3 "PRI_TDM_RX_3"
+#define LPASS_BE_PRI_TDM_TX_3 "PRI_TDM_TX_3"
+#define LPASS_BE_PRI_TDM_RX_4 "PRI_TDM_RX_4"
+#define LPASS_BE_PRI_TDM_TX_4 "PRI_TDM_TX_4"
+#define LPASS_BE_PRI_TDM_RX_5 "PRI_TDM_RX_5"
+#define LPASS_BE_PRI_TDM_TX_5 "PRI_TDM_TX_5"
+#define LPASS_BE_PRI_TDM_RX_6 "PRI_TDM_RX_6"
+#define LPASS_BE_PRI_TDM_TX_6 "PRI_TDM_TX_6"
+#define LPASS_BE_PRI_TDM_RX_7 "PRI_TDM_RX_7"
+#define LPASS_BE_PRI_TDM_TX_7 "PRI_TDM_TX_7"
+#define LPASS_BE_SEC_TDM_RX_0 "SEC_TDM_RX_0"
+#define LPASS_BE_SEC_TDM_TX_0 "SEC_TDM_TX_0"
+#define LPASS_BE_SEC_TDM_RX_1 "SEC_TDM_RX_1"
+#define LPASS_BE_SEC_TDM_TX_1 "SEC_TDM_TX_1"
+#define LPASS_BE_SEC_TDM_RX_2 "SEC_TDM_RX_2"
+#define LPASS_BE_SEC_TDM_TX_2 "SEC_TDM_TX_2"
+#define LPASS_BE_SEC_TDM_RX_3 "SEC_TDM_RX_3"
+#define LPASS_BE_SEC_TDM_TX_3 "SEC_TDM_TX_3"
+#define LPASS_BE_SEC_TDM_RX_4 "SEC_TDM_RX_4"
+#define LPASS_BE_SEC_TDM_TX_4 "SEC_TDM_TX_4"
+#define LPASS_BE_SEC_TDM_RX_5 "SEC_TDM_RX_5"
+#define LPASS_BE_SEC_TDM_TX_5 "SEC_TDM_TX_5"
+#define LPASS_BE_SEC_TDM_RX_6 "SEC_TDM_RX_6"
+#define LPASS_BE_SEC_TDM_TX_6 "SEC_TDM_TX_6"
+#define LPASS_BE_SEC_TDM_RX_7 "SEC_TDM_RX_7"
+#define LPASS_BE_SEC_TDM_TX_7 "SEC_TDM_TX_7"
+#define LPASS_BE_TERT_TDM_RX_0 "TERT_TDM_RX_0"
+#define LPASS_BE_TERT_TDM_TX_0 "TERT_TDM_TX_0"
+#define LPASS_BE_TERT_TDM_RX_1 "TERT_TDM_RX_1"
+#define LPASS_BE_TERT_TDM_TX_1 "TERT_TDM_TX_1"
+#define LPASS_BE_TERT_TDM_RX_2 "TERT_TDM_RX_2"
+#define LPASS_BE_TERT_TDM_TX_2 "TERT_TDM_TX_2"
+#define LPASS_BE_TERT_TDM_RX_3 "TERT_TDM_RX_3"
+#define LPASS_BE_TERT_TDM_TX_3 "TERT_TDM_TX_3"
+#define LPASS_BE_TERT_TDM_RX_4 "TERT_TDM_RX_4"
+#define LPASS_BE_TERT_TDM_TX_4 "TERT_TDM_TX_4"
+#define LPASS_BE_TERT_TDM_RX_5 "TERT_TDM_RX_5"
+#define LPASS_BE_TERT_TDM_TX_5 "TERT_TDM_TX_5"
+#define LPASS_BE_TERT_TDM_RX_6 "TERT_TDM_RX_6"
+#define LPASS_BE_TERT_TDM_TX_6 "TERT_TDM_TX_6"
+#define LPASS_BE_TERT_TDM_RX_7 "TERT_TDM_RX_7"
+#define LPASS_BE_TERT_TDM_TX_7 "TERT_TDM_TX_7"
+#define LPASS_BE_QUAT_TDM_RX_0 "QUAT_TDM_RX_0"
+#define LPASS_BE_QUAT_TDM_TX_0 "QUAT_TDM_TX_0"
+#define LPASS_BE_QUAT_TDM_RX_1 "QUAT_TDM_RX_1"
+#define LPASS_BE_QUAT_TDM_TX_1 "QUAT_TDM_TX_1"
+#define LPASS_BE_QUAT_TDM_RX_2 "QUAT_TDM_RX_2"
+#define LPASS_BE_QUAT_TDM_TX_2 "QUAT_TDM_TX_2"
+#define LPASS_BE_QUAT_TDM_RX_3 "QUAT_TDM_RX_3"
+#define LPASS_BE_QUAT_TDM_TX_3 "QUAT_TDM_TX_3"
+#define LPASS_BE_QUAT_TDM_RX_4 "QUAT_TDM_RX_4"
+#define LPASS_BE_QUAT_TDM_TX_4 "QUAT_TDM_TX_4"
+#define LPASS_BE_QUAT_TDM_RX_5 "QUAT_TDM_RX_5"
+#define LPASS_BE_QUAT_TDM_TX_5 "QUAT_TDM_TX_5"
+#define LPASS_BE_QUAT_TDM_RX_6 "QUAT_TDM_RX_6"
+#define LPASS_BE_QUAT_TDM_TX_6 "QUAT_TDM_TX_6"
+#define LPASS_BE_QUAT_TDM_RX_7 "QUAT_TDM_RX_7"
+#define LPASS_BE_QUAT_TDM_TX_7 "QUAT_TDM_TX_7"
+
+#define LPASS_BE_SLIMBUS_7_RX "SLIMBUS_7_RX"
+#define LPASS_BE_SLIMBUS_7_TX "SLIMBUS_7_TX"
+#define LPASS_BE_SLIMBUS_8_RX "SLIMBUS_8_RX"
+#define LPASS_BE_SLIMBUS_8_TX "SLIMBUS_8_TX"
+
+#define LPASS_BE_USB_AUDIO_RX "USB_AUDIO_RX"
+#define LPASS_BE_USB_AUDIO_TX "USB_AUDIO_TX"
+
+#define LPASS_BE_INT0_MI2S_RX "INT0_MI2S_RX"
+#define LPASS_BE_INT0_MI2S_TX "INT0_MI2S_TX"
+#define LPASS_BE_INT1_MI2S_RX "INT1_MI2S_RX"
+#define LPASS_BE_INT1_MI2S_TX "INT1_MI2S_TX"
+#define LPASS_BE_INT2_MI2S_RX "INT2_MI2S_RX"
+#define LPASS_BE_INT2_MI2S_TX "INT2_MI2S_TX"
+#define LPASS_BE_INT3_MI2S_RX "INT3_MI2S_RX"
+#define LPASS_BE_INT3_MI2S_TX "INT3_MI2S_TX"
+#define LPASS_BE_INT4_MI2S_RX "INT4_MI2S_RX"
+#define LPASS_BE_INT4_MI2S_TX "INT4_MI2S_TX"
+#define LPASS_BE_INT5_MI2S_RX "INT5_MI2S_RX"
+#define LPASS_BE_INT5_MI2S_TX "INT5_MI2S_TX"
+#define LPASS_BE_INT6_MI2S_RX "INT6_MI2S_RX"
+#define LPASS_BE_INT6_MI2S_TX "INT6_MI2S_TX"
+
+#define LPASS_BE_SEC_MI2S_RX_1 "SEC_MI2S_RX_1"
+#define LPASS_BE_SEC_MI2S_RX_2 "SEC_MI2S_RX_2"
+#define LPASS_BE_SEC_MI2S_RX_3 "SEC_MI2S_RX_3"
+#define LPASS_BE_SEC_MI2S_RX_4 "SEC_MI2S_RX_4"
+#define LPASS_BE_SEC_MI2S_TX_1 "SEC_MI2S_TX_1"
+#define LPASS_BE_SEC_MI2S_TX_2 "SEC_MI2S_TX_2"
+#define LPASS_BE_SEC_MI2S_TX_3 "SEC_MI2S_TX_3"
+#define LPASS_BE_SEC_MI2S_TX_4 "SEC_MI2S_TX_4"
+
+#define LPASS_BE_TERT_MI2S_RX_1 "TERT_MI2S_RX_1"
+#define LPASS_BE_TERT_MI2S_RX_2 "TERT_MI2S_RX_2"
+#define LPASS_BE_TERT_MI2S_RX_3 "TERT_MI2S_RX_3"
+#define LPASS_BE_TERT_MI2S_RX_4 "TERT_MI2S_RX_4"
+#define LPASS_BE_TERT_MI2S_TX_1 "TERT_MI2S_TX_1"
+#define LPASS_BE_TERT_MI2S_TX_2 "TERT_MI2S_TX_2"
+#define LPASS_BE_TERT_MI2S_TX_3 "TERT_MI2S_TX_3"
+#define LPASS_BE_TERT_MI2S_TX_4 "TERT_MI2S_TX_4"
+
+#define LPASS_BE_QUAT_MI2S_RX_1 "QUAT_MI2S_RX_1"
+#define LPASS_BE_QUAT_MI2S_RX_2 "QUAT_MI2S_RX_2"
+#define LPASS_BE_QUAT_MI2S_RX_3 "QUAT_MI2S_RX_3"
+#define LPASS_BE_QUAT_MI2S_RX_4 "QUAT_MI2S_RX_4"
+#define LPASS_BE_QUAT_MI2S_TX_1 "QUAT_MI2S_TX_1"
+#define LPASS_BE_QUAT_MI2S_TX_2 "QUAT_MI2S_TX_2"
+#define LPASS_BE_QUAT_MI2S_TX_3 "QUAT_MI2S_TX_3"
+#define LPASS_BE_QUAT_MI2S_TX_4 "QUAT_MI2S_TX_4"
+
+/* For multimedia front-ends, asm session is allocated dynamically.
+ * Hence, asm session/multimedia front-end mapping has to be maintained.
+ * Due to this reason, additional multimedia front-end must be placed before
+ * non-multimedia front-ends.
+ */
+
+enum {
+ MSM_FRONTEND_DAI_MULTIMEDIA1 = 0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4,
+ MSM_FRONTEND_DAI_MULTIMEDIA5,
+ MSM_FRONTEND_DAI_MULTIMEDIA6,
+ MSM_FRONTEND_DAI_MULTIMEDIA7,
+ MSM_FRONTEND_DAI_MULTIMEDIA8,
+ MSM_FRONTEND_DAI_MULTIMEDIA9,
+ MSM_FRONTEND_DAI_MULTIMEDIA10,
+ MSM_FRONTEND_DAI_MULTIMEDIA11,
+ MSM_FRONTEND_DAI_MULTIMEDIA12,
+ MSM_FRONTEND_DAI_MULTIMEDIA13,
+ MSM_FRONTEND_DAI_MULTIMEDIA14,
+ MSM_FRONTEND_DAI_MULTIMEDIA15,
+ MSM_FRONTEND_DAI_MULTIMEDIA16,
+ MSM_FRONTEND_DAI_MULTIMEDIA17,
+ MSM_FRONTEND_DAI_MULTIMEDIA18,
+ MSM_FRONTEND_DAI_MULTIMEDIA19,
+ MSM_FRONTEND_DAI_MULTIMEDIA20,
+ MSM_FRONTEND_DAI_MULTIMEDIA21,
+ MSM_FRONTEND_DAI_MULTIMEDIA22,
+ MSM_FRONTEND_DAI_MULTIMEDIA23,
+ MSM_FRONTEND_DAI_MULTIMEDIA24,
+ MSM_FRONTEND_DAI_MULTIMEDIA25,
+ MSM_FRONTEND_DAI_MULTIMEDIA26,
+ MSM_FRONTEND_DAI_MULTIMEDIA27,
+ MSM_FRONTEND_DAI_MULTIMEDIA28,
+ MSM_FRONTEND_DAI_MULTIMEDIA29,
+ MSM_FRONTEND_DAI_MULTIMEDIA30,
+ MSM_FRONTEND_DAI_MULTIMEDIA31,
+ MSM_FRONTEND_DAI_MULTIMEDIA32,
+ MSM_FRONTEND_DAI_MULTIMEDIA33,
+ MSM_FRONTEND_DAI_CS_VOICE,
+ MSM_FRONTEND_DAI_VOIP,
+ MSM_FRONTEND_DAI_AFE_RX,
+ MSM_FRONTEND_DAI_AFE_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB,
+ MSM_FRONTEND_DAI_VOLTE,
+ MSM_FRONTEND_DAI_DTMF_RX,
+ MSM_FRONTEND_DAI_VOICE2,
+ MSM_FRONTEND_DAI_QCHAT,
+ MSM_FRONTEND_DAI_VOLTE_STUB,
+ MSM_FRONTEND_DAI_LSM1,
+ MSM_FRONTEND_DAI_LSM2,
+ MSM_FRONTEND_DAI_LSM3,
+ MSM_FRONTEND_DAI_LSM4,
+ MSM_FRONTEND_DAI_LSM5,
+ MSM_FRONTEND_DAI_LSM6,
+ MSM_FRONTEND_DAI_LSM7,
+ MSM_FRONTEND_DAI_LSM8,
+ MSM_FRONTEND_DAI_VOICE2_STUB,
+ MSM_FRONTEND_DAI_VOWLAN,
+ MSM_FRONTEND_DAI_VOICEMMODE1,
+ MSM_FRONTEND_DAI_VOICEMMODE2,
+ MSM_FRONTEND_DAI_MAX,
+};
+
+#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA33 + 1)
+#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA33
+
+enum {
+ MSM_BACKEND_DAI_PRI_I2S_RX = 0,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ MSM_BACKEND_DAI_SLIMBUS_2_TX,
+ MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ MSM_BACKEND_DAI_SLIMBUS_6_TX,
+ MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ MSM_BACKEND_DAI_SLIMBUS_8_RX,
+ MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ MSM_BACKEND_DAI_EXTPROC_RX,
+ MSM_BACKEND_DAI_EXTPROC_TX,
+ MSM_BACKEND_DAI_EXTPROC_EC_TX,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_BACKEND_DAI_PRI_MI2S_RX,
+ MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_BACKEND_DAI_AUDIO_I2S_RX,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_BACKEND_DAI_SPDIF_RX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_SD1,
+ MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+ MSM_BACKEND_DAI_QUINARY_MI2S_TX,
+ MSM_BACKEND_DAI_SENARY_MI2S_TX,
+ MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ MSM_BACKEND_DAI_PRI_TDM_RX_1,
+ MSM_BACKEND_DAI_PRI_TDM_TX_1,
+ MSM_BACKEND_DAI_PRI_TDM_RX_2,
+ MSM_BACKEND_DAI_PRI_TDM_TX_2,
+ MSM_BACKEND_DAI_PRI_TDM_RX_3,
+ MSM_BACKEND_DAI_PRI_TDM_TX_3,
+ MSM_BACKEND_DAI_PRI_TDM_RX_4,
+ MSM_BACKEND_DAI_PRI_TDM_TX_4,
+ MSM_BACKEND_DAI_PRI_TDM_RX_5,
+ MSM_BACKEND_DAI_PRI_TDM_TX_5,
+ MSM_BACKEND_DAI_PRI_TDM_RX_6,
+ MSM_BACKEND_DAI_PRI_TDM_TX_6,
+ MSM_BACKEND_DAI_PRI_TDM_RX_7,
+ MSM_BACKEND_DAI_PRI_TDM_TX_7,
+ MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ MSM_BACKEND_DAI_SEC_TDM_RX_1,
+ MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ MSM_BACKEND_DAI_SEC_TDM_RX_2,
+ MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ MSM_BACKEND_DAI_SEC_TDM_RX_3,
+ MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ MSM_BACKEND_DAI_SEC_TDM_RX_4,
+ MSM_BACKEND_DAI_SEC_TDM_TX_4,
+ MSM_BACKEND_DAI_SEC_TDM_RX_5,
+ MSM_BACKEND_DAI_SEC_TDM_TX_5,
+ MSM_BACKEND_DAI_SEC_TDM_RX_6,
+ MSM_BACKEND_DAI_SEC_TDM_TX_6,
+ MSM_BACKEND_DAI_SEC_TDM_RX_7,
+ MSM_BACKEND_DAI_SEC_TDM_TX_7,
+ MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_BACKEND_DAI_TERT_TDM_RX_4,
+ MSM_BACKEND_DAI_TERT_TDM_TX_4,
+ MSM_BACKEND_DAI_TERT_TDM_RX_5,
+ MSM_BACKEND_DAI_TERT_TDM_TX_5,
+ MSM_BACKEND_DAI_TERT_TDM_RX_6,
+ MSM_BACKEND_DAI_TERT_TDM_TX_6,
+ MSM_BACKEND_DAI_TERT_TDM_RX_7,
+ MSM_BACKEND_DAI_TERT_TDM_TX_7,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_4,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_4,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_5,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_5,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_6,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_6,
+ MSM_BACKEND_DAI_QUAT_TDM_RX_7,
+ MSM_BACKEND_DAI_QUAT_TDM_TX_7,
+ MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+ MSM_BACKEND_DAI_USB_RX,
+ MSM_BACKEND_DAI_USB_TX,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_BACKEND_DAI_INT0_MI2S_RX,
+ MSM_BACKEND_DAI_INT0_MI2S_TX,
+ MSM_BACKEND_DAI_INT1_MI2S_RX,
+ MSM_BACKEND_DAI_INT1_MI2S_TX,
+ MSM_BACKEND_DAI_INT2_MI2S_RX,
+ MSM_BACKEND_DAI_INT2_MI2S_TX,
+ MSM_BACKEND_DAI_INT3_MI2S_RX,
+ MSM_BACKEND_DAI_INT3_MI2S_TX,
+ MSM_BACKEND_DAI_INT4_MI2S_RX,
+ MSM_BACKEND_DAI_INT4_MI2S_TX,
+ MSM_BACKEND_DAI_INT5_MI2S_RX,
+ MSM_BACKEND_DAI_INT5_MI2S_TX,
+ MSM_BACKEND_DAI_INT6_MI2S_RX,
+ MSM_BACKEND_DAI_INT6_MI2S_TX,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_1,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_2,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_3,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_RX_4,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_1,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_2,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_3,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX_4,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_1,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_2,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_3,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_RX_4,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_1,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_2,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_3,
+ MSM_BACKEND_DAI_TERTIARY_MI2S_TX_4,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_1,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_2,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_3,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_RX_4,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_1,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_2,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_3,
+ MSM_BACKEND_DAI_QUATERNARY_MI2S_TX_4,
+ MSM_BACKEND_DAI_PROXY_RX,
+ MSM_BACKEND_DAI_PROXY_TX,
+ MSM_BACKEND_DAI_MAX,
+};
+
+enum msm_pcm_routing_event {
+ MSM_PCM_RT_EVT_BUF_RECFG,
+ MSM_PCM_RT_EVT_DEVSWITCH,
+ MSM_PCM_RT_EVT_MAX,
+};
+
+enum {
+ EXT_EC_REF_NONE = 0,
+ EXT_EC_REF_PRI_MI2S_TX,
+ EXT_EC_REF_SEC_MI2S_TX,
+ EXT_EC_REF_TERT_MI2S_TX,
+ EXT_EC_REF_QUAT_MI2S_TX,
+ EXT_EC_REF_QUIN_MI2S_TX,
+ EXT_EC_REF_SLIM_1_TX,
+};
+
+#define INVALID_SESSION -1
+#define SESSION_TYPE_RX 0
+#define SESSION_TYPE_TX 1
+#define MAX_SESSION_TYPES 2
+#define INT_RX_VOL_MAX_STEPS 0x2000
+#define INT_RX_VOL_GAIN 0x2000
+
+#define RELEASE_LOCK 0
+#define ACQUIRE_LOCK 1
+
+#define HDMI_RX_ID 0x8001
+
+enum {
+ ADM_PP_PARAM_MUTE_ID,
+ ADM_PP_PARAM_LATENCY_ID,
+ ADM_PP_PARAM_LIMITER_ID
+};
+
+enum {
+ ADM_PP_PARAM_MUTE_BIT = 0x1,
+ ADM_PP_PARAM_LATENCY_BIT = 0x2,
+ ADM_PP_PARAM_LIMITER_BIT = 0x4
+};
+
+#define BE_DAI_PORT_SESSIONS_IDX_MAX 4
+#define BE_DAI_FE_SESSIONS_IDX_MAX 2
+
+struct msm_pcm_routing_evt {
+ void (*event_func)(enum msm_pcm_routing_event, void *);
+ void *priv_data;
+};
+
+struct msm_pcm_routing_bdai_data {
+ u16 port_id; /* AFE port ID */
+ u8 active; /* track if this backend is enabled */
+
+ /* Front-end sessions */
+ unsigned long fe_sessions[BE_DAI_FE_SESSIONS_IDX_MAX];
+ /*
+ * Track Tx BE ports -> Rx BE ports.
+ * port_sessions[0] used to track BE 0 to BE 63.
+ * port_sessions[1] used to track BE 64 to BE 127.
+ * port_sessions[2] used to track BE 128 to BE 191.
+ * port_sessions[3] used to track BE 192 to BE 255.
+ */
+ u64 port_sessions[BE_DAI_PORT_SESSIONS_IDX_MAX];
+
+ unsigned int sample_rate;
+ unsigned int channel;
+ unsigned int format;
+ unsigned int adm_override_ch;
+ u32 passthr_mode[MSM_FRONTEND_DAI_MAX];
+ char *name;
+};
+
+struct msm_pcm_routing_fdai_data {
+ u16 be_srate; /* track prior backend sample rate for flushing purpose */
+ int strm_id; /* ASM stream ID */
+ int perf_mode;
+ struct msm_pcm_routing_evt event_info;
+};
+
+#define MAX_APP_TYPES 16
+struct msm_pcm_routing_app_type_data {
+ int app_type;
+ u32 sample_rate;
+ int bit_width;
+};
+
+struct msm_pcm_stream_app_type_cfg {
+ int app_type;
+ int acdb_dev_id;
+ int sample_rate;
+ u32 copp_token;
+};
+
+/* dai_id: front-end ID,
+ * dspst_id: DSP audio stream ID
+ * stream_type: playback or capture
+ */
+int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, int dspst_id,
+ int stream_type);
+void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
+ int stream_type);
+int msm_pcm_routing_reg_phy_compr_stream(int fedai_id, int perf_mode,
+ int dspst_id, int stream_type,
+ uint32_t compr_passthr);
+
+int msm_pcm_routing_reg_phy_stream_v2(int fedai_id, int perf_mode,
+ int dspst_id, int stream_type,
+ struct msm_pcm_routing_evt event_info);
+
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
+
+int msm_routing_check_backend_enabled(int fedai_id);
+
+
+void msm_pcm_routing_get_bedai_info(int be_idx,
+ struct msm_pcm_routing_bdai_data *bedai);
+void msm_pcm_routing_get_fedai_info(int fe_idx, int sess_type,
+ struct msm_pcm_routing_fdai_data *fe_dai);
+void msm_pcm_routing_acquire_lock(void);
+void msm_pcm_routing_release_lock(void);
+
+int msm_pcm_routing_reg_stream_app_type_cfg(
+ int fedai_id, int session_type, int be_id,
+ struct msm_pcm_stream_app_type_cfg *cfg_data);
+int msm_pcm_routing_get_stream_app_type_cfg(
+ int fedai_id, int session_type, int *be_id,
+ struct msm_pcm_stream_app_type_cfg *cfg_data);
+int msm_routing_set_downmix_control_data(int be_id, int session_id,
+ struct asm_stream_pan_ctrl_params *pan_param);
+int msm_pcm_routing_set_channel_mixer_runtime(
+ int be_id, int session_id,
+ int session_type,
+ struct msm_pcm_channel_mixer *params);
+
+int msm_pcm_routing_set_channel_mixer_cfg(
+ int fe_id, int session_type,
+ struct msm_pcm_channel_mixer *params);
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
new file mode 100644
index 000000000000..2615ee61db21
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
@@ -0,0 +1,1062 @@
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/of_device.h>
+
+#include "msm-pcm-voice-v2.h"
+#include "msm-qti-pp-config.h"
+#include "q6voice.h"
+
+struct msm_dtmf_detected_event_data {
+ uint32_t event_type;
+ uint32_t payload_len;
+ struct vss_istream_evt_rx_dtmf_detected dtmf_payload;
+};
+
+static struct msm_voice voice_info[VOICE_SESSION_INDEX_MAX];
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+
+ .buffer_bytes_max = 4096 * 2,
+ .period_bytes_min = 2048,
+ .period_bytes_max = 4096,
+ .periods_min = 2,
+ .periods_max = 4,
+
+ .fifo_size = 0,
+};
+
+static int is_valid_session_id(uint32_t session_id)
+{
+ int idx = 0;
+
+ switch (session_id) {
+ case VOICE_SESSION_VSID:
+ case VOICE2_SESSION_VSID:
+ case VOLTE_SESSION_VSID:
+ case QCHAT_SESSION_VSID:
+ case VOWLAN_SESSION_VSID:
+ case VOICEMMODE1_VSID:
+ case VOICEMMODE2_VSID:
+ case ALL_SESSION_VSID:
+ idx = 1;
+ break;
+ default:
+ pr_debug("%s: Invalid session_id : %x\n", __func__, session_id);
+ break;
+ }
+ return idx;
+}
+
+static int get_idx_for_session(uint32_t session_id)
+{
+ int idx = 0;
+
+ switch (session_id) {
+ case VOICE_SESSION_VSID:
+ idx = VOICE_SESSION_INDEX;
+ break;
+ case VOICE2_SESSION_VSID:
+ idx = VOICE2_SESSION_INDEX;
+ break;
+ case VOLTE_SESSION_VSID:
+ idx = VOLTE_SESSION_INDEX;
+ break;
+ case QCHAT_SESSION_VSID:
+ idx = QCHAT_SESSION_INDEX;
+ break;
+ case VOWLAN_SESSION_VSID:
+ idx = VOWLAN_SESSION_INDEX;
+ break;
+ case VOICEMMODE1_VSID:
+ idx = VOICEMMODE1_INDEX;
+ break;
+ case VOICEMMODE2_VSID:
+ idx = VOICEMMODE2_INDEX;
+ break;
+ case ALL_SESSION_VSID:
+ idx = VOICE_SESSION_INDEX_MAX - 1;
+ break;
+ default:
+ pr_err("%s: Invalid session_id : %x\n", __func__, session_id);
+ break;
+ }
+
+ return idx;
+}
+
+static bool is_volte(struct msm_voice *pvolte)
+{
+ if (pvolte == &voice_info[VOLTE_SESSION_INDEX])
+ return true;
+ else
+ return false;
+}
+
+static bool is_voice2(struct msm_voice *pvoice2)
+{
+ if (pvoice2 == &voice_info[VOICE2_SESSION_INDEX])
+ return true;
+ else
+ return false;
+}
+
+static bool is_qchat(struct msm_voice *pqchat)
+{
+ if (pqchat == &voice_info[QCHAT_SESSION_INDEX])
+ return true;
+ else
+ return false;
+}
+
+static bool is_vowlan(struct msm_voice *pvowlan)
+{
+ if (pvowlan == &voice_info[VOWLAN_SESSION_INDEX])
+ return true;
+ else
+ return false;
+}
+
+static bool is_voicemmode1(struct msm_voice *pvoicemmode1)
+{
+ if (pvoicemmode1 == &voice_info[VOICEMMODE1_INDEX])
+ return true;
+ else
+ return false;
+}
+
+static bool is_voicemmode2(struct msm_voice *pvoicemmode2)
+{
+ if (pvoicemmode2 == &voice_info[VOICEMMODE2_INDEX])
+ return true;
+ else
+ return false;
+}
+
+static uint32_t get_session_id(struct msm_voice *pvoc)
+{
+ uint32_t session_id = 0;
+
+ if (is_volte(pvoc))
+ session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+ else if (is_voice2(pvoc))
+ session_id = voc_get_session_id(VOICE2_SESSION_NAME);
+ else if (is_qchat(pvoc))
+ session_id = voc_get_session_id(QCHAT_SESSION_NAME);
+ else if (is_vowlan(pvoc))
+ session_id = voc_get_session_id(VOWLAN_SESSION_NAME);
+ else if (is_voicemmode1(pvoc))
+ session_id = voc_get_session_id(VOICEMMODE1_NAME);
+ else if (is_voicemmode2(pvoc))
+ session_id = voc_get_session_id(VOICEMMODE2_NAME);
+ else
+ session_id = voc_get_session_id(VOICE_SESSION_NAME);
+
+ return session_id;
+}
+
+static void dtmf_rx_detected_evt_hdlr(uint8_t *pkt,
+ char *session,
+ void *private_data)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *soc_prtd = private_data;
+ struct msm_dtmf_detected_event_data event_data = {ADSP_STREAM_PP_EVENT,
+ sizeof(struct vss_istream_evt_rx_dtmf_detected),
+ {0} };
+
+ if (!pkt) {
+ pr_err("%s: packet is NULL\n",
+ __func__);
+ return;
+ }
+
+ if (!private_data) {
+ pr_err("%s: private_data is NULL\n",
+ __func__);
+ return;
+ }
+
+ memcpy(&event_data.dtmf_payload, pkt,
+ sizeof(struct vss_istream_evt_rx_dtmf_detected));
+
+ ret = msm_adsp_inform_mixer_ctl(soc_prtd, (uint32_t *)&event_data);
+ if (ret) {
+ pr_err("%s: failed to inform mixer ctl. err = %d\n",
+ __func__, ret);
+ return;
+ }
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+
+ if (!prtd->playback_start)
+ prtd->playback_start = 1;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+
+ if (!prtd->capture_start)
+ prtd->capture_start = 1;
+
+ return 0;
+}
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_voice *voice;
+
+ if (!strncmp("VoLTE", substream->pcm->id, 5)) {
+ voice = &voice_info[VOLTE_SESSION_INDEX];
+ pr_debug("%s: Open VoLTE Substream Id=%s\n",
+ __func__, substream->pcm->id);
+ } else if (!strncmp("Voice2", substream->pcm->id, 6)) {
+ voice = &voice_info[VOICE2_SESSION_INDEX];
+ pr_debug("%s: Open Voice2 Substream Id=%s\n",
+ __func__, substream->pcm->id);
+ } else if (!strncmp("QCHAT", substream->pcm->id, 5)) {
+ voice = &voice_info[QCHAT_SESSION_INDEX];
+ pr_debug("%s: Open QCHAT Substream Id=%s\n",
+ __func__, substream->pcm->id);
+ } else if (!strncmp("VoWLAN", substream->pcm->id, 6)) {
+ voice = &voice_info[VOWLAN_SESSION_INDEX];
+ pr_debug("%s: Open VoWLAN Substream Id=%s\n",
+ __func__, substream->pcm->id);
+ } else if (!strncmp("VoiceMMode1", substream->pcm->id, 11)) {
+ voice = &voice_info[VOICEMMODE1_INDEX];
+ pr_debug("%s: Open VoiceMMode1 Substream Id=%s\n",
+ __func__, substream->pcm->id);
+ } else if (!strncmp("VoiceMMode2", substream->pcm->id, 11)) {
+ voice = &voice_info[VOICEMMODE2_INDEX];
+ pr_debug("%s: Open VoiceMMode2 Substream Id=%s\n",
+ __func__, substream->pcm->id);
+ } else {
+ voice = &voice_info[VOICE_SESSION_INDEX];
+ pr_debug("%s: Open VOICE Substream Id=%s\n",
+ __func__, substream->pcm->id);
+ }
+ mutex_lock(&voice->lock);
+
+ runtime->hw = msm_pcm_hardware;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ voice->playback_substream = substream;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ voice->capture_substream = substream;
+
+ voice->instance++;
+ pr_debug("%s: Instance = %d, Stream ID = %s\n",
+ __func__ , voice->instance, substream->pcm->id);
+ runtime->private_data = voice;
+
+ mutex_unlock(&voice->lock);
+ msm_adsp_init_mixer_ctl_pp_event_queue(soc_prtd);
+ return 0;
+}
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+
+ if (prtd->playback_start)
+ prtd->playback_start = 0;
+
+ prtd->playback_substream = NULL;
+
+ return 0;
+}
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+
+ if (prtd->capture_start)
+ prtd->capture_start = 0;
+ prtd->capture_substream = NULL;
+
+ return 0;
+}
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_voice *prtd = runtime->private_data;
+ uint32_t session_id = 0;
+ int ret = 0;
+
+ mutex_lock(&prtd->lock);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+
+ prtd->instance--;
+ if (!prtd->playback_start && !prtd->capture_start) {
+ pr_debug("end voice call\n");
+
+ session_id = get_session_id(prtd);
+ if (session_id)
+ voc_end_voice_call(session_id);
+ }
+ mutex_unlock(&prtd->lock);
+ msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd);
+
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+ uint32_t session_id = 0;
+
+ mutex_lock(&prtd->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+
+ if (prtd->playback_start && prtd->capture_start) {
+ session_id = get_session_id(prtd);
+ if (session_id)
+ voc_start_voice_call(session_id);
+ }
+ mutex_unlock(&prtd->lock);
+
+ return ret;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+
+ pr_debug("%s: Voice\n", __func__);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+ uint32_t session_id = 0;
+
+ pr_debug("%s: cmd = %d\n", __func__, cmd);
+
+ session_id = get_session_id(prtd);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("Start & Stop Voice call not handled in Trigger.\n");
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: resume call session_id = %d\n", __func__,
+ session_id);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+ if (prtd->playback_start && prtd->capture_start) {
+ if (session_id)
+ voc_resume_voice_call(session_id);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("%s: pause call session_id=%d\n",
+ __func__, session_id);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (prtd->playback_start)
+ prtd->playback_start = 0;
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (prtd->capture_start)
+ prtd->capture_start = 0;
+ }
+ if (session_id)
+ voc_standby_voice_call(session_id);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+ uint32_t session_id = get_session_id(prtd);
+ enum voice_lch_mode lch_mode;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_VOICE_IOCTL_LCH:
+ if (copy_from_user(&lch_mode, (void *)arg,
+ sizeof(enum voice_lch_mode))) {
+ pr_err("%s: Copy from user failed, size %zd\n",
+ __func__, sizeof(enum voice_lch_mode));
+
+ ret = -EFAULT;
+ break;
+ }
+
+ pr_debug("%s: %s lch_mode:%d\n",
+ __func__, substream->pcm->id, lch_mode);
+
+ switch (lch_mode) {
+ case VOICE_LCH_START:
+ case VOICE_LCH_STOP:
+ ret = voc_set_lch(session_id, lch_mode);
+ break;
+
+ default:
+ pr_err("%s: Invalid LCH MODE %d\n", __func__, lch_mode);
+
+ ret = -EFAULT;
+ }
+
+ break;
+ default:
+ pr_debug("%s: Falling into default snd_lib_ioctl cmd 0x%x\n",
+ __func__, cmd);
+
+ ret = snd_pcm_lib_ioctl(substream, cmd, arg);
+ break;
+ }
+
+ if (!ret)
+ pr_debug("%s: ret %d\n", __func__, ret);
+ else
+ pr_err("%s: cmd 0x%x failed %d\n", __func__, cmd, ret);
+
+ return ret;
+}
+
+static int msm_voice_sidetone_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret;
+ bool sidetone_enable = ucontrol->value.integer.value[0];
+ uint32_t session_id = ALL_SESSION_VSID;
+
+ if (sidetone_enable < 0) {
+ pr_err("%s: Invalid arguments sidetone enable %d\n",
+ __func__, sidetone_enable);
+ ret = -EINVAL;
+ return ret;
+ }
+ ret = voc_set_afe_sidetone(session_id, sidetone_enable);
+ pr_debug("%s: AFE Sidetone enable=%d session_id=0x%x ret=%d\n",
+ __func__, sidetone_enable, session_id, ret);
+ return ret;
+}
+
+static int msm_voice_sidetone_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ ucontrol->value.integer.value[0] = voc_get_afe_sidetone();
+ return 0;
+}
+
+static int msm_voice_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int volume = ucontrol->value.integer.value[0];
+ uint32_t session_id = ucontrol->value.integer.value[1];
+ int ramp_duration = ucontrol->value.integer.value[2];
+
+ if ((volume < 0) || (ramp_duration < 0)
+ || (ramp_duration > MAX_RAMP_DURATION)) {
+ pr_err(" %s Invalid arguments", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: volume: %d session_id: %#x ramp_duration: %d\n", __func__,
+ volume, session_id, ramp_duration);
+
+ voc_set_rx_vol_step(session_id, RX_PATH, volume, ramp_duration);
+
+done:
+ return ret;
+}
+
+static int msm_voice_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int mute = ucontrol->value.integer.value[0];
+ uint32_t session_id = ucontrol->value.integer.value[1];
+ int ramp_duration = ucontrol->value.integer.value[2];
+
+ if ((mute < 0) || (mute > 1) || (ramp_duration < 0)
+ || (ramp_duration > MAX_RAMP_DURATION)) {
+ pr_err(" %s Invalid arguments", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
+ mute, session_id, ramp_duration);
+
+ ret = voc_set_tx_mute(session_id, TX_PATH, mute, ramp_duration);
+
+done:
+ return ret;
+}
+
+static int msm_voice_tx_device_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int mute = ucontrol->value.integer.value[0];
+ uint32_t session_id = ucontrol->value.integer.value[1];
+ int ramp_duration = ucontrol->value.integer.value[2];
+
+ if ((mute < 0) || (mute > 1) || (ramp_duration < 0) ||
+ (ramp_duration > MAX_RAMP_DURATION)) {
+ pr_err(" %s Invalid arguments", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
+ mute, session_id, ramp_duration);
+
+ ret = voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_TX,
+ mute, ramp_duration);
+
+done:
+ return ret;
+}
+
+static int msm_voice_rx_device_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int mute = ucontrol->value.integer.value[0];
+ uint32_t session_id = ucontrol->value.integer.value[1];
+ int ramp_duration = ucontrol->value.integer.value[2];
+
+ if ((mute < 0) || (mute > 1) || (ramp_duration < 0) ||
+ (ramp_duration > MAX_RAMP_DURATION)) {
+ pr_err(" %s Invalid arguments", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
+ mute, session_id, ramp_duration);
+
+ voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_RX,
+ mute, ramp_duration);
+
+done:
+ return ret;
+}
+
+
+
+static const char const *tty_mode[] = {"OFF", "HCO", "VCO", "FULL"};
+static const struct soc_enum msm_tty_mode_enum[] = {
+ SOC_ENUM_SINGLE_EXT(4, tty_mode),
+};
+
+static int msm_voice_tty_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ voc_get_tty_mode(voc_get_session_id(VOICE_SESSION_NAME));
+ return 0;
+}
+
+static int msm_voice_tty_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int tty_mode = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: tty_mode=%d\n", __func__, tty_mode);
+
+ voc_set_tty_mode(voc_get_session_id(VOICE_SESSION_NAME), tty_mode);
+ voc_set_tty_mode(voc_get_session_id(VOICE2_SESSION_NAME), tty_mode);
+ voc_set_tty_mode(voc_get_session_id(VOLTE_SESSION_NAME), tty_mode);
+ voc_set_tty_mode(voc_get_session_id(VOWLAN_SESSION_NAME), tty_mode);
+ voc_set_tty_mode(voc_get_session_id(VOICEMMODE1_NAME), tty_mode);
+ voc_set_tty_mode(voc_get_session_id(VOICEMMODE2_NAME), tty_mode);
+
+ return 0;
+}
+
+static int msm_voice_slowtalk_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int st_enable = ucontrol->value.integer.value[0];
+ uint32_t session_id = ucontrol->value.integer.value[1];
+ struct module_instance_info mod_inst_info = {0};
+
+ pr_debug("%s: st enable=%d session_id=%#x\n", __func__, st_enable,
+ session_id);
+
+ mod_inst_info.module_id = MODULE_ID_VOICE_MODULE_ST;
+ mod_inst_info.instance_id = INSTANCE_ID_0;
+ voc_set_pp_enable(session_id, mod_inst_info, st_enable);
+
+ return 0;
+}
+
+static int msm_voice_hd_voice_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ uint32_t hd_enable = ucontrol->value.integer.value[0];
+ uint32_t session_id = ucontrol->value.integer.value[1];
+
+ pr_debug("%s: HD Voice enable=%d session_id=%#x\n", __func__, hd_enable,
+ session_id);
+
+ ret = voc_set_hd_enable(session_id, hd_enable);
+
+ return ret;
+}
+
+static int msm_dtmf_detect_rx_vsid_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t session_id = ucontrol->value.integer.value[0];
+ uint32_t enable = ucontrol->value.integer.value[1];
+
+ if (enable)
+ enable = 1;
+
+ pr_debug("%s: sess_id=%d enable=%d\n", __func__, session_id, enable);
+
+ return voc_enable_dtmf_rx_detection(session_id, enable);
+}
+
+static int msm_dtmf_detect_rx_vsid_cb_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_voice *dtmf_voice_info = NULL;
+ uint32_t session_id = ucontrol->value.integer.value[0];
+ uint32_t enable = ucontrol->value.integer.value[1];
+
+ if (!is_valid_session_id(session_id)) {
+ pr_err(" %s Invalid session_id : %x\n", __func__, session_id);
+ return -EINVAL;
+ }
+
+ if (enable)
+ enable = 1;
+
+ pr_debug("%s: enable dtmf detect cb =%d for session_id=%d\n",
+ __func__, enable, session_id);
+
+ dtmf_voice_info = &voice_info[get_idx_for_session(session_id)];
+ voc_register_dtmf_rx_detection_cb
+ ((dtmf_rx_det_cb_fn) dtmf_rx_detected_evt_hdlr,
+ (void *) dtmf_voice_info->capture_substream->private_data);
+
+ return 0;
+}
+
+static int msm_voice_topology_disable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int disable = ucontrol->value.integer.value[0];
+ uint32_t session_id = ucontrol->value.integer.value[1];
+
+ if ((disable < 0) || (disable > 1)) {
+ pr_err(" %s Invalid arguments: %d\n", __func__, disable);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ pr_debug("%s: disable = %d, session_id = %d\n", __func__, disable,
+ session_id);
+
+ ret = voc_disable_topology(session_id, disable);
+
+done:
+ return ret;
+}
+
+static int msm_voice_cvd_version_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int ret = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = CVD_VERSION_STRING_MAX_SIZE;
+
+ return ret;
+}
+
+static int msm_voice_cvd_version_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ char cvd_version[CVD_VERSION_STRING_MAX_SIZE] = CVD_VERSION_DEFAULT;
+ int ret;
+
+ pr_debug("%s:\n", __func__);
+
+ ret = voc_get_cvd_version(cvd_version);
+
+ if (ret)
+ pr_err("%s: Error retrieving CVD version, error:%d\n",
+ __func__, ret);
+
+ memcpy(ucontrol->value.bytes.data, cvd_version, sizeof(cvd_version));
+
+ return 0;
+}
+
+static struct snd_kcontrol_new msm_voice_controls[] = {
+ SOC_SINGLE_MULTI_EXT("Voice Rx Device Mute", SND_SOC_NOPM, 0, VSID_MAX,
+ 0, 3, NULL, msm_voice_rx_device_mute_put),
+ SOC_SINGLE_MULTI_EXT("Voice Tx Device Mute", SND_SOC_NOPM, 0, VSID_MAX,
+ 0, 3, NULL, msm_voice_tx_device_mute_put),
+ SOC_SINGLE_MULTI_EXT("Voice Tx Mute", SND_SOC_NOPM, 0, VSID_MAX,
+ 0, 3, NULL, msm_voice_mute_put),
+ SOC_SINGLE_MULTI_EXT("Voice Rx Gain", SND_SOC_NOPM, 0, VSID_MAX, 0, 3,
+ NULL, msm_voice_gain_put),
+ SOC_ENUM_EXT("TTY Mode", msm_tty_mode_enum[0], msm_voice_tty_mode_get,
+ msm_voice_tty_mode_put),
+ SOC_SINGLE_MULTI_EXT("Slowtalk Enable", SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
+ NULL, msm_voice_slowtalk_put),
+ SOC_SINGLE_MULTI_EXT("Voice Topology Disable", SND_SOC_NOPM, 0,
+ VSID_MAX, 0, 2, NULL,
+ msm_voice_topology_disable_put),
+ SOC_SINGLE_MULTI_EXT("HD Voice Enable", SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
+ NULL, msm_voice_hd_voice_put),
+ SOC_SINGLE_MULTI_EXT("DTMF_Detect Rx VSID enable",
+ SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
+ NULL, msm_dtmf_detect_rx_vsid_put),
+ SOC_SINGLE_MULTI_EXT("DTMF_Detect Rx Callback VSID enable",
+ SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
+ NULL, msm_dtmf_detect_rx_vsid_cb_put),
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "CVD Version",
+ .info = msm_voice_cvd_version_info,
+ .get = msm_voice_cvd_version_get,
+ },
+ SOC_SINGLE_MULTI_EXT("Voice Sidetone Enable", SND_SOC_NOPM, 0, 1, 0, 1,
+ msm_voice_sidetone_get, msm_voice_sidetone_put),
+
+};
+
+static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int msm_pcm_add_voice_adsp_stream_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CMD;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_voice_adsp_stream_cmd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_pcm_adsp_stream_cmd_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s rtd is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str)
+ return -ENOMEM;
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_voice_adsp_stream_cmd_config_control[0].name = mixer_str;
+ fe_voice_adsp_stream_cmd_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s\n", mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_voice_adsp_stream_cmd_config_control,
+ ARRAY_SIZE(fe_voice_adsp_stream_cmd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+
+ kfree(mixer_str);
+ return ret;
+}
+
+static int msm_pcm_add_voice_adsp_stream_callback_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol *kctl;
+
+ struct snd_kcontrol_new fe_voice_adsp_callback_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_callback_info,
+ .get = msm_adsp_stream_callback_get,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: added new pcm FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str)
+ return -ENOMEM;
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_voice_adsp_callback_config_control[0].name = mixer_str;
+ fe_voice_adsp_callback_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_voice_adsp_callback_config_control,
+ ARRAY_SIZE(fe_voice_adsp_callback_config_control));
+ if (ret < 0) {
+ pr_err("%s: failed to add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl->private_data = NULL;
+
+free_mixer_str:
+ kfree(mixer_str);
+ return ret;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .ioctl = msm_pcm_ioctl,
+ .compat_ioctl = msm_pcm_ioctl,
+};
+
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = msm_pcm_add_voice_adsp_stream_cmd_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm ADSP Stream Cmd Control\n",
+ __func__);
+
+ ret = msm_pcm_add_voice_adsp_stream_callback_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm ADSP Stream Callback Control\n",
+ __func__);
+
+ return ret;
+}
+
+static int msm_pcm_voice_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_add_platform_controls(platform, msm_voice_controls,
+ ARRAY_SIZE(msm_voice_controls));
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+ .probe = msm_pcm_voice_probe,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+ int rc;
+ bool destroy_cvd = false;
+ bool vote_bms = false;
+ const char *is_destroy_cvd = "qcom,destroy-cvd";
+ const char *is_vote_bms = "qcom,vote-bms";
+
+ if (!is_voc_initialized()) {
+ pr_debug("%s: voice module not initialized yet, deferring probe()\n",
+ __func__);
+
+ rc = -EPROBE_DEFER;
+ goto done;
+ }
+
+ rc = voc_alloc_cal_shared_memory();
+ if (rc == -EPROBE_DEFER) {
+ pr_debug("%s: memory allocation for calibration deferred %d\n",
+ __func__, rc);
+
+ goto done;
+ } else if (rc < 0) {
+ pr_err("%s: memory allocation for calibration failed %d\n",
+ __func__, rc);
+ }
+
+ pr_debug("%s: dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+ destroy_cvd = of_property_read_bool(pdev->dev.of_node,
+ is_destroy_cvd);
+ voc_set_destroy_cvd_flag(destroy_cvd);
+
+ vote_bms = of_property_read_bool(pdev->dev.of_node,
+ is_vote_bms);
+ voc_set_vote_bms_flag(vote_bms);
+
+ rc = snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+
+done:
+ return rc;
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_voice_dt_match[] = {
+ {.compatible = "qcom,msm-pcm-voice"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_voice_dt_match);
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-voice",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_voice_dt_match,
+ },
+ .probe = msm_pcm_probe,
+ .remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ int i = 0;
+
+ memset(&voice_info, 0, sizeof(voice_info));
+
+ for (i = 0; i < VOICE_SESSION_INDEX_MAX; i++)
+ mutex_init(&voice_info[i].lock);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("Voice PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h
new file mode 100644
index 000000000000..e00cebc51e7e
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_PCM_VOICE_H
+#define _MSM_PCM_VOICE_H
+#include <sound/apr_audio-v2.h>
+
+enum {
+ VOICE_SESSION_INDEX,
+ VOLTE_SESSION_INDEX,
+ VOICE2_SESSION_INDEX,
+ QCHAT_SESSION_INDEX,
+ VOWLAN_SESSION_INDEX,
+ VOICEMMODE1_INDEX,
+ VOICEMMODE2_INDEX,
+ VOICE_SESSION_INDEX_MAX,
+};
+
+struct msm_voice {
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ int instance;
+
+ struct mutex lock;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+
+ int playback_start;
+ int capture_start;
+};
+
+#endif /*_MSM_PCM_VOICE_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
new file mode 100644
index 000000000000..b2387a746f61
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
@@ -0,0 +1,1713 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+#include "q6voice.h"
+
+#define SHARED_MEM_BUF 2
+#define VOIP_MAX_Q_LEN 10
+#define VOIP_MAX_VOC_PKT_SIZE 4096
+#define VOIP_MIN_VOC_PKT_SIZE 320
+
+/* Length of the DSP frame info header added to the voc packet. */
+#define DSP_FRAME_HDR_LEN 1
+
+#define MODE_IS127 0x2
+#define MODE_4GV_NB 0x3
+#define MODE_4GV_WB 0x4
+#define MODE_AMR 0x5
+#define MODE_AMR_WB 0xD
+#define MODE_PCM 0xC
+#define MODE_4GV_NW 0xE
+#define MODE_G711 0xA
+#define MODE_G711A 0xF
+
+enum msm_audio_g711a_frame_type {
+ MVS_G711A_SPEECH_GOOD,
+ MVS_G711A_SID,
+ MVS_G711A_NO_DATA,
+ MVS_G711A_ERASURE
+};
+
+enum msm_audio_g711a_mode {
+ MVS_G711A_MODE_MULAW,
+ MVS_G711A_MODE_ALAW
+};
+
+enum msm_audio_g711_mode {
+ MVS_G711_MODE_MULAW,
+ MVS_G711_MODE_ALAW
+};
+
+#define VOIP_MODE_MAX MODE_G711A
+#define VOIP_RATE_MAX 23850
+
+enum format {
+ FORMAT_S16_LE = 2,
+ FORMAT_SPECIAL = 31,
+};
+
+
+enum amr_rate_type {
+ AMR_RATE_4750, /* AMR 4.75 kbps */
+ AMR_RATE_5150, /* AMR 5.15 kbps */
+ AMR_RATE_5900, /* AMR 5.90 kbps */
+ AMR_RATE_6700, /* AMR 6.70 kbps */
+ AMR_RATE_7400, /* AMR 7.40 kbps */
+ AMR_RATE_7950, /* AMR 7.95 kbps */
+ AMR_RATE_10200, /* AMR 10.20 kbps */
+ AMR_RATE_12200, /* AMR 12.20 kbps */
+ AMR_RATE_6600, /* AMR-WB 6.60 kbps */
+ AMR_RATE_8850, /* AMR-WB 8.85 kbps */
+ AMR_RATE_12650, /* AMR-WB 12.65 kbps */
+ AMR_RATE_14250, /* AMR-WB 14.25 kbps */
+ AMR_RATE_15850, /* AMR-WB 15.85 kbps */
+ AMR_RATE_18250, /* AMR-WB 18.25 kbps */
+ AMR_RATE_19850, /* AMR-WB 19.85 kbps */
+ AMR_RATE_23050, /* AMR-WB 23.05 kbps */
+ AMR_RATE_23850, /* AMR-WB 23.85 kbps */
+ AMR_RATE_UNDEF
+};
+
+enum voip_state {
+ VOIP_STOPPED,
+ VOIP_STARTED,
+};
+
+struct voip_frame_hdr {
+ uint32_t timestamp;
+ union {
+ /*
+ * Bits 0-3: Frame type
+ * [optional] Bits 16-19: Frame rate
+ */
+ uint32_t frame_type;
+ uint32_t packet_rate;
+ };
+};
+struct voip_frame {
+ struct voip_frame_hdr frm_hdr;
+ uint32_t pktlen;
+ uint8_t voc_pkt[VOIP_MAX_VOC_PKT_SIZE];
+};
+
+struct voip_buf_node {
+ struct list_head list;
+ struct voip_frame frame;
+};
+
+struct voip_drv_info {
+ enum voip_state state;
+
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ struct list_head in_queue;
+ struct list_head free_in_queue;
+
+ struct list_head out_queue;
+ struct list_head free_out_queue;
+
+ wait_queue_head_t out_wait;
+ wait_queue_head_t in_wait;
+
+ struct mutex lock;
+
+ spinlock_t dsp_lock;
+ spinlock_t dsp_ul_lock;
+
+ bool voip_reset;
+ uint32_t mode;
+ uint32_t rate_type;
+ uint32_t rate;
+ uint32_t dtx_mode;
+
+ uint8_t capture_start;
+ uint8_t playback_start;
+
+ uint8_t playback_prepare;
+ uint8_t capture_prepare;
+
+ unsigned int play_samp_rate;
+ unsigned int cap_samp_rate;
+
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pcm_playback_irq_pos; /* IRQ position */
+ unsigned int pcm_playback_buf_pos; /* position in buffer */
+
+ unsigned int pcm_capture_size;
+ unsigned int pcm_capture_count;
+ unsigned int pcm_capture_irq_pos; /* IRQ position */
+ unsigned int pcm_capture_buf_pos; /* position in buffer */
+
+ uint32_t evrc_min_rate;
+ uint32_t evrc_max_rate;
+};
+
+static int voip_get_media_type(uint32_t mode, uint32_t rate_type,
+ unsigned int samp_rate,
+ unsigned int *media_type);
+static int voip_get_rate_type(uint32_t mode,
+ uint32_t rate,
+ uint32_t *rate_type);
+static int voip_config_vocoder(struct snd_pcm_substream *substream);
+static int msm_voip_mode_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int msm_voip_mode_config_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int msm_voip_rate_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int msm_voip_evrc_min_max_rate_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int msm_voip_evrc_min_max_rate_config_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+static struct voip_drv_info voip_info;
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_SPECIAL,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = sizeof(struct voip_buf_node) * VOIP_MAX_Q_LEN,
+ .period_bytes_min = VOIP_MIN_VOC_PKT_SIZE,
+ .period_bytes_max = VOIP_MAX_VOC_PKT_SIZE,
+ .periods_min = VOIP_MAX_Q_LEN,
+ .periods_max = VOIP_MAX_Q_LEN,
+ .fifo_size = 0,
+};
+
+
+static int msm_voip_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int mute = ucontrol->value.integer.value[0];
+ int ramp_duration = ucontrol->value.integer.value[1];
+
+ if ((mute < 0) || (mute > 1) || (ramp_duration < 0)) {
+ pr_err(" %s Invalid arguments", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: mute=%d ramp_duration=%d\n", __func__, mute,
+ ramp_duration);
+
+ voc_set_tx_mute(voc_get_session_id(VOIP_SESSION_NAME), TX_PATH, mute,
+ ramp_duration);
+
+done:
+ return ret;
+}
+
+static int msm_voip_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int volume = ucontrol->value.integer.value[0];
+ int ramp_duration = ucontrol->value.integer.value[1];
+
+ if ((volume < 0) || (ramp_duration < 0)) {
+ pr_err(" %s Invalid arguments", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: volume: %d ramp_duration: %d\n", __func__, volume,
+ ramp_duration);
+
+ voc_set_rx_vol_step(voc_get_session_id(VOIP_SESSION_NAME),
+ RX_PATH,
+ volume,
+ ramp_duration);
+
+done:
+ return ret;
+}
+
+static int msm_voip_dtx_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ mutex_lock(&voip_info.lock);
+
+ voip_info.dtx_mode = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: dtx: %d\n", __func__, voip_info.dtx_mode);
+
+ mutex_unlock(&voip_info.lock);
+
+ return 0;
+}
+static int msm_voip_dtx_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ mutex_lock(&voip_info.lock);
+
+ ucontrol->value.integer.value[0] = voip_info.dtx_mode;
+
+ mutex_unlock(&voip_info.lock);
+
+ return 0;
+}
+
+static struct snd_kcontrol_new msm_voip_controls[] = {
+ SOC_SINGLE_MULTI_EXT("Voip Tx Mute", SND_SOC_NOPM, 0,
+ MAX_RAMP_DURATION,
+ 0, 2, NULL, msm_voip_mute_put),
+ SOC_SINGLE_MULTI_EXT("Voip Rx Gain", SND_SOC_NOPM, 0,
+ MAX_RAMP_DURATION,
+ 0, 2, NULL, msm_voip_gain_put),
+ SOC_SINGLE_EXT("Voip Mode Config", SND_SOC_NOPM, 0, VOIP_MODE_MAX, 0,
+ msm_voip_mode_config_get, msm_voip_mode_config_put),
+ SOC_SINGLE_EXT("Voip Rate Config", SND_SOC_NOPM, 0, VOIP_RATE_MAX, 0,
+ NULL, msm_voip_rate_config_put),
+ SOC_SINGLE_MULTI_EXT("Voip Evrc Min Max Rate Config", SND_SOC_NOPM,
+ 0, VOC_1_RATE, 0, 2,
+ msm_voip_evrc_min_max_rate_config_get,
+ msm_voip_evrc_min_max_rate_config_put),
+ SOC_SINGLE_EXT("Voip Dtx Mode", SND_SOC_NOPM, 0, 1, 0,
+ msm_voip_dtx_mode_get, msm_voip_dtx_mode_put),
+};
+
+static int msm_pcm_voip_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_add_platform_controls(platform, msm_voip_controls,
+ ARRAY_SIZE(msm_voip_controls));
+
+ return 0;
+}
+
+/* sample rate supported */
+static unsigned int supported_sample_rates[] = {8000, 16000, 32000, 48000};
+
+static void voip_ssr_cb_fn(uint32_t opcode, void *private_data)
+{
+
+ /* Notify ASoC to send next playback/Capture to unblock write/read */
+ struct voip_drv_info *prtd = private_data;
+
+ if (opcode == 0xFFFFFFFF) {
+
+ prtd->voip_reset = true;
+ pr_debug("%s: Notify ASoC to send next playback/Capture\n",
+ __func__);
+
+ prtd->pcm_playback_irq_pos += prtd->pcm_count;
+ if (prtd->state == VOIP_STARTED)
+ snd_pcm_period_elapsed(prtd->playback_substream);
+ wake_up(&prtd->out_wait);
+
+ prtd->pcm_capture_irq_pos += prtd->pcm_capture_count;
+ if (prtd->state == VOIP_STARTED)
+ snd_pcm_period_elapsed(prtd->capture_substream);
+ wake_up(&prtd->in_wait);
+
+ } else {
+ pr_err("%s: Invalid opcode during reset : %d\n",
+ __func__, opcode);
+ }
+}
+
+/* capture path */
+static void voip_process_ul_pkt(uint8_t *voc_pkt,
+ uint32_t pkt_len,
+ uint32_t timestamp,
+ void *private_data)
+{
+ struct voip_buf_node *buf_node = NULL;
+ struct voip_drv_info *prtd = private_data;
+ unsigned long dsp_flags;
+
+ if (prtd->capture_substream == NULL)
+ return;
+
+ /* Copy up-link packet into out_queue. */
+ spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
+
+ /* discarding UL packets till start is received */
+ if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) {
+ buf_node = list_first_entry(&prtd->free_out_queue,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ switch (prtd->mode) {
+ case MODE_AMR_WB:
+ case MODE_AMR: {
+ /* Remove the DSP frame info header. Header format:
+ * Bits 0-3: Frame rate
+ * Bits 4-7: Frame type
+ */
+ buf_node->frame.frm_hdr.timestamp = timestamp;
+ buf_node->frame.frm_hdr.frame_type =
+ ((*voc_pkt) & 0xF0) >> 4;
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+ buf_node->frame.pktlen = pkt_len - DSP_FRAME_HDR_LEN;
+ memcpy(&buf_node->frame.voc_pkt[0],
+ voc_pkt,
+ buf_node->frame.pktlen);
+
+ list_add_tail(&buf_node->list, &prtd->out_queue);
+ break;
+ }
+ case MODE_IS127:
+ case MODE_4GV_NB:
+ case MODE_4GV_WB:
+ case MODE_4GV_NW: {
+ /* Remove the DSP frame info header.
+ * Header format:
+ * Bits 0-3: frame rate
+ */
+ buf_node->frame.frm_hdr.timestamp = timestamp;
+ buf_node->frame.frm_hdr.packet_rate = (*voc_pkt) & 0x0F;
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+ buf_node->frame.pktlen = pkt_len - DSP_FRAME_HDR_LEN;
+
+ memcpy(&buf_node->frame.voc_pkt[0],
+ voc_pkt,
+ buf_node->frame.pktlen);
+
+ list_add_tail(&buf_node->list, &prtd->out_queue);
+ break;
+ }
+ case MODE_G711:
+ case MODE_G711A:{
+ /* G711 frames are 10ms each, but the DSP works with
+ * 20ms frames and sends two 10ms frames per buffer.
+ * Extract the two frames and put them in separate
+ * buffers.
+ */
+ /* Remove the first DSP frame info header.
+ * Header format: G711A
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ *
+ * Header format: G711
+ * Bits 2-3: Frame rate
+ */
+ if (prtd->mode == MODE_G711A)
+ buf_node->frame.frm_hdr.frame_type =
+ (*voc_pkt) & 0x03;
+ buf_node->frame.frm_hdr.timestamp = timestamp;
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ /* There are two frames in the buffer. Length of the
+ * first frame:
+ */
+ buf_node->frame.pktlen = (pkt_len -
+ 2 * DSP_FRAME_HDR_LEN) / 2;
+
+ memcpy(&buf_node->frame.voc_pkt[0],
+ voc_pkt,
+ buf_node->frame.pktlen);
+ voc_pkt = voc_pkt + buf_node->frame.pktlen;
+
+ list_add_tail(&buf_node->list, &prtd->out_queue);
+
+ /* Get another buffer from the free Q and fill in the
+ * second frame.
+ */
+ if (!list_empty(&prtd->free_out_queue)) {
+ buf_node =
+ list_first_entry(&prtd->free_out_queue,
+ struct voip_buf_node,
+ list);
+ list_del(&buf_node->list);
+
+ /* Remove the second DSP frame info header.
+ * Header format:
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ */
+
+ if (prtd->mode == MODE_G711A)
+ buf_node->frame.frm_hdr.frame_type =
+ (*voc_pkt) & 0x03;
+ buf_node->frame.frm_hdr.timestamp = timestamp;
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ /* There are two frames in the buffer. Length
+ * of the second frame:
+ */
+ buf_node->frame.pktlen = (pkt_len -
+ 2 * DSP_FRAME_HDR_LEN) / 2;
+
+ memcpy(&buf_node->frame.voc_pkt[0],
+ voc_pkt,
+ buf_node->frame.pktlen);
+
+ list_add_tail(&buf_node->list,
+ &prtd->out_queue);
+ } else {
+ /* Drop the second frame */
+ pr_err("%s: UL data dropped, read is slow\n",
+ __func__);
+ }
+ break;
+ }
+ default: {
+ buf_node->frame.frm_hdr.timestamp = timestamp;
+ buf_node->frame.pktlen = pkt_len;
+ memcpy(&buf_node->frame.voc_pkt[0],
+ voc_pkt,
+ buf_node->frame.pktlen);
+ list_add_tail(&buf_node->list, &prtd->out_queue);
+ }
+ }
+ pr_debug("%s: pkt_len =%d, frame.pktlen=%d, timestamp=%d\n",
+ __func__, pkt_len, buf_node->frame.pktlen, timestamp);
+
+ if (prtd->mode == MODE_PCM)
+ prtd->pcm_capture_irq_pos += buf_node->frame.pktlen;
+ else
+ prtd->pcm_capture_irq_pos += prtd->pcm_capture_count;
+
+ spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
+ snd_pcm_period_elapsed(prtd->capture_substream);
+ } else {
+ spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
+ pr_err("UL data dropped\n");
+ }
+
+ wake_up(&prtd->out_wait);
+}
+
+/* playback path */
+static void voip_process_dl_pkt(uint8_t *voc_pkt, void *private_data)
+{
+ struct voip_buf_node *buf_node = NULL;
+ struct voip_drv_info *prtd = private_data;
+ unsigned long dsp_flags;
+ uint32_t rate_type;
+ uint32_t frame_rate;
+ u32 pkt_len;
+ u8 *voc_addr = NULL;
+
+ if (prtd->playback_substream == NULL)
+ return;
+
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+
+ if (!list_empty(&prtd->in_queue) && prtd->playback_start) {
+ buf_node = list_first_entry(&prtd->in_queue,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ switch (prtd->mode) {
+ case MODE_AMR:
+ case MODE_AMR_WB: {
+ *((uint32_t *)voc_pkt) = buf_node->frame.pktlen +
+ DSP_FRAME_HDR_LEN;
+ /* Advance to the header of voip packet */
+ voc_pkt = voc_pkt + sizeof(uint32_t);
+ /*
+ * Add the DSP frame info header. Header format:
+ * Bits 0-3: Frame rate
+ * Bits 4-7: Frame type
+ */
+ *voc_pkt = ((buf_node->frame.frm_hdr.frame_type &
+ 0x0F) << 4);
+ frame_rate = (buf_node->frame.frm_hdr.frame_type &
+ 0xFFFF0000) >> 16;
+ if (frame_rate) {
+ if (voip_get_rate_type(prtd->mode, frame_rate,
+ &rate_type)) {
+ pr_err("%s(): fail at getting rate_type\n",
+ __func__);
+ } else
+ prtd->rate_type = rate_type;
+ }
+ *voc_pkt |= prtd->rate_type & 0x0F;
+
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.pktlen);
+ list_add_tail(&buf_node->list, &prtd->free_in_queue);
+ break;
+ }
+ case MODE_IS127:
+ case MODE_4GV_NB:
+ case MODE_4GV_WB:
+ case MODE_4GV_NW: {
+ *((uint32_t *)voc_pkt) = buf_node->frame.pktlen +
+ DSP_FRAME_HDR_LEN;
+ /* Advance to the header of voip packet */
+ voc_pkt = voc_pkt + sizeof(uint32_t);
+ /*
+ * Add the DSP frame info header. Header format:
+ * Bits 0-3 : Frame rate
+ */
+ *voc_pkt = buf_node->frame.frm_hdr.packet_rate & 0x0F;
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.pktlen);
+
+ list_add_tail(&buf_node->list, &prtd->free_in_queue);
+ break;
+ }
+ case MODE_G711:
+ case MODE_G711A:{
+ /* G711 frames are 10ms each but the DSP expects 20ms
+ * worth of data, so send two 10ms frames per buffer.
+ */
+ /* Add the first DSP frame info header. Header format:
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ */
+ voc_addr = voc_pkt;
+ voc_pkt = voc_pkt + sizeof(uint32_t);
+
+ *voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+ (buf_node->frame.frm_hdr.frame_type & 0x03);
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ pkt_len = buf_node->frame.pktlen + DSP_FRAME_HDR_LEN;
+
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.pktlen);
+ voc_pkt = voc_pkt + buf_node->frame.pktlen;
+
+ list_add_tail(&buf_node->list, &prtd->free_in_queue);
+
+ if (!list_empty(&prtd->in_queue)) {
+ /* Get the second buffer. */
+ buf_node = list_first_entry(&prtd->in_queue,
+ struct voip_buf_node,
+ list);
+ list_del(&buf_node->list);
+
+ /* Add the second DSP frame info header.
+ * Header format:
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ */
+ *voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+ (buf_node->frame.frm_hdr.frame_type & 0x03);
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ pkt_len = pkt_len + buf_node->frame.pktlen +
+ DSP_FRAME_HDR_LEN;
+
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.pktlen);
+
+ list_add_tail(&buf_node->list,
+ &prtd->free_in_queue);
+ } else {
+ /* Only 10ms worth of data is available, signal
+ * erasure frame.
+ */
+ *voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+ (MVS_G711A_ERASURE & 0x03);
+
+ pkt_len = pkt_len + DSP_FRAME_HDR_LEN;
+ pr_debug("%s, Only 10ms read, erase 2nd frame\n",
+ __func__);
+ }
+ *((uint32_t *)voc_addr) = pkt_len;
+ break;
+ }
+ default: {
+ *((uint32_t *)voc_pkt) = buf_node->frame.pktlen;
+ voc_pkt = voc_pkt + sizeof(uint32_t);
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.pktlen);
+ list_add_tail(&buf_node->list, &prtd->free_in_queue);
+ }
+ }
+ pr_debug("%s: frame.pktlen=%d\n", __func__,
+ buf_node->frame.pktlen);
+
+ if (prtd->mode == MODE_PCM)
+ prtd->pcm_playback_irq_pos += buf_node->frame.pktlen;
+ else
+ prtd->pcm_playback_irq_pos += prtd->pcm_count;
+
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ snd_pcm_period_elapsed(prtd->playback_substream);
+ } else {
+ *((uint32_t *)voc_pkt) = 0;
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ pr_err_ratelimited("DL data not available\n");
+ }
+ wake_up(&prtd->in_wait);
+}
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ prtd->play_samp_rate = runtime->rate;
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_playback_irq_pos = 0;
+ prtd->pcm_playback_buf_pos = 0;
+ prtd->playback_prepare = 1;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+ int ret = 0;
+
+ prtd->cap_samp_rate = runtime->rate;
+ prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_capture_irq_pos = 0;
+ prtd->pcm_capture_buf_pos = 0;
+ prtd->capture_prepare = 1;
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ pr_debug("%s: Trigger start\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->capture_start = 1;
+ else
+ prtd->playback_start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->playback_start = 0;
+ else
+ prtd->capture_start = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = &voip_info;
+ int ret = 0;
+
+ pr_debug("%s, VoIP\n", __func__);
+ mutex_lock(&prtd->lock);
+
+ runtime->hw = msm_pcm_hardware;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_debug("snd_pcm_hw_constraint_list failed\n");
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ pr_debug("snd_pcm_hw_constraint_integer failed\n");
+ goto err;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ prtd->playback_substream = substream;
+ } else {
+ prtd->capture_substream = substream;
+ }
+ runtime->private_data = prtd;
+err:
+ mutex_unlock(&prtd->lock);
+
+ return ret;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ struct voip_buf_node *buf_node = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+ unsigned long dsp_flags;
+
+ int count = frames_to_bytes(runtime, frames);
+ pr_debug("%s: count = %d, frames=%d\n", __func__, count, (int)frames);
+
+ if (prtd->voip_reset) {
+ pr_debug("%s: RESET event happened during VoIP\n", __func__);
+ return -ENETRESET;
+ }
+
+ ret = wait_event_interruptible_timeout(prtd->in_wait,
+ (!list_empty(&prtd->free_in_queue) ||
+ prtd->state == VOIP_STOPPED),
+ 1 * HZ);
+ if (prtd->voip_reset) {
+ pr_debug("%s: RESET event happened during VoIP\n", __func__);
+ return -ENETRESET;
+ }
+
+ if (ret > 0) {
+ if (count <= VOIP_MAX_VOC_PKT_SIZE) {
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ buf_node =
+ list_first_entry(&prtd->free_in_queue,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ if (prtd->mode == MODE_PCM) {
+ ret = copy_from_user(&buf_node->frame.voc_pkt,
+ buf, count);
+ if (ret) {
+ pr_err("%s: copy from user failed %d\n",
+ __func__, ret);
+ return -EFAULT;
+ }
+ buf_node->frame.pktlen = count;
+ } else {
+ ret = copy_from_user(&buf_node->frame,
+ buf, count);
+ if (ret) {
+ pr_err("%s: copy from user failed %d\n",
+ __func__, ret);
+ return -EFAULT;
+ }
+ if (buf_node->frame.pktlen >= count)
+ buf_node->frame.pktlen = count -
+ (sizeof(buf_node->frame.frm_hdr) +
+ sizeof(buf_node->frame.pktlen));
+ }
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ list_add_tail(&buf_node->list, &prtd->in_queue);
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ } else {
+ pr_err("%s: Write cnt %d is > VOIP_MAX_VOC_PKT_SIZE\n",
+ __func__, count);
+ ret = -ENOMEM;
+ }
+
+ } else if (ret == 0) {
+ pr_err("%s: No free DL buffs\n", __func__);
+ ret = -ETIMEDOUT;
+ } else {
+ pr_err("%s: playback copy was interrupted %d\n", __func__, ret);
+ }
+
+ return ret;
+}
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int count = 0;
+ struct voip_buf_node *buf_node = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+ unsigned long dsp_flags;
+ int size;
+
+ count = frames_to_bytes(runtime, frames);
+
+ pr_debug("%s: count = %d\n", __func__, count);
+
+ if (prtd->voip_reset) {
+ pr_debug("%s: RESET event happened during VoIP\n", __func__);
+ return -ENETRESET;
+ }
+
+ ret = wait_event_interruptible_timeout(prtd->out_wait,
+ (!list_empty(&prtd->out_queue) ||
+ prtd->state == VOIP_STOPPED),
+ 1 * HZ);
+
+ if (prtd->voip_reset) {
+ pr_debug("%s: RESET event happened during VoIP\n", __func__);
+ return -ENETRESET;
+ }
+
+ if (ret > 0) {
+
+ if (count <= VOIP_MAX_VOC_PKT_SIZE) {
+ spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
+ buf_node = list_first_entry(&prtd->out_queue,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
+ if (prtd->mode == MODE_PCM) {
+ ret = copy_to_user(buf,
+ &buf_node->frame.voc_pkt,
+ buf_node->frame.pktlen);
+ } else {
+ size = sizeof(buf_node->frame.frm_hdr) +
+ sizeof(buf_node->frame.pktlen) +
+ buf_node->frame.pktlen;
+
+ ret = copy_to_user(buf,
+ &buf_node->frame,
+ size);
+ }
+ if (ret) {
+ pr_err("%s: Copy to user retuned %d\n",
+ __func__, ret);
+ ret = -EFAULT;
+ }
+ spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
+ list_add_tail(&buf_node->list,
+ &prtd->free_out_queue);
+ spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
+ } else {
+ pr_err("%s: Read count %d > VOIP_MAX_VOC_PKT_SIZE\n",
+ __func__, count);
+ ret = -ENOMEM;
+ }
+
+
+ } else if (ret == 0) {
+ pr_err_ratelimited("%s: No UL data available\n", __func__);
+ ret = -ETIMEDOUT;
+ } else {
+ pr_err("%s: Read was interrupted\n", __func__);
+ ret = -ERESTARTSYS;
+ }
+ return ret;
+}
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct list_head *ptr = NULL;
+ struct list_head *next = NULL;
+ struct voip_buf_node *buf_node = NULL;
+ struct snd_dma_buffer *p_dma_buf, *c_dma_buf;
+ struct snd_pcm_substream *p_substream, *c_substream;
+ struct snd_pcm_runtime *runtime;
+ struct voip_drv_info *prtd;
+ unsigned long dsp_flags;
+
+ if (substream == NULL) {
+ pr_err("substream is NULL\n");
+ return -EINVAL;
+ }
+ runtime = substream->runtime;
+ prtd = runtime->private_data;
+
+ wake_up(&prtd->out_wait);
+
+ mutex_lock(&prtd->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->playback_prepare = 0;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->capture_prepare = 0;
+
+ if (!prtd->playback_prepare && !prtd->capture_prepare) {
+ if (prtd->state == VOIP_STARTED) {
+ prtd->voip_reset = false;
+ prtd->state = VOIP_STOPPED;
+ voc_end_voice_call(
+ voc_get_session_id(VOIP_SESSION_NAME));
+ voc_register_mvs_cb(NULL, NULL, NULL, prtd);
+ }
+ /* release all buffer */
+ /* release in_queue and free_in_queue */
+ pr_debug("release all buffer\n");
+ p_substream = prtd->playback_substream;
+ if (p_substream == NULL) {
+ pr_debug("p_substream is NULL\n");
+ goto capt;
+ }
+ p_dma_buf = &p_substream->dma_buffer;
+ if (p_dma_buf == NULL) {
+ pr_debug("p_dma_buf is NULL\n");
+ goto capt;
+ }
+ if (p_dma_buf->area != NULL) {
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ list_for_each_safe(ptr, next, &prtd->in_queue) {
+ buf_node = list_entry(ptr,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ }
+ list_for_each_safe(ptr, next, &prtd->free_in_queue) {
+ buf_node = list_entry(ptr,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ }
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ dma_free_coherent(p_substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max, p_dma_buf->area,
+ p_dma_buf->addr);
+ p_dma_buf->area = NULL;
+ }
+ /* release out_queue and free_out_queue */
+capt: c_substream = prtd->capture_substream;
+ if (c_substream == NULL) {
+ pr_debug("c_substream is NULL\n");
+ goto done;
+ }
+ c_dma_buf = &c_substream->dma_buffer;
+ if (c_substream == NULL) {
+ pr_debug("c_dma_buf is NULL.\n");
+ goto done;
+ }
+ if (c_dma_buf->area != NULL) {
+ spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
+ list_for_each_safe(ptr, next, &prtd->out_queue) {
+ buf_node = list_entry(ptr,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ }
+ list_for_each_safe(ptr, next, &prtd->free_out_queue) {
+ buf_node = list_entry(ptr,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ }
+ spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
+ dma_free_coherent(c_substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max, c_dma_buf->area,
+ c_dma_buf->addr);
+ c_dma_buf->area = NULL;
+ }
+done:
+ prtd->capture_substream = NULL;
+ prtd->playback_substream = NULL;
+ }
+ mutex_unlock(&prtd->lock);
+
+ return ret;
+}
+
+static int voip_config_vocoder(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+ uint32_t media_type = 0;
+ uint32_t rate_type = 0;
+ uint32_t evrc_min_rate_type = 0;
+ uint32_t evrc_max_rate_type = 0;
+
+ pr_debug("%s(): mode=%d, playback rate=%d, capture rate=%d\n",
+ __func__, prtd->mode, prtd->play_samp_rate,
+ prtd->cap_samp_rate);
+
+ if ((runtime->format != FORMAT_S16_LE &&
+ runtime->format != FORMAT_SPECIAL) &&
+ ((prtd->mode == MODE_AMR) || (prtd->mode == MODE_AMR_WB) ||
+ (prtd->mode == MODE_IS127) || (prtd->mode == MODE_4GV_NB) ||
+ (prtd->mode == MODE_4GV_WB) || (prtd->mode == MODE_4GV_NW) ||
+ (prtd->mode == MODE_G711) || (prtd->mode == MODE_G711A))) {
+ pr_err("%s(): mode:%d and format:%u are not matched\n",
+ __func__, prtd->mode, (uint32_t)runtime->format);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (runtime->format != FORMAT_S16_LE && (prtd->mode == MODE_PCM)) {
+ pr_err("%s(): mode:%d and format:%u are not matched\n",
+ __func__, prtd->mode, runtime->format);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((prtd->mode == MODE_PCM) ||
+ (prtd->mode == MODE_AMR) ||
+ (prtd->mode == MODE_AMR_WB) ||
+ (prtd->mode == MODE_G711) ||
+ (prtd->mode == MODE_G711A)) {
+ ret = voip_get_rate_type(prtd->mode,
+ prtd->rate,
+ &rate_type);
+ if (ret < 0) {
+ pr_err("%s(): fail at getting rate_type, ret=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ prtd->rate_type = rate_type;
+ pr_debug("rate_type=%d\n", rate_type);
+
+ } else if ((prtd->mode == MODE_IS127) ||
+ (prtd->mode == MODE_4GV_NB) ||
+ (prtd->mode == MODE_4GV_WB) ||
+ (prtd->mode == MODE_4GV_NW)) {
+ ret = voip_get_rate_type(prtd->mode,
+ prtd->evrc_min_rate,
+ &evrc_min_rate_type);
+ if (ret < 0) {
+ pr_err("%s(): fail at getting min rate, ret=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ if (evrc_min_rate_type == VOC_0_RATE)
+ evrc_min_rate_type = VOC_8_RATE;
+
+ ret = voip_get_rate_type(prtd->mode,
+ prtd->evrc_max_rate,
+ &evrc_max_rate_type);
+ if (ret < 0) {
+ pr_err("%s(): fail at getting max rate, ret=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ if (evrc_max_rate_type == VOC_0_RATE)
+ evrc_max_rate_type = VOC_1_RATE;
+
+ if (evrc_max_rate_type < evrc_min_rate_type) {
+ pr_err("%s(): Invalid EVRC min max rates: %d, %d\n",
+ __func__, evrc_min_rate_type,
+ evrc_max_rate_type);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ pr_debug("%s(): min rate=%d, max rate=%d\n",
+ __func__, evrc_min_rate_type, evrc_max_rate_type);
+ }
+ ret = voip_get_media_type(prtd->mode,
+ prtd->rate_type,
+ prtd->play_samp_rate,
+ &media_type);
+ if (ret < 0) {
+ pr_err("%s(): fail at getting media_type, ret=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ pr_debug("%s(): media_type=%d\n", __func__, media_type);
+
+ if ((prtd->play_samp_rate == 8000 && prtd->cap_samp_rate == 8000) ||
+ (prtd->play_samp_rate == 16000 && prtd->cap_samp_rate == 16000) ||
+ (prtd->play_samp_rate == 32000 && prtd->cap_samp_rate == 32000) ||
+ (prtd->play_samp_rate == 48000 && prtd->cap_samp_rate == 48000)) {
+ voc_config_vocoder(media_type, rate_type,
+ VSS_NETWORK_ID_VOIP,
+ voip_info.dtx_mode,
+ evrc_min_rate_type,
+ evrc_max_rate_type);
+ } else {
+ pr_debug("%s: Invalid rate playback %d, capture %d\n",
+ __func__, prtd->play_samp_rate,
+ prtd->cap_samp_rate);
+
+ ret = -EINVAL;
+ }
+done:
+
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ mutex_lock(&prtd->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+
+ if (prtd->playback_prepare && prtd->capture_prepare
+ && (prtd->state != VOIP_STARTED)) {
+ ret = voip_config_vocoder(substream);
+ if (ret < 0) {
+ pr_err("%s(): fail at configuring vocoder for voip, ret=%d\n",
+ __func__, ret);
+
+ goto done;
+ }
+
+ /* Initialaizing cb variables */
+ voc_register_mvs_cb(voip_process_ul_pkt,
+ voip_process_dl_pkt,
+ voip_ssr_cb_fn, prtd);
+
+ ret = voc_start_voice_call(
+ voc_get_session_id(VOIP_SESSION_NAME));
+
+ if (ret < 0) {
+ pr_err("%s: voc_start_voice_call() failed err %d",
+ __func__, ret);
+
+ goto done;
+ }
+ prtd->state = VOIP_STARTED;
+ }
+done:
+ mutex_unlock(&prtd->lock);
+
+ return ret;
+}
+
+static snd_pcm_uframes_t
+msm_pcm_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ if (prtd->pcm_playback_irq_pos >= prtd->pcm_size)
+ prtd->pcm_playback_irq_pos = 0;
+ return bytes_to_frames(runtime, (prtd->pcm_playback_irq_pos));
+}
+
+static snd_pcm_uframes_t
+msm_pcm_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size)
+ prtd->pcm_capture_irq_pos = 0;
+ return bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos));
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ snd_pcm_uframes_t ret = 0;
+ pr_debug("%s\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_pointer(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_pointer(substream);
+ return ret;
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ pr_debug("%s\n", __func__);
+ dma_mmap_coherent(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return 0;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct voip_buf_node *buf_node = NULL;
+ int i = 0, offset = 0;
+
+ pr_debug("%s: voip\n", __func__);
+
+ mutex_lock(&voip_info.lock);
+
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+
+ dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max,
+ &dma_buf->addr, GFP_KERNEL);
+ if (!dma_buf->area) {
+ pr_err("%s:MSM VOIP dma_alloc failed\n", __func__);
+ mutex_unlock(&voip_info.lock);
+ return -ENOMEM;
+ }
+
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
+ buf_node = (void *)dma_buf->area + offset;
+
+ list_add_tail(&buf_node->list,
+ &voip_info.free_in_queue);
+ offset = offset + sizeof(struct voip_buf_node);
+ }
+ } else {
+ for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
+ buf_node = (void *) dma_buf->area + offset;
+ list_add_tail(&buf_node->list,
+ &voip_info.free_out_queue);
+ offset = offset + sizeof(struct voip_buf_node);
+ }
+ }
+
+ mutex_unlock(&voip_info.lock);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int msm_voip_mode_config_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ mutex_lock(&voip_info.lock);
+
+ ucontrol->value.integer.value[0] = voip_info.mode;
+
+ mutex_unlock(&voip_info.lock);
+
+ return 0;
+}
+
+static int msm_voip_mode_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ mutex_lock(&voip_info.lock);
+
+ voip_info.mode = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: mode=%d\n", __func__, voip_info.mode);
+
+ mutex_unlock(&voip_info.lock);
+
+ return 0;
+}
+
+static int msm_voip_rate_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int rate = ucontrol->value.integer.value[0];
+
+ mutex_lock(&voip_info.lock);
+
+ if (voip_info.rate != rate) {
+ voip_info.rate = rate;
+ pr_debug("%s: rate=%d\n", __func__, voip_info.rate);
+
+ if (voip_info.state == VOIP_STARTED &&
+ (voip_info.mode == MODE_AMR ||
+ voip_info.mode == MODE_AMR_WB)) {
+ ret = voip_config_vocoder(
+ voip_info.capture_substream);
+ if (ret) {
+ pr_err("%s:Failed to configure vocoder, ret=%d\n",
+ __func__, ret);
+
+ goto done;
+ }
+
+ ret = voc_update_amr_vocoder_rate(
+ voc_get_session_id(VOIP_SESSION_NAME));
+ if (ret) {
+ pr_err("%s:Failed to update AMR rate, ret=%d\n",
+ __func__, ret);
+ }
+ }
+ }
+
+done:
+ mutex_unlock(&voip_info.lock);
+
+ return ret;
+}
+
+static int msm_voip_evrc_min_max_rate_config_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ mutex_lock(&voip_info.lock);
+
+ ucontrol->value.integer.value[0] = voip_info.evrc_min_rate;
+ ucontrol->value.integer.value[1] = voip_info.evrc_max_rate;
+
+ mutex_unlock(&voip_info.lock);
+
+ return 0;
+}
+
+static int msm_voip_evrc_min_max_rate_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ mutex_lock(&voip_info.lock);
+
+ voip_info.evrc_min_rate = ucontrol->value.integer.value[0];
+ voip_info.evrc_max_rate = ucontrol->value.integer.value[1];
+
+ pr_debug("%s(): evrc_min_rate=%d,evrc_max_rate=%d\n", __func__,
+ voip_info.evrc_min_rate, voip_info.evrc_max_rate);
+
+ mutex_unlock(&voip_info.lock);
+
+ return 0;
+}
+
+static int voip_get_rate_type(uint32_t mode, uint32_t rate,
+ uint32_t *rate_type)
+{
+ int ret = 0;
+
+ switch (mode) {
+ case MODE_AMR: {
+ switch (rate) {
+ case 4750:
+ *rate_type = AMR_RATE_4750;
+ break;
+ case 5150:
+ *rate_type = AMR_RATE_5150;
+ break;
+ case 5900:
+ *rate_type = AMR_RATE_5900;
+ break;
+ case 6700:
+ *rate_type = AMR_RATE_6700;
+ break;
+ case 7400:
+ *rate_type = AMR_RATE_7400;
+ break;
+ case 7950:
+ *rate_type = AMR_RATE_7950;
+ break;
+ case 10200:
+ *rate_type = AMR_RATE_10200;
+ break;
+ case 12200:
+ *rate_type = AMR_RATE_12200;
+ break;
+ default:
+ pr_err("wrong rate for AMR NB.\n");
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+ case MODE_AMR_WB: {
+ switch (rate) {
+ case 6600:
+ *rate_type = AMR_RATE_6600 - AMR_RATE_6600;
+ break;
+ case 8850:
+ *rate_type = AMR_RATE_8850 - AMR_RATE_6600;
+ break;
+ case 12650:
+ *rate_type = AMR_RATE_12650 - AMR_RATE_6600;
+ break;
+ case 14250:
+ *rate_type = AMR_RATE_14250 - AMR_RATE_6600;
+ break;
+ case 15850:
+ *rate_type = AMR_RATE_15850 - AMR_RATE_6600;
+ break;
+ case 18250:
+ *rate_type = AMR_RATE_18250 - AMR_RATE_6600;
+ break;
+ case 19850:
+ *rate_type = AMR_RATE_19850 - AMR_RATE_6600;
+ break;
+ case 23050:
+ *rate_type = AMR_RATE_23050 - AMR_RATE_6600;
+ break;
+ case 23850:
+ *rate_type = AMR_RATE_23850 - AMR_RATE_6600;
+ break;
+ default:
+ pr_err("wrong rate for AMR_WB.\n");
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+ case MODE_PCM: {
+ *rate_type = 0;
+ break;
+ }
+ case MODE_IS127:
+ case MODE_4GV_NB:
+ case MODE_4GV_WB: {
+ switch (rate) {
+ case VOC_0_RATE:
+ case VOC_8_RATE:
+ case VOC_4_RATE:
+ case VOC_2_RATE:
+ case VOC_1_RATE:
+ *rate_type = rate;
+ break;
+ default:
+ pr_err("wrong rate for IS127/4GV_NB/WB.\n");
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+ case MODE_4GV_NW: {
+ switch (rate) {
+ case VOC_0_RATE:
+ case VOC_8_RATE:
+ case VOC_4_RATE:
+ case VOC_2_RATE:
+ case VOC_1_RATE:
+ case VOC_8_RATE_NC:
+ *rate_type = rate;
+ break;
+ default:
+ pr_err("wrong rate for 4GV_NW.\n");
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+ case MODE_G711:
+ case MODE_G711A:
+ *rate_type = rate;
+ break;
+ default:
+ pr_err("wrong mode type.\n");
+ ret = -EINVAL;
+ }
+ pr_debug("%s, mode=%d, rate=%u, rate_type=%d\n",
+ __func__, mode, rate, *rate_type);
+ return ret;
+}
+
+static int voip_get_media_type(uint32_t mode, uint32_t rate_type,
+ unsigned int samp_rate,
+ unsigned int *media_type)
+{
+ int ret = 0;
+
+ pr_debug("%s: mode=%d, samp_rate=%d\n", __func__,
+ mode, samp_rate);
+ switch (mode) {
+ case MODE_AMR:
+ *media_type = VSS_MEDIA_ID_AMR_NB_MODEM;
+ break;
+ case MODE_AMR_WB:
+ *media_type = VSS_MEDIA_ID_AMR_WB_MODEM;
+ break;
+ case MODE_PCM:
+ if (samp_rate == 8000)
+ *media_type = VSS_MEDIA_ID_PCM_8_KHZ;
+ else if (samp_rate == 16000)
+ *media_type = VSS_MEDIA_ID_PCM_16_KHZ;
+ else if (samp_rate == 32000)
+ *media_type = VSS_MEDIA_ID_PCM_32_KHZ;
+ else
+ *media_type = VSS_MEDIA_ID_PCM_48_KHZ;
+ break;
+ case MODE_IS127: /* EVRC-A */
+ *media_type = VSS_MEDIA_ID_EVRC_MODEM;
+ break;
+ case MODE_4GV_NB: /* EVRC-B */
+ *media_type = VSS_MEDIA_ID_4GV_NB_MODEM;
+ break;
+ case MODE_4GV_WB: /* EVRC-WB */
+ *media_type = VSS_MEDIA_ID_4GV_WB_MODEM;
+ break;
+ case MODE_4GV_NW: /* EVRC-NW */
+ *media_type = VSS_MEDIA_ID_4GV_NW_MODEM;
+ break;
+ case MODE_G711:
+ case MODE_G711A:
+ if (rate_type == MVS_G711A_MODE_MULAW)
+ *media_type = VSS_MEDIA_ID_G711_MULAW;
+ else
+ *media_type = VSS_MEDIA_ID_G711_ALAW;
+ break;
+ default:
+ pr_debug(" input mode is not supported\n");
+ ret = -EINVAL;
+ }
+
+ pr_debug("%s: media_type is 0x%x\n", __func__, *media_type);
+
+ return ret;
+}
+
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ pr_debug("msm_asoc_pcm_new\n");
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+ .probe = msm_pcm_voip_probe,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ if (!is_voc_initialized()) {
+ pr_debug("%s: voice module not initialized yet, deferring probe()\n",
+ __func__);
+
+ rc = -EPROBE_DEFER;
+ goto done;
+ }
+
+ rc = voc_alloc_cal_shared_memory();
+ if (rc == -EPROBE_DEFER) {
+ pr_debug("%s: memory allocation for calibration deferred %d\n",
+ __func__, rc);
+
+ goto done;
+ } else if (rc < 0) {
+ pr_err("%s: memory allocation for calibration failed %d\n",
+ __func__, rc);
+ }
+
+ rc = voc_alloc_voip_shared_memory();
+ if (rc < 0) {
+ pr_err("%s: error allocating shared mem err %d\n",
+ __func__, rc);
+ }
+
+
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ rc = snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+
+done:
+ return rc;
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_voip_dt_match[] = {
+ {.compatible = "qcom,msm-voip-dsp"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_voip_dt_match);
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-voip-dsp",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_voip_dt_match,
+ },
+ .probe = msm_pcm_probe,
+ .remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ memset(&voip_info, 0, sizeof(voip_info));
+ voip_info.mode = MODE_PCM;
+ mutex_init(&voip_info.lock);
+
+ spin_lock_init(&voip_info.dsp_lock);
+ spin_lock_init(&voip_info.dsp_ul_lock);
+
+ init_waitqueue_head(&voip_info.out_wait);
+ init_waitqueue_head(&voip_info.in_wait);
+
+ INIT_LIST_HEAD(&voip_info.in_queue);
+ INIT_LIST_HEAD(&voip_info.free_in_queue);
+ INIT_LIST_HEAD(&voip_info.out_queue);
+ INIT_LIST_HEAD(&voip_info.free_out_queue);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
new file mode 100644
index 000000000000..c975a7e48e78
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
@@ -0,0 +1,1487 @@
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6common.h>
+#include <sound/asound.h>
+#include <sound/q6audio-v2.h>
+#include <sound/tlv.h>
+
+#include "msm-qti-pp-config.h"
+#include "msm-pcm-routing-v2.h"
+
+/* EQUALIZER */
+/* Equal to Frontend after last of the MULTIMEDIA SESSIONS */
+#define MAX_EQ_SESSIONS MSM_FRONTEND_DAI_CS_VOICE
+
+enum {
+ EQ_BAND1 = 0,
+ EQ_BAND2,
+ EQ_BAND3,
+ EQ_BAND4,
+ EQ_BAND5,
+ EQ_BAND6,
+ EQ_BAND7,
+ EQ_BAND8,
+ EQ_BAND9,
+ EQ_BAND10,
+ EQ_BAND11,
+ EQ_BAND12,
+ EQ_BAND_MAX,
+};
+
+/* Audio Sphere data structures */
+struct msm_audio_pp_asphere_state_s {
+ uint32_t enabled;
+ uint32_t strength;
+ uint32_t mode;
+ uint32_t version;
+ int port_id[AFE_MAX_PORTS];
+ int copp_idx[AFE_MAX_PORTS];
+ bool initialized;
+ uint32_t enabled_prev;
+ uint32_t strength_prev;
+};
+
+static struct msm_audio_pp_asphere_state_s asphere_state;
+
+struct msm_audio_eq_stream_config eq_data[MAX_EQ_SESSIONS];
+
+static int msm_route_hfp_vol_control;
+static const DECLARE_TLV_DB_LINEAR(hfp_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS);
+
+static int msm_route_icc_vol_control;
+static const DECLARE_TLV_DB_LINEAR(icc_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS);
+
+static int msm_route_pri_auxpcm_lb_vol_ctrl;
+static const DECLARE_TLV_DB_LINEAR(pri_auxpcm_lb_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS);
+
+static int msm_route_sec_auxpcm_lb_vol_ctrl;
+static const DECLARE_TLV_DB_LINEAR(sec_auxpcm_lb_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS);
+
+static int msm_multichannel_ec_primary_mic_ch;
+
+static void msm_qti_pp_send_eq_values_(int eq_idx)
+{
+ int result;
+ struct msm_pcm_routing_fdai_data fe_dai;
+ struct audio_client *ac = NULL;
+
+ msm_pcm_routing_get_fedai_info(eq_idx, SESSION_TYPE_RX, &fe_dai);
+ ac = q6asm_get_audio_client(fe_dai.strm_id);
+
+ if (ac == NULL) {
+ pr_err("%s: Could not get audio client for session: %d\n",
+ __func__, fe_dai.strm_id);
+ goto done;
+ }
+
+ result = q6asm_equalizer(ac, &eq_data[eq_idx]);
+
+ if (result < 0)
+ pr_err("%s: Call to ASM equalizer failed, returned = %d\n",
+ __func__, result);
+done:
+ return;
+}
+
+static int msm_qti_pp_get_eq_enable_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+
+ if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS))
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = eq_data[eq_idx].enable;
+
+ pr_debug("%s: EQ #%d enable %d\n", __func__,
+ eq_idx, eq_data[eq_idx].enable);
+ return 0;
+}
+
+static int msm_qti_pp_put_eq_enable_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int value = ucontrol->value.integer.value[0];
+
+ if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS))
+ return -EINVAL;
+ pr_debug("%s: EQ #%d enable %d\n", __func__,
+ eq_idx, value);
+ eq_data[eq_idx].enable = value;
+ msm_pcm_routing_acquire_lock();
+ msm_qti_pp_send_eq_values_(eq_idx);
+ msm_pcm_routing_release_lock();
+ return 0;
+}
+
+static int msm_qti_pp_get_eq_band_count_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+
+ if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS))
+ return -EINVAL;
+ ucontrol->value.integer.value[0] = eq_data[eq_idx].num_bands;
+
+ pr_debug("%s: EQ #%d bands %d\n", __func__,
+ eq_idx, eq_data[eq_idx].num_bands);
+ return eq_data[eq_idx].num_bands;
+}
+
+static int msm_qti_pp_put_eq_band_count_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int value = ucontrol->value.integer.value[0];
+
+ if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS))
+ return -EINVAL;
+
+ pr_debug("%s: EQ #%d bands %d\n", __func__,
+ eq_idx, value);
+ eq_data[eq_idx].num_bands = value;
+ return 0;
+}
+
+static int msm_qti_pp_put_dtmf_module_enable
+ (struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+ struct msm_pcm_routing_fdai_data fe_dai;
+ struct audio_client *ac = NULL;
+ struct param_hdr_v3 param_hdr;
+ int ret = 0;
+ u32 flag = (bool)ucontrol->value.integer.value[0];
+
+ fe_id = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ msm_pcm_routing_get_fedai_info(fe_id, SESSION_TYPE_RX, &fe_dai);
+ ac = q6asm_get_audio_client(fe_dai.strm_id);
+
+ if (ac == NULL) {
+ pr_err("%s ac is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_DTMF_DETECTION;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE;
+ param_hdr.param_size = 4;
+
+ ret = q6asm_pack_and_set_pp_param_in_band(ac,
+ param_hdr, (u8 *)&flag);
+
+done:
+ return ret;
+}
+
+static int msm_qti_pp_get_eq_band_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS) ||
+ (band_idx < EQ_BAND1) || (band_idx >= EQ_BAND_MAX))
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] =
+ eq_data[eq_idx].eq_bands[band_idx].band_idx;
+ ucontrol->value.integer.value[1] =
+ eq_data[eq_idx].eq_bands[band_idx].filter_type;
+ ucontrol->value.integer.value[2] =
+ eq_data[eq_idx].eq_bands[band_idx].center_freq_hz;
+ ucontrol->value.integer.value[3] =
+ eq_data[eq_idx].eq_bands[band_idx].filter_gain;
+ ucontrol->value.integer.value[4] =
+ eq_data[eq_idx].eq_bands[band_idx].q_factor;
+
+ pr_debug("%s: band_idx = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].band_idx);
+ pr_debug("%s: filter_type = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].filter_type);
+ pr_debug("%s: center_freq_hz = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].center_freq_hz);
+ pr_debug("%s: filter_gain = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].filter_gain);
+ pr_debug("%s: q_factor = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].q_factor);
+ return 0;
+}
+
+static int msm_qti_pp_put_eq_band_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS) ||
+ (band_idx < EQ_BAND1) || (band_idx >= EQ_BAND_MAX))
+ return -EINVAL;
+
+ eq_data[eq_idx].eq_bands[band_idx].band_idx =
+ ucontrol->value.integer.value[0];
+ eq_data[eq_idx].eq_bands[band_idx].filter_type =
+ ucontrol->value.integer.value[1];
+ eq_data[eq_idx].eq_bands[band_idx].center_freq_hz =
+ ucontrol->value.integer.value[2];
+ eq_data[eq_idx].eq_bands[band_idx].filter_gain =
+ ucontrol->value.integer.value[3];
+ eq_data[eq_idx].eq_bands[band_idx].q_factor =
+ ucontrol->value.integer.value[4];
+ return 0;
+}
+
+#ifdef CONFIG_QTI_PP
+void msm_qti_pp_send_eq_values(int fedai_id)
+{
+ if (eq_data[fedai_id].enable)
+ msm_qti_pp_send_eq_values_(fedai_id);
+}
+
+/* CUSTOM MIXING */
+int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx,
+ unsigned int session_id,
+ uint16_t op_FL_ip_FL_weight,
+ uint16_t op_FL_ip_FR_weight,
+ uint16_t op_FR_ip_FL_weight,
+ uint16_t op_FR_ip_FR_weight)
+{
+ char *params_value;
+ int *update_params_value32, rc = 0;
+ int16_t *update_params_value16 = 0;
+ uint32_t params_length = CUSTOM_STEREO_PAYLOAD_SIZE * sizeof(uint32_t);
+ uint32_t avail_length = params_length;
+ pr_debug("%s: port_id - %d, session id - %d\n", __func__, port_id,
+ session_id);
+ params_value = kzalloc(params_length, GFP_KERNEL);
+ if (!params_value) {
+ pr_err("%s, params memory alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ update_params_value32 = (int *)params_value;
+ if (avail_length < 2 * sizeof(uint32_t))
+ goto skip_send_cmd;
+ *update_params_value32++ = MTMX_MODULE_ID_DEFAULT_CHMIXER;
+ *update_params_value32++ = DEFAULT_CHMIXER_PARAM_ID_COEFF;
+ avail_length = avail_length - (2 * sizeof(uint32_t));
+
+ update_params_value16 = (int16_t *)update_params_value32;
+ if (avail_length < 10 * sizeof(uint16_t))
+ goto skip_send_cmd;
+ *update_params_value16++ = CUSTOM_STEREO_CMD_PARAM_SIZE;
+ /*for alignment only*/
+ *update_params_value16++ = 0;
+ /*index is 32-bit param in little endian*/
+ *update_params_value16++ = CUSTOM_STEREO_INDEX_PARAM;
+ *update_params_value16++ = 0;
+ /*for stereo mixing num out ch*/
+ *update_params_value16++ = CUSTOM_STEREO_NUM_OUT_CH;
+ /*for stereo mixing num in ch*/
+ *update_params_value16++ = CUSTOM_STEREO_NUM_IN_CH;
+
+ /* Out ch map FL/FR*/
+ *update_params_value16++ = PCM_CHANNEL_FL;
+ *update_params_value16++ = PCM_CHANNEL_FR;
+
+ /* In ch map FL/FR*/
+ *update_params_value16++ = PCM_CHANNEL_FL;
+ *update_params_value16++ = PCM_CHANNEL_FR;
+ avail_length = avail_length - (10 * sizeof(uint16_t));
+ /* weighting coefficients as name suggests,
+ mixing will be done according to these coefficients*/
+ if (avail_length < 4 * sizeof(uint16_t))
+ goto skip_send_cmd;
+ *update_params_value16++ = op_FL_ip_FL_weight;
+ *update_params_value16++ = op_FL_ip_FR_weight;
+ *update_params_value16++ = op_FR_ip_FL_weight;
+ *update_params_value16++ = op_FR_ip_FR_weight;
+ avail_length = avail_length - (4 * sizeof(uint16_t));
+ if (params_length) {
+ rc = adm_set_pspd_matrix_params(port_id,
+ copp_idx,
+ session_id,
+ params_value,
+ params_length,
+ ADM_MATRIX_ID_AUDIO_RX);
+ if (rc) {
+ pr_err("%s: send params failed rc=%d\n", __func__, rc);
+ kfree(params_value);
+ return -EINVAL;
+ }
+ }
+ kfree(params_value);
+ return 0;
+skip_send_cmd:
+ pr_err("%s: insufficient memory, send cmd failed\n",
+ __func__);
+ kfree(params_value);
+ return -ENOMEM;
+}
+#endif /* CONFIG_QTI_PP */
+
+/* RMS */
+static int msm_qti_pp_get_rms_value_control(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ int be_idx = 0, copp_idx;
+ char *param_value;
+ int *update_param_value;
+ uint32_t param_size = (RMS_PAYLOAD_LEN + 1) * sizeof(uint32_t);
+ struct msm_pcm_routing_bdai_data msm_bedai;
+ struct param_hdr_v3 param_hdr = {0};
+
+ param_value = kzalloc(param_size, GFP_KERNEL);
+ if (!param_value)
+ return -ENOMEM;
+ msm_pcm_routing_acquire_lock();
+ for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) {
+ msm_pcm_routing_get_bedai_info(be_idx, &msm_bedai);
+ if (msm_bedai.port_id == SLIMBUS_0_TX)
+ break;
+ }
+ if ((be_idx >= MSM_BACKEND_DAI_MAX) || !msm_bedai.active) {
+ pr_err("%s, back not active to query rms be_idx:%d\n",
+ __func__, be_idx);
+ rc = -EINVAL;
+ goto get_rms_value_err;
+ }
+ copp_idx = adm_get_default_copp_idx(SLIMBUS_0_TX);
+ if ((copp_idx < 0) || (copp_idx > MAX_COPPS_PER_PORT)) {
+ pr_err("%s, no active copp to query rms copp_idx:%d\n",
+ __func__ , copp_idx);
+ rc = -EINVAL;
+ goto get_rms_value_err;
+ }
+ param_hdr.module_id = RMS_MODULEID_APPI_PASSTHRU;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = RMS_PARAM_FIRST_SAMPLE;
+ param_hdr.param_size = param_size;
+ rc = adm_get_pp_params(SLIMBUS_0_TX, copp_idx, ADM_CLIENT_ID_DEFAULT,
+ NULL, &param_hdr, param_value);
+ if (rc) {
+ pr_err("%s: get parameters failed rc=%d\n", __func__, rc);
+ rc = -EINVAL;
+ goto get_rms_value_err;
+ }
+ update_param_value = (int *)param_value;
+ ucontrol->value.integer.value[0] = update_param_value[0];
+
+ pr_debug("%s: FROM DSP value[0] 0x%x\n",
+ __func__, update_param_value[0]);
+get_rms_value_err:
+ msm_pcm_routing_release_lock();
+ kfree(param_value);
+ return rc;
+}
+
+static int msm_qti_pp_put_rms_value_control(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* not used */
+ return 0;
+}
+
+/* VOLUME */
+static int msm_route_fm_vol_control;
+static int msm_afe_lb_vol_ctrl;
+static int msm_afe_sec_mi2s_lb_vol_ctrl;
+static int msm_afe_tert_mi2s_lb_vol_ctrl;
+static int msm_afe_quat_mi2s_lb_vol_ctrl;
+static int msm_afe_slimbus_7_lb_vol_ctrl;
+static int msm_afe_slimbus_8_lb_vol_ctrl;
+static const DECLARE_TLV_DB_LINEAR(fm_rx_vol_gain, 0, INT_RX_VOL_MAX_STEPS);
+static const DECLARE_TLV_DB_LINEAR(afe_lb_vol_gain, 0, INT_RX_VOL_MAX_STEPS);
+
+static int msm_qti_pp_get_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_route_fm_vol_control;
+ return 0;
+}
+
+static int msm_qti_pp_set_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ afe_loopback_gain(INT_FM_TX , ucontrol->value.integer.value[0]);
+
+ msm_route_fm_vol_control = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int msm_qti_pp_get_pri_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_afe_lb_vol_ctrl;
+ return 0;
+}
+
+static int msm_qti_pp_set_pri_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ afe_loopback_gain(AFE_PORT_ID_PRIMARY_MI2S_TX,
+ ucontrol->value.integer.value[0]);
+
+ msm_afe_lb_vol_ctrl = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int msm_qti_pp_get_sec_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_afe_sec_mi2s_lb_vol_ctrl;
+ return 0;
+}
+
+static int msm_qti_pp_set_sec_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ afe_loopback_gain(AFE_PORT_ID_SECONDARY_MI2S_TX,
+ ucontrol->value.integer.value[0]);
+ msm_afe_sec_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int msm_qti_pp_get_tert_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_afe_tert_mi2s_lb_vol_ctrl;
+ return 0;
+}
+
+static int msm_qti_pp_set_tert_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ afe_loopback_gain(AFE_PORT_ID_TERTIARY_MI2S_TX,
+ ucontrol->value.integer.value[0]);
+ msm_afe_tert_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int msm_qti_pp_get_slimbus_7_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_afe_slimbus_7_lb_vol_ctrl;
+ return 0;
+}
+
+static int msm_qti_pp_set_slimbus_7_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = afe_loopback_gain(SLIMBUS_7_TX,
+ ucontrol->value.integer.value[0]);
+
+ if (ret)
+ pr_err("%s: failed to set LB vol for SLIMBUS_7_TX, err %d\n",
+ __func__, ret);
+ else
+ msm_afe_slimbus_7_lb_vol_ctrl =
+ ucontrol->value.integer.value[0];
+
+ return ret;
+}
+
+static int msm_qti_pp_get_slimbus_8_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_afe_slimbus_8_lb_vol_ctrl;
+ return 0;
+}
+
+static int msm_qti_pp_set_slimbus_8_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+
+ ret = afe_loopback_gain(SLIMBUS_8_TX,
+ ucontrol->value.integer.value[0]);
+
+ if (ret)
+ pr_err("%s: failed to set LB vol for SLIMBUS_8_TX", __func__);
+ else
+ msm_afe_slimbus_8_lb_vol_ctrl =
+ ucontrol->value.integer.value[0];
+
+ return ret;
+}
+
+static int msm_qti_pp_get_icc_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_route_icc_vol_control;
+ return 0;
+}
+
+static int msm_qti_pp_set_icc_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ adm_set_mic_gain(AFE_PORT_ID_QUATERNARY_TDM_TX,
+ adm_get_default_copp_idx(AFE_PORT_ID_QUATERNARY_TDM_TX),
+ ucontrol->value.integer.value[0]);
+ msm_route_icc_vol_control = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int msm_qti_pp_get_quat_mi2s_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_afe_quat_mi2s_lb_vol_ctrl;
+ return 0;
+}
+
+static int msm_qti_pp_set_quat_mi2s_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ afe_loopback_gain(AFE_PORT_ID_QUATERNARY_MI2S_TX,
+ ucontrol->value.integer.value[0]);
+
+ msm_afe_quat_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int msm_qti_pp_get_hfp_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_route_hfp_vol_control;
+ return 0;
+}
+
+static int msm_qti_pp_set_hfp_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ afe_loopback_gain(INT_BT_SCO_TX , ucontrol->value.integer.value[0]);
+
+ msm_route_hfp_vol_control = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int msm_qti_pp_get_pri_auxpcm_lb_vol_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_route_pri_auxpcm_lb_vol_ctrl;
+ pr_debug("%s: Volume = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_qti_pp_set_pri_auxpcm_lb_vol_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ afe_loopback_gain(mc->reg, ucontrol->value.integer.value[0]);
+
+ msm_route_pri_auxpcm_lb_vol_ctrl = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int msm_qti_pp_get_sec_auxpcm_lb_vol_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_route_sec_auxpcm_lb_vol_ctrl;
+ pr_debug("%s: Volume = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_qti_pp_set_sec_auxpcm_lb_vol_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ afe_loopback_gain(mc->reg, ucontrol->value.integer.value[0]);
+
+ msm_route_sec_auxpcm_lb_vol_ctrl = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int msm_qti_pp_get_channel_map_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL_V2] = {0};
+ int i;
+
+ adm_get_multi_ch_map(channel_map, ADM_PATH_PLAYBACK);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ ucontrol->value.integer.value[i] = (unsigned) channel_map[i];
+ return 0;
+}
+
+static int msm_qti_pp_put_channel_map_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL_V2] = {0};
+ int i;
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++)
+ channel_map[i] = (char)(ucontrol->value.integer.value[i]);
+ adm_set_multi_ch_map(channel_map, ADM_PATH_PLAYBACK);
+
+ return 0;
+}
+
+/* Audio Sphere functions */
+
+static void msm_qti_pp_asphere_init_state(void)
+{
+ int i;
+ if (asphere_state.initialized)
+ return;
+ asphere_state.initialized = true;
+ for (i = 0; i < AFE_MAX_PORTS; i++) {
+ asphere_state.port_id[i] = -1;
+ asphere_state.copp_idx[i] = -1;
+ }
+ asphere_state.enabled = 0;
+ asphere_state.strength = 0;
+ asphere_state.mode = 0;
+ asphere_state.version = 0;
+ asphere_state.enabled_prev = 0;
+ asphere_state.strength_prev = 0;
+}
+
+static int msm_qti_pp_asphere_send_params(int port_id, int copp_idx, bool force)
+{
+ u8 *packed_params = NULL;
+ u32 packed_params_size = 0;
+ u32 param_size = 0;
+ struct param_hdr_v3 param_hdr = {0};
+ bool set_enable = force ||
+ (asphere_state.enabled != asphere_state.enabled_prev);
+ bool set_strength = asphere_state.enabled == 1 && (set_enable ||
+ (asphere_state.strength != asphere_state.strength_prev));
+ int param_count = 0;
+ int ret = 0;
+
+ if (set_enable)
+ param_count++;
+ if (set_strength)
+ param_count++;
+
+ if (param_count == 0) {
+ pr_debug("%s: Nothing to send, exiting\n", __func__);
+ return 0;
+ }
+
+ pr_debug("%s: port_id %d, copp_id %d, forced %d, param_count %d\n",
+ __func__, port_id, copp_idx, force, param_count);
+ pr_debug("%s: enable prev:%u cur:%u, strength prev:%u cur:%u\n",
+ __func__, asphere_state.enabled_prev, asphere_state.enabled,
+ asphere_state.strength_prev, asphere_state.strength);
+
+ packed_params_size =
+ param_count * (sizeof(struct param_hdr_v3) + sizeof(uint32_t));
+ packed_params = kzalloc(packed_params_size, GFP_KERNEL);
+ if (!packed_params)
+ return -ENOMEM;
+
+ packed_params_size = 0;
+ param_hdr.module_id = AUDPROC_MODULE_ID_AUDIOSPHERE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ if (set_strength) {
+ /* add strength command */
+ param_hdr.param_id = AUDPROC_PARAM_ID_AUDIOSPHERE_STRENGTH;
+ param_hdr.param_size = sizeof(asphere_state.strength);
+ ret = q6common_pack_pp_params(packed_params +
+ packed_params_size,
+ &param_hdr,
+ (u8 *) &asphere_state.strength,
+ &param_size);
+ if (ret) {
+ pr_err("%s: Failed to pack params, error %d\n",
+ __func__, ret);
+ goto done;
+ }
+ packed_params_size += param_size;
+ }
+ if (set_enable) {
+ /* add enable command */
+ param_hdr.param_id = AUDPROC_PARAM_ID_AUDIOSPHERE_ENABLE;
+ param_hdr.param_size = sizeof(asphere_state.enabled);
+ q6common_pack_pp_params(packed_params + packed_params_size,
+ &param_hdr,
+ (u8 *) &asphere_state.enabled,
+ &param_size);
+ if (ret) {
+ pr_err("%s: Failed to pack params, error %d\n",
+ __func__, ret);
+ goto done;
+ }
+ packed_params_size += param_size;
+ }
+
+ pr_debug("%s: packed data size: %d\n", __func__, packed_params_size);
+ ret = adm_set_pp_params(port_id, copp_idx, NULL, packed_params,
+ packed_params_size);
+ if (ret)
+ pr_err("%s: set param failed with err=%d\n", __func__, ret);
+
+done:
+ kfree(packed_params);
+ return 0;
+}
+
+#if defined(CONFIG_QTI_PP) && defined(CONFIG_QTI_PP_AUDIOSPHERE)
+int msm_qti_pp_asphere_init(int port_id, int copp_idx)
+{
+ int index = adm_validate_and_get_port_index(port_id);
+
+ pr_debug("%s, port_id %d, copp_id %d\n", __func__, port_id, copp_idx);
+ if (index < 0) {
+ pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index,
+ port_id);
+ return -EINVAL;
+ }
+ msm_qti_pp_asphere_init_state();
+
+ asphere_state.port_id[index] = port_id;
+ asphere_state.copp_idx[index] = copp_idx;
+
+ if (asphere_state.enabled)
+ msm_qti_pp_asphere_send_params(port_id, copp_idx, true);
+
+ return 0;
+}
+
+void msm_qti_pp_asphere_deinit(int port_id)
+{
+ int index = adm_validate_and_get_port_index(port_id);
+
+ pr_debug("%s, port_id %d\n", __func__, port_id);
+ if (index < 0) {
+ pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index,
+ port_id);
+ return;
+ }
+
+ if (asphere_state.port_id[index] == port_id) {
+ asphere_state.port_id[index] = -1;
+ asphere_state.copp_idx[index] = -1;
+ }
+}
+#endif
+
+static int msm_qti_pp_asphere_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (!asphere_state.initialized)
+ return -EAGAIN;
+ ucontrol->value.integer.value[0] = asphere_state.enabled;
+ ucontrol->value.integer.value[1] = asphere_state.strength;
+ pr_debug("%s, enable %u, strength %u\n", __func__,
+ asphere_state.enabled, asphere_state.strength);
+ return 0;
+}
+
+static int msm_qti_pp_asphere_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int32_t enable = ucontrol->value.integer.value[0];
+ int32_t strength = ucontrol->value.integer.value[1];
+ int i;
+
+ pr_debug("%s, enable %u, strength %u\n", __func__, enable, strength);
+
+ msm_qti_pp_asphere_init_state();
+
+ if (enable == 0 || enable == 1) {
+ asphere_state.enabled_prev = asphere_state.enabled;
+ asphere_state.enabled = enable;
+ }
+
+ if (strength >= 0 && strength <= 1000) {
+ asphere_state.strength_prev = asphere_state.strength;
+ asphere_state.strength = strength;
+ }
+
+ if (asphere_state.strength != asphere_state.strength_prev ||
+ asphere_state.enabled != asphere_state.enabled_prev) {
+ for (i = 0; i < AFE_MAX_PORTS; i++) {
+ if (asphere_state.port_id[i] >= 0)
+ msm_qti_pp_asphere_send_params(
+ asphere_state.port_id[i],
+ asphere_state.copp_idx[i],
+ false);
+ }
+ }
+ return 0;
+}
+
+int msm_adsp_init_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_kcontrol *kctl;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
+ struct dsp_stream_callback_prtd *kctl_prtd = NULL;
+
+ if (!rtd) {
+ pr_err("%s: rtd is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name,
+ rtd->pcm->device);
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ kfree(mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (kctl->private_data != NULL) {
+ pr_err("%s: kctl_prtd is not NULL at initialization.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ kctl_prtd = kzalloc(sizeof(struct dsp_stream_callback_prtd),
+ GFP_KERNEL);
+ if (!kctl_prtd) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ spin_lock_init(&kctl_prtd->prtd_spin_lock);
+ INIT_LIST_HEAD(&kctl_prtd->event_queue);
+ kctl_prtd->event_count = 0;
+ kctl->private_data = kctl_prtd;
+
+done:
+ return ret;
+}
+
+int msm_adsp_clean_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_kcontrol *kctl;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct dsp_stream_callback_list *node, *n;
+ unsigned long spin_flags;
+ const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
+ struct dsp_stream_callback_prtd *kctl_prtd = NULL;
+
+ if (!rtd) {
+ pr_err("%s: rtd is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name,
+ rtd->pcm->device);
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ kfree(mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ kctl_prtd = (struct dsp_stream_callback_prtd *)
+ kctl->private_data;
+ if (kctl_prtd != NULL) {
+ spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags);
+ /* clean the queue */
+ list_for_each_entry_safe(node, n,
+ &kctl_prtd->event_queue, list) {
+ list_del(&node->list);
+ kctl_prtd->event_count--;
+ pr_debug("%s: %d remaining events after del.\n",
+ __func__, kctl_prtd->event_count);
+ kfree(node);
+ }
+ spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags);
+ }
+
+ kfree(kctl_prtd);
+ kctl->private_data = NULL;
+
+done:
+ return ret;
+}
+
+int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd,
+ uint32_t *payload)
+{
+ /* adsp pp event notifier */
+ struct snd_kcontrol *kctl;
+ struct snd_ctl_elem_value control;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct dsp_stream_callback_list *new_event;
+ struct dsp_stream_callback_list *oldest_event;
+ unsigned long spin_flags;
+ struct dsp_stream_callback_prtd *kctl_prtd = NULL;
+ struct msm_adsp_event_data *event_data = NULL;
+ const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
+ struct snd_ctl_elem_info kctl_info;
+
+ if (!rtd || !payload) {
+ pr_err("%s: %s is NULL\n", __func__,
+ (!rtd) ? "rtd" : "payload");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (rtd->card->snd_card == NULL) {
+ pr_err("%s: snd_card is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_ATOMIC);
+ if (!mixer_str) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name,
+ rtd->pcm->device);
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ kfree(mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ event_data = (struct msm_adsp_event_data *)payload;
+ if (event_data->payload_len < sizeof(struct msm_adsp_event_data)) {
+ pr_err("%s: event_data size of %x is less than expected.\n",
+ __func__, event_data->payload_len);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ kctl->info(kctl, &kctl_info);
+
+ if (event_data->payload_len >
+ kctl_info.count - sizeof(struct msm_adsp_event_data)) {
+ pr_err("%s: payload length exceeds limit of %u bytes.\n",
+ __func__, kctl_info.count);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ kctl_prtd = (struct dsp_stream_callback_prtd *)
+ kctl->private_data;
+ if (kctl_prtd == NULL) {
+ /* queue is not initialized */
+ ret = -EINVAL;
+ pr_err("%s: event queue is not initialized.\n", __func__);
+ goto done;
+ }
+
+ new_event = kzalloc(sizeof(struct dsp_stream_callback_list)
+ + event_data->payload_len,
+ GFP_ATOMIC);
+ if (new_event == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ memcpy((void *)&new_event->event, (void *)payload,
+ event_data->payload_len
+ + sizeof(struct msm_adsp_event_data));
+
+ spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags);
+ while (kctl_prtd->event_count >= DSP_STREAM_CALLBACK_QUEUE_SIZE) {
+ pr_info("%s: queue of size %d is full. delete oldest one.\n",
+ __func__, DSP_STREAM_CALLBACK_QUEUE_SIZE);
+ oldest_event = list_first_entry(&kctl_prtd->event_queue,
+ struct dsp_stream_callback_list, list);
+ pr_info("%s: event deleted: type %d length %d\n",
+ __func__, oldest_event->event.event_type,
+ oldest_event->event.payload_len);
+ list_del(&oldest_event->list);
+ kctl_prtd->event_count--;
+ kfree(oldest_event);
+ }
+
+ list_add_tail(&new_event->list, &kctl_prtd->event_queue);
+ kctl_prtd->event_count++;
+ spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags);
+
+ control.id = kctl->id;
+ snd_ctl_notify(rtd->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &control.id);
+
+done:
+ return ret;
+}
+
+int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count =
+ sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data);
+
+ return 0;
+}
+
+int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t payload_size = 0;
+ struct dsp_stream_callback_list *oldest_event;
+ unsigned long spin_flags;
+ struct dsp_stream_callback_prtd *kctl_prtd = NULL;
+ int ret = 0;
+
+ kctl_prtd = (struct dsp_stream_callback_prtd *)
+ kcontrol->private_data;
+ if (kctl_prtd == NULL) {
+ pr_err("%s: ASM Stream PP event queue is not initialized.\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags);
+ pr_debug("%s: %d events in queue.\n", __func__, kctl_prtd->event_count);
+ if (list_empty(&kctl_prtd->event_queue)) {
+ pr_err("%s: ASM Stream PP event queue is empty.\n", __func__);
+ ret = -EINVAL;
+ spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags);
+ goto done;
+ }
+
+ oldest_event = list_first_entry(&kctl_prtd->event_queue,
+ struct dsp_stream_callback_list, list);
+ list_del(&oldest_event->list);
+ kctl_prtd->event_count--;
+ spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags);
+
+ payload_size = oldest_event->event.payload_len;
+ pr_debug("%s: event fetched: type %d length %d\n",
+ __func__, oldest_event->event.event_type,
+ oldest_event->event.payload_len);
+ memcpy(ucontrol->value.bytes.data, &oldest_event->event,
+ sizeof(struct msm_adsp_event_data) + payload_size);
+ kfree(oldest_event);
+
+done:
+ return ret;
+}
+
+int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count =
+ sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data);
+
+ return 0;
+}
+
+static int msm_multichannel_ec_primary_mic_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int copp_idx = 0;
+ int port_id = AFE_PORT_ID_QUATERNARY_TDM_TX;
+
+ msm_multichannel_ec_primary_mic_ch = ucontrol->value.integer.value[0];
+ pr_debug("%s: msm_multichannel_ec_primary_mic_ch = %u\n",
+ __func__, msm_multichannel_ec_primary_mic_ch);
+ copp_idx = adm_get_default_copp_idx(port_id);
+ if ((copp_idx < 0) || (copp_idx > MAX_COPPS_PER_PORT)) {
+ pr_err("%s : no active copp to query multichannel ec copp_idx: %u\n",
+ __func__, copp_idx);
+ return -EINVAL;
+ }
+ adm_send_set_multichannel_ec_primary_mic_ch(port_id, copp_idx,
+ msm_multichannel_ec_primary_mic_ch);
+
+ return ret;
+}
+
+static int msm_multichannel_ec_primary_mic_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_multichannel_ec_primary_mic_ch;
+ pr_debug("%s: msm_multichannel_ec_primary_mic_ch = %lu\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static const struct snd_kcontrol_new msm_multichannel_ec_controls[] = {
+ SOC_SINGLE_EXT("Multichannel EC Primary Mic Ch", SND_SOC_NOPM, 0,
+ 0xFFFFFFFF, 0, msm_multichannel_ec_primary_mic_ch_get,
+ msm_multichannel_ec_primary_mic_ch_put),
+};
+
+static const struct snd_kcontrol_new int_fm_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("Internal FM RX Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_qti_pp_get_fm_vol_mixer,
+ msm_qti_pp_set_fm_vol_mixer, fm_rx_vol_gain),
+ SOC_SINGLE_EXT_TLV("Quat MI2S FM RX Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_qti_pp_get_quat_mi2s_fm_vol_mixer,
+ msm_qti_pp_set_quat_mi2s_fm_vol_mixer, fm_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new pri_mi2s_lb_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("PRI MI2S LOOPBACK Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_qti_pp_get_pri_mi2s_lb_vol_mixer,
+ msm_qti_pp_set_pri_mi2s_lb_vol_mixer, afe_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new sec_mi2s_lb_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("SEC MI2S LOOPBACK Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_qti_pp_get_sec_mi2s_lb_vol_mixer,
+ msm_qti_pp_set_sec_mi2s_lb_vol_mixer, afe_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new tert_mi2s_lb_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("Tert MI2S LOOPBACK Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_qti_pp_get_tert_mi2s_lb_vol_mixer,
+ msm_qti_pp_set_tert_mi2s_lb_vol_mixer, afe_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new slimbus_7_lb_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("SLIMBUS_7 LOOPBACK Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0,
+ msm_qti_pp_get_slimbus_7_lb_vol_mixer,
+ msm_qti_pp_set_slimbus_7_lb_vol_mixer,
+ afe_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new slimbus_8_lb_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("SLIMBUS_8 LOOPBACK Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_qti_pp_get_slimbus_8_lb_vol_mixer,
+ msm_qti_pp_set_slimbus_8_lb_vol_mixer, afe_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new int_hfp_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("Internal HFP RX Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_qti_pp_get_hfp_vol_mixer,
+ msm_qti_pp_set_hfp_vol_mixer, hfp_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new int_icc_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("Internal ICC Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_qti_pp_get_icc_vol_mixer,
+ msm_qti_pp_set_icc_vol_mixer, icc_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new pri_auxpcm_lb_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("PRI AUXPCM LOOPBACK Volume",
+ AFE_PORT_ID_PRIMARY_PCM_TX, 0, INT_RX_VOL_GAIN, 0,
+ msm_qti_pp_get_pri_auxpcm_lb_vol_mixer,
+ msm_qti_pp_set_pri_auxpcm_lb_vol_mixer,
+ pri_auxpcm_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new sec_auxpcm_lb_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("SEC AUXPCM LOOPBACK Volume",
+ AFE_PORT_ID_SECONDARY_PCM_TX, 0, INT_RX_VOL_GAIN, 0,
+ msm_qti_pp_get_sec_auxpcm_lb_vol_mixer,
+ msm_qti_pp_set_sec_auxpcm_lb_vol_mixer,
+ sec_auxpcm_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = {
+ SOC_SINGLE_MULTI_EXT("Playback Device Channel Map",
+ SND_SOC_NOPM, 0, 255,
+ 0, 32, msm_qti_pp_get_channel_map_mixer,
+ msm_qti_pp_put_channel_map_mixer),
+};
+
+
+static const struct snd_kcontrol_new get_rms_controls[] = {
+ SOC_SINGLE_EXT("Get RMS", SND_SOC_NOPM, 0, 0xFFFFFFFF,
+ 0, msm_qti_pp_get_rms_value_control, msm_qti_pp_put_rms_value_control),
+};
+
+static const struct snd_kcontrol_new eq_enable_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1 EQ Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_qti_pp_get_eq_enable_mixer,
+ msm_qti_pp_put_eq_enable_mixer),
+ SOC_SINGLE_EXT("MultiMedia2 EQ Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_qti_pp_get_eq_enable_mixer,
+ msm_qti_pp_put_eq_enable_mixer),
+ SOC_SINGLE_EXT("MultiMedia3 EQ Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_qti_pp_get_eq_enable_mixer,
+ msm_qti_pp_put_eq_enable_mixer),
+};
+
+static const struct snd_kcontrol_new eq_band_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1 EQ Band Count", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 11, 0,
+ msm_qti_pp_get_eq_band_count_audio_mixer,
+ msm_qti_pp_put_eq_band_count_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2 EQ Band Count", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 11, 0,
+ msm_qti_pp_get_eq_band_count_audio_mixer,
+ msm_qti_pp_put_eq_band_count_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3 EQ Band Count", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 11, 0,
+ msm_qti_pp_get_eq_band_count_audio_mixer,
+ msm_qti_pp_put_eq_band_count_audio_mixer),
+};
+
+static const struct snd_kcontrol_new eq_coeff_mixer_controls[] = {
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band1", EQ_BAND1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band2", EQ_BAND2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band3", EQ_BAND3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band4", EQ_BAND4,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band5", EQ_BAND5,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band6", EQ_BAND6,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band7", EQ_BAND7,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band8", EQ_BAND8,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band9", EQ_BAND9,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band10", EQ_BAND10,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band11", EQ_BAND11,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band12", EQ_BAND12,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band1", EQ_BAND1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band2", EQ_BAND2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band3", EQ_BAND3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band4", EQ_BAND4,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band5", EQ_BAND5,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band6", EQ_BAND6,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band7", EQ_BAND7,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band8", EQ_BAND8,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band9", EQ_BAND9,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band10", EQ_BAND10,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band11", EQ_BAND11,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band12", EQ_BAND12,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band1", EQ_BAND1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band2", EQ_BAND2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band3", EQ_BAND3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band4", EQ_BAND4,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band5", EQ_BAND5,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band6", EQ_BAND6,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band7", EQ_BAND7,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band8", EQ_BAND8,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band9", EQ_BAND9,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band10", EQ_BAND10,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band11", EQ_BAND11,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band12", EQ_BAND12,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+};
+
+static const struct snd_kcontrol_new asphere_mixer_controls[] = {
+ SOC_SINGLE_MULTI_EXT("MSM ASphere Set Param", SND_SOC_NOPM, 0,
+ 0xFFFFFFFF, 0, 2, msm_qti_pp_asphere_get, msm_qti_pp_asphere_set),
+};
+
+static const struct snd_kcontrol_new dtmf_detect_enable_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1 DTMF Detect Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, NULL,
+ msm_qti_pp_put_dtmf_module_enable),
+ SOC_SINGLE_EXT("MultiMedia6 DTMF Detect Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, NULL,
+ msm_qti_pp_put_dtmf_module_enable),
+ SOC_SINGLE_EXT("MultiMedia21 DTMF Detect Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, NULL,
+ msm_qti_pp_put_dtmf_module_enable),
+};
+
+#ifdef CONFIG_QTI_PP
+void msm_qti_pp_add_controls(struct snd_soc_platform *platform)
+{
+ snd_soc_add_platform_controls(platform, int_fm_vol_mixer_controls,
+ ARRAY_SIZE(int_fm_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, pri_mi2s_lb_vol_mixer_controls,
+ ARRAY_SIZE(pri_mi2s_lb_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, sec_mi2s_lb_vol_mixer_controls,
+ ARRAY_SIZE(sec_mi2s_lb_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, tert_mi2s_lb_vol_mixer_controls,
+ ARRAY_SIZE(tert_mi2s_lb_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, slimbus_7_lb_vol_mixer_controls,
+ ARRAY_SIZE(slimbus_7_lb_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, slimbus_8_lb_vol_mixer_controls,
+ ARRAY_SIZE(slimbus_8_lb_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, int_hfp_vol_mixer_controls,
+ ARRAY_SIZE(int_hfp_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, int_icc_vol_mixer_controls,
+ ARRAY_SIZE(int_icc_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ pri_auxpcm_lb_vol_mixer_controls,
+ ARRAY_SIZE(pri_auxpcm_lb_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ sec_auxpcm_lb_vol_mixer_controls,
+ ARRAY_SIZE(sec_auxpcm_lb_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ multi_ch_channel_map_mixer_controls,
+ ARRAY_SIZE(multi_ch_channel_map_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, get_rms_controls,
+ ARRAY_SIZE(get_rms_controls));
+
+ snd_soc_add_platform_controls(platform, eq_enable_mixer_controls,
+ ARRAY_SIZE(eq_enable_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, eq_band_mixer_controls,
+ ARRAY_SIZE(eq_band_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, eq_coeff_mixer_controls,
+ ARRAY_SIZE(eq_coeff_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, asphere_mixer_controls,
+ ARRAY_SIZE(asphere_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, msm_multichannel_ec_controls,
+ ARRAY_SIZE(msm_multichannel_ec_controls));
+
+ snd_soc_add_platform_controls(platform,
+ dtmf_detect_enable_mixer_controls,
+ ARRAY_SIZE(dtmf_detect_enable_mixer_controls));
+}
+#endif /* CONFIG_QTI_PP */
diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h
new file mode 100644
index 000000000000..6ffcbdbd543e
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_QTI_PP_H_
+#define _MSM_QTI_PP_H_
+
+#include <sound/soc.h>
+int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd,
+ uint32_t *payload);
+int msm_adsp_init_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd);
+int msm_adsp_clean_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd);
+int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+#ifdef CONFIG_QTI_PP
+void msm_qti_pp_send_eq_values(int fedai_id);
+int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx,
+ unsigned int session_id,
+ uint16_t op_FL_ip_FL_weight,
+ uint16_t op_FL_ip_FR_weight,
+ uint16_t op_FR_ip_FL_weight,
+ uint16_t op_FR_ip_FR_weight);
+void msm_qti_pp_add_controls(struct snd_soc_platform *platform);
+#else /* CONFIG_QTI_PP */
+#define msm_qti_pp_send_eq_values(fedai_id) do {} while (0)
+#define msm_qti_pp_send_stereo_to_custom_stereo_cmd(port_id, copp_idx, \
+ session_id, op_FL_ip_FL_weight, op_FL_ip_FR_weight, \
+ op_FR_ip_FL_weight, op_FR_ip_FR_weight) (0)
+#define msm_qti_pp_add_controls(platform) do {} while (0)
+#endif /* CONFIG_QTI_PP */
+
+
+#if defined(CONFIG_QTI_PP) && defined(CONFIG_QTI_PP_AUDIOSPHERE)
+int msm_qti_pp_asphere_init(int port_id, int copp_idx);
+void msm_qti_pp_asphere_deinit(int port_id);
+#else
+#define msm_qti_pp_asphere_init(port_id, copp_idx) (0)
+#define msm_qti_pp_asphere_deinit(port_id) do {} while (0)
+#endif
+
+#endif /* _MSM_QTI_PP_H_ */
+
diff --git a/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c
new file mode 100644
index 000000000000..ef9c652b45a8
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c
@@ -0,0 +1,1451 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_ion.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6core.h>
+#include <sound/q6audio-v2.h>
+#include <sound/pcm_params.h>
+#include <sound/timer.h>
+#include <sound/tlv.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/compress_params.h>
+#include <sound/compress_offload.h>
+#include <sound/compress_driver.h>
+#include <linux/msm_audio.h>
+
+#include "msm-pcm-routing-v2.h"
+#include "msm-qti-pp-config.h"
+
+#define LOOPBACK_SESSION_MAX_NUM_STREAMS 2
+/* Max volume corresponding to 24dB */
+#define TRANSCODE_LR_VOL_MAX_STEPS 0xFFFF
+
+#define APP_TYPE_CONFIG_IDX_APP_TYPE 0
+#define APP_TYPE_CONFIG_IDX_ACDB_ID 1
+#define APP_TYPE_CONFIG_IDX_SAMPLE_RATE 2
+#define APP_TYPE_CONFIG_IDX_BE_ID 3
+
+static DEFINE_MUTEX(transcode_loopback_session_lock);
+
+struct trans_loopback_pdata {
+ struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX];
+ int32_t ion_fd[MSM_FRONTEND_DAI_MAX];
+ uint32_t master_gain;
+ int perf_mode;
+};
+
+struct loopback_stream {
+ struct snd_compr_stream *cstream;
+ uint32_t codec_format;
+ bool start;
+};
+
+enum loopback_session_state {
+ /* One or both streams not opened */
+ LOOPBACK_SESSION_CLOSE = 0,
+ /* Loopback streams opened */
+ LOOPBACK_SESSION_READY,
+ /* Loopback streams opened and formats configured */
+ LOOPBACK_SESSION_START,
+ /* Trigger issued on either of streams when in START state */
+ LOOPBACK_SESSION_RUN
+};
+
+struct msm_transcode_loopback {
+ struct loopback_stream source;
+ struct loopback_stream sink;
+
+ struct snd_compr_caps source_compr_cap;
+ struct snd_compr_caps sink_compr_cap;
+
+ uint32_t instance;
+ uint32_t num_streams;
+ int session_state;
+
+ struct mutex lock;
+
+ int session_id;
+ struct audio_client *audio_client;
+ int32_t shm_ion_fd;
+ struct ion_client *lib_ion_client;
+ struct ion_client *shm_ion_client;
+ struct ion_handle *lib_ion_handle;
+ struct ion_handle *shm_ion_handle;
+};
+
+/* Transcode loopback global info struct */
+static struct msm_transcode_loopback transcode_info;
+
+static void loopback_event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_transcode_loopback *trans =
+ (struct msm_transcode_loopback *)priv;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_compr_stream *cstream;
+ struct audio_client *ac;
+ int stream_id;
+ int ret;
+
+ if (!trans || !payload) {
+ pr_err("%s: rtd or payload is NULL\n", __func__);
+ return;
+ }
+
+ cstream = trans->sink.cstream;
+ ac = trans->audio_client;
+
+ /*
+ * Token for rest of the compressed commands use to set
+ * session id, stream id, dir etc.
+ */
+ stream_id = q6asm_get_stream_id_from_token(token);
+
+ switch (opcode) {
+ case ASM_STREAM_CMD_ENCDEC_EVENTS:
+ case ASM_IEC_61937_MEDIA_FMT_EVENT:
+ pr_debug("%s: Handling stream event : 0X%x\n",
+ __func__, opcode);
+ rtd = cstream->private_data;
+ if (!rtd) {
+ pr_err("%s: rtd is NULL\n", __func__);
+ return;
+ }
+
+ ret = msm_adsp_inform_mixer_ctl(rtd, payload);
+ if (ret) {
+ pr_err("%s: failed to inform mixer ctrl. err = %d\n",
+ __func__, ret);
+ return;
+ }
+ break;
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2:
+ pr_debug("%s: ASM_SESSION_CMD_RUN_V2:", __func__);
+ pr_debug("token 0x%x, stream id %d\n", token,
+ stream_id);
+ break;
+ case ASM_STREAM_CMD_CLOSE:
+ pr_debug("%s: ASM_DATA_CMD_CLOSE:", __func__);
+ pr_debug("token 0x%x, stream id %d\n", token,
+ stream_id);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ pr_debug("%s: Not Supported Event opcode[0x%x]\n",
+ __func__, opcode);
+ break;
+ }
+}
+
+static void populate_codec_list(struct msm_transcode_loopback *trans,
+ struct snd_compr_stream *cstream)
+{
+ struct snd_compr_caps compr_cap;
+
+ pr_debug("%s\n", __func__);
+
+ memset(&compr_cap, 0, sizeof(struct snd_compr_caps));
+
+ if (cstream->direction == SND_COMPRESS_CAPTURE) {
+ compr_cap.direction = SND_COMPRESS_CAPTURE;
+ compr_cap.num_codecs = 3;
+ compr_cap.codecs[0] = SND_AUDIOCODEC_PCM;
+ compr_cap.codecs[1] = SND_AUDIOCODEC_AC3;
+ compr_cap.codecs[2] = SND_AUDIOCODEC_EAC3;
+ memcpy(&trans->source_compr_cap, &compr_cap,
+ sizeof(struct snd_compr_caps));
+ }
+
+ if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+ compr_cap.direction = SND_COMPRESS_PLAYBACK;
+ compr_cap.num_codecs = 1;
+ compr_cap.codecs[0] = SND_AUDIOCODEC_PCM;
+ memcpy(&trans->sink_compr_cap, &compr_cap,
+ sizeof(struct snd_compr_caps));
+ }
+}
+
+static int msm_transcode_map_ion_fd(struct msm_transcode_loopback *trans,
+ int fd)
+{
+ ion_phys_addr_t paddr = 0;
+ size_t pa_len = 0;
+ int ret = 0;
+
+ ret = msm_audio_ion_phys_assign("audio_lib_mem_client",
+ &trans->lib_ion_client,
+ &trans->lib_ion_handle, fd,
+ &paddr, &pa_len, HLOS_TO_ADSP);
+ if (ret) {
+ pr_err("%s: audio lib ION phys failed, rc = %d\n", __func__,
+ ret);
+ goto done;
+ }
+
+ ret = q6core_add_remove_pool_pages(paddr, pa_len,
+ ADSP_MEMORY_MAP_HLOS_PHYSPOOL, true);
+ if (ret) {
+ pr_err("%s: add pages failed, rc = %d\n", __func__, ret);
+ /* Assign back to HLOS if add pages cmd failed */
+ msm_audio_ion_phys_free(trans->lib_ion_client,
+ trans->lib_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ }
+
+done:
+ return ret;
+}
+
+static int msm_transcode_unmap_ion_fd(struct msm_transcode_loopback *trans)
+{
+ ion_phys_addr_t paddr = 0;
+ size_t pa_len = 0;
+ int ret = 0;
+
+ if (!trans->lib_ion_client || !trans->lib_ion_handle) {
+ pr_err("%s: ion_client or ion_handle is NULL", __func__);
+ return -EINVAL;
+ }
+ ret = msm_audio_ion_phys_free(trans->lib_ion_client,
+ trans->lib_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ if (ret) {
+ pr_err("%s: audio lib ION phys failed, rc = %d\n", __func__,
+ ret);
+ goto done;
+ }
+
+ ret = q6core_add_remove_pool_pages(paddr, pa_len,
+ ADSP_MEMORY_MAP_HLOS_PHYSPOOL, false);
+ if (ret)
+ pr_err("%s: remove pages failed, rc = %d\n", __func__, ret);
+
+done:
+ return ret;
+}
+
+static int msm_transcode_loopback_open(struct snd_compr_stream *cstream)
+{
+ int ret = 0;
+ struct snd_compr_runtime *runtime;
+ struct snd_soc_pcm_runtime *rtd;
+ struct msm_transcode_loopback *trans = &transcode_info;
+ struct trans_loopback_pdata *pdata;
+
+ if (cstream == NULL) {
+ pr_err("%s: Invalid substream\n", __func__);
+ return -EINVAL;
+ }
+ runtime = cstream->runtime;
+ rtd = snd_pcm_substream_chip(cstream);
+ pdata = snd_soc_platform_get_drvdata(rtd->platform);
+ pdata->cstream[rtd->dai_link->be_id] = cstream;
+
+ mutex_lock(&trans->lock);
+ if (trans->num_streams > LOOPBACK_SESSION_MAX_NUM_STREAMS) {
+ pr_err("msm_transcode_open failed..invalid stream\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (cstream->direction == SND_COMPRESS_CAPTURE) {
+ if (trans->source.cstream == NULL) {
+ trans->source.cstream = cstream;
+ trans->num_streams++;
+ } else {
+ pr_err("%s: capture stream already opened\n",
+ __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+ } else if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+ if (trans->sink.cstream == NULL) {
+ trans->sink.cstream = cstream;
+ trans->num_streams++;
+ } else {
+ pr_debug("%s: playback stream already opened\n",
+ __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+ msm_adsp_init_mixer_ctl_pp_event_queue(rtd);
+ if (pdata->ion_fd[rtd->dai_link->be_id] > 0) {
+ ret = msm_transcode_map_ion_fd(trans,
+ pdata->ion_fd[rtd->dai_link->be_id]);
+ if (ret < 0)
+ goto exit;
+ }
+ }
+
+ pr_debug("%s: num stream%d, stream name %s\n", __func__,
+ trans->num_streams, cstream->name);
+
+ populate_codec_list(trans, cstream);
+
+ if (trans->num_streams == LOOPBACK_SESSION_MAX_NUM_STREAMS) {
+ pr_debug("%s: Moving loopback session to READY state %d\n",
+ __func__, trans->session_state);
+ trans->session_state = LOOPBACK_SESSION_READY;
+ }
+
+ runtime->private_data = trans;
+
+exit:
+ mutex_unlock(&trans->lock);
+ return ret;
+}
+
+static void stop_transcoding(struct msm_transcode_loopback *trans)
+{
+ struct snd_soc_pcm_runtime *soc_pcm_rx;
+ struct snd_soc_pcm_runtime *soc_pcm_tx;
+
+ if (trans->audio_client != NULL) {
+ q6asm_cmd(trans->audio_client, CMD_CLOSE);
+
+ if (trans->sink.cstream != NULL) {
+ soc_pcm_rx = trans->sink.cstream->private_data;
+ msm_pcm_routing_dereg_phy_stream(
+ soc_pcm_rx->dai_link->be_id,
+ SND_COMPRESS_PLAYBACK);
+ }
+ if (trans->source.cstream != NULL) {
+ soc_pcm_tx = trans->source.cstream->private_data;
+ msm_pcm_routing_dereg_phy_stream(
+ soc_pcm_tx->dai_link->be_id,
+ SND_COMPRESS_CAPTURE);
+ }
+ q6asm_audio_client_free(trans->audio_client);
+ trans->audio_client = NULL;
+ }
+}
+
+static int msm_transcode_loopback_free(struct snd_compr_stream *cstream)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_transcode_loopback *trans = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(cstream);
+ struct trans_loopback_pdata *pdata = snd_soc_platform_get_drvdata(
+ rtd->platform);
+ int ret = 0;
+ ion_phys_addr_t paddr = 0;
+ size_t pa_len = 0;
+
+ mutex_lock(&trans->lock);
+
+ pr_debug("%s: Transcode loopback end:%d, streams %d\n", __func__,
+ cstream->direction, trans->num_streams);
+ trans->num_streams--;
+ stop_transcoding(trans);
+
+ if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+ memset(&trans->sink, 0, sizeof(struct loopback_stream));
+ msm_adsp_clean_mixer_ctl_pp_event_queue(rtd);
+ if (trans->shm_ion_fd > 0) {
+ msm_audio_ion_phys_free(trans->shm_ion_client,
+ trans->shm_ion_handle,
+ &paddr, &pa_len, ADSP_TO_HLOS);
+ trans->shm_ion_fd = 0;
+ }
+ if (pdata->ion_fd[rtd->dai_link->be_id] > 0) {
+ msm_transcode_unmap_ion_fd(trans);
+ pdata->ion_fd[rtd->dai_link->be_id] = 0;
+ }
+ } else if (cstream->direction == SND_COMPRESS_CAPTURE) {
+ memset(&trans->source, 0, sizeof(struct loopback_stream));
+ }
+
+ trans->session_state = LOOPBACK_SESSION_CLOSE;
+ mutex_unlock(&trans->lock);
+ return ret;
+}
+
+static int msm_transcode_loopback_trigger(struct snd_compr_stream *cstream,
+ int cmd)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_transcode_loopback *trans = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+
+ if (trans->session_state == LOOPBACK_SESSION_START) {
+ pr_debug("%s: Issue Loopback session %d RUN\n",
+ __func__, trans->instance);
+ q6asm_run_nowait(trans->audio_client, 0, 0, 0);
+ trans->session_state = LOOPBACK_SESSION_RUN;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("%s: Issue Loopback session %d STOP\n", __func__,
+ trans->instance);
+ if (trans->session_state == LOOPBACK_SESSION_RUN)
+ q6asm_cmd_nowait(trans->audio_client, CMD_PAUSE);
+ trans->session_state = LOOPBACK_SESSION_START;
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *codec_param)
+{
+
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct msm_transcode_loopback *trans = runtime->private_data;
+ struct snd_soc_pcm_runtime *soc_pcm_rx;
+ struct snd_soc_pcm_runtime *soc_pcm_tx;
+ struct snd_soc_pcm_runtime *rtd;
+ struct trans_loopback_pdata *pdata;
+ uint32_t bit_width = 16;
+ int ret = 0;
+
+ if (trans == NULL) {
+ pr_err("%s: Invalid param\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&trans->lock);
+
+ rtd = snd_pcm_substream_chip(cstream);
+ pdata = snd_soc_platform_get_drvdata(rtd->platform);
+
+ if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+ if (codec_param->codec.id == SND_AUDIOCODEC_PCM) {
+ trans->sink.codec_format =
+ FORMAT_LINEAR_PCM;
+ switch (codec_param->codec.format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bit_width = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bit_width = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ bit_width = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bit_width = 16;
+ break;
+ }
+ } else {
+ pr_debug("%s: unknown sink codec\n", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+ trans->sink.start = true;
+ }
+
+ if (cstream->direction == SND_COMPRESS_CAPTURE) {
+ switch (codec_param->codec.id) {
+ case SND_AUDIOCODEC_PCM:
+ pr_debug("Source SND_AUDIOCODEC_PCM\n");
+ trans->source.codec_format =
+ FORMAT_LINEAR_PCM;
+ break;
+ case SND_AUDIOCODEC_AC3:
+ pr_debug("Source SND_AUDIOCODEC_AC3\n");
+ trans->source.codec_format =
+ FORMAT_AC3;
+ break;
+ case SND_AUDIOCODEC_EAC3:
+ pr_debug("Source SND_AUDIOCODEC_EAC3\n");
+ trans->source.codec_format =
+ FORMAT_EAC3;
+ break;
+ default:
+ pr_debug("%s: unknown source codec\n", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+ trans->source.start = true;
+ }
+
+ pr_debug("%s: trans->source.start %d trans->sink.start %d trans->source.cstream %pK trans->sink.cstream %pK trans->session_state %d\n",
+ __func__, trans->source.start, trans->sink.start,
+ trans->source.cstream, trans->sink.cstream,
+ trans->session_state);
+
+ if ((trans->session_state == LOOPBACK_SESSION_READY) &&
+ trans->source.start && trans->sink.start) {
+ pr_debug("%s: Moving loopback session to start state\n",
+ __func__);
+ trans->session_state = LOOPBACK_SESSION_START;
+ }
+
+ if (trans->session_state == LOOPBACK_SESSION_START) {
+ if (trans->audio_client != NULL) {
+ pr_debug("%s: ASM client already opened, closing\n",
+ __func__);
+ stop_transcoding(trans);
+ }
+
+ trans->audio_client = q6asm_audio_client_alloc(
+ (app_cb)loopback_event_handler, trans);
+ if (!trans->audio_client) {
+ pr_err("%s: Could not allocate memory\n", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+ pr_debug("%s: ASM client allocated, callback %pK\n", __func__,
+ loopback_event_handler);
+ trans->session_id = trans->audio_client->session;
+ trans->audio_client->perf_mode = pdata->perf_mode;
+ ret = q6asm_open_transcode_loopback(trans->audio_client,
+ bit_width,
+ trans->source.codec_format,
+ trans->sink.codec_format);
+ if (ret < 0) {
+ pr_err("%s: Session transcode loopback open failed\n",
+ __func__);
+ q6asm_audio_client_free(trans->audio_client);
+ trans->audio_client = NULL;
+ goto exit;
+ }
+
+ pr_debug("%s: Starting ADM open for loopback\n", __func__);
+ soc_pcm_rx = trans->sink.cstream->private_data;
+ soc_pcm_tx = trans->source.cstream->private_data;
+ if (trans->source.codec_format != FORMAT_LINEAR_PCM)
+ msm_pcm_routing_reg_phy_compr_stream(
+ soc_pcm_tx->dai_link->be_id,
+ false,
+ trans->session_id,
+ SNDRV_PCM_STREAM_CAPTURE,
+ COMPRESSED_PASSTHROUGH_GEN);
+ else
+ msm_pcm_routing_reg_phy_stream(
+ soc_pcm_tx->dai_link->be_id,
+ trans->audio_client->perf_mode,
+ trans->session_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ /* Opening Rx ADM in LOW_LATENCY mode by default */
+ msm_pcm_routing_reg_phy_stream(
+ soc_pcm_rx->dai_link->be_id,
+ trans->audio_client->perf_mode,
+ trans->session_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ pr_debug("%s: Successfully opened ADM sessions\n", __func__);
+ }
+exit:
+ mutex_unlock(&trans->lock);
+ return ret;
+}
+
+static int msm_transcode_loopback_get_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_caps *arg)
+{
+ struct snd_compr_runtime *runtime;
+ struct msm_transcode_loopback *trans;
+
+ if (!arg || !cstream) {
+ pr_err("%s: Invalid arguments\n", __func__);
+ return -EINVAL;
+ }
+
+ runtime = cstream->runtime;
+ trans = runtime->private_data;
+ pr_debug("%s\n", __func__);
+ if (cstream->direction == SND_COMPRESS_CAPTURE)
+ memcpy(arg, &trans->source_compr_cap,
+ sizeof(struct snd_compr_caps));
+ else
+ memcpy(arg, &trans->sink_compr_cap,
+ sizeof(struct snd_compr_caps));
+ return 0;
+}
+
+static int msm_transcode_loopback_set_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct trans_loopback_pdata *pdata;
+
+ if (!metadata || !cstream) {
+ pr_err("%s: Invalid arguments\n", __func__);
+ return -EINVAL;
+ }
+
+ rtd = snd_pcm_substream_chip(cstream);
+ pdata = snd_soc_platform_get_drvdata(rtd->platform);
+
+ switch (metadata->key) {
+ case SNDRV_COMPRESS_LATENCY_MODE:
+ {
+ switch (metadata->value[0]) {
+ case SNDRV_COMPRESS_LEGACY_LATENCY_MODE:
+ pdata->perf_mode = LEGACY_PCM_MODE;
+ break;
+ case SNDRV_COMPRESS_LOW_LATENCY_MODE:
+ pdata->perf_mode = LOW_LATENCY_PCM_MODE;
+ break;
+ default:
+ pr_debug("%s: Unsupported latency mode %d, default to Legacy\n",
+ __func__, metadata->value[0]);
+ pdata->perf_mode = LEGACY_PCM_MODE;
+ break;
+ }
+ }
+ break;
+ default:
+ pr_debug("%s: Unsupported metadata %d\n",
+ __func__, metadata->key);
+ break;
+ }
+ return 0;
+}
+
+static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_transcode_loopback *prtd;
+ int ret = 0;
+ struct msm_adsp_event_data *event_data = NULL;
+ uint64_t actual_payload_len = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ if (cstream == NULL) {
+ pr_err("%s cstream is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: prtd is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client == NULL) {
+ pr_err("%s: audio_client is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data;
+ if ((event_data->event_type < ADSP_STREAM_PP_EVENT) ||
+ (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) {
+ pr_err("%s: invalid event_type=%d",
+ __func__, event_data->event_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ actual_payload_len = sizeof(struct msm_adsp_event_data) +
+ event_data->payload_len;
+ if (actual_payload_len >= U32_MAX) {
+ pr_err("%s payload length 0x%X exceeds limit",
+ __func__, event_data->payload_len);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (event_data->payload_len > sizeof(ucontrol->value.bytes.data)
+ - sizeof(struct msm_adsp_event_data)) {
+ pr_err("%s param length=%d exceeds limit",
+ __func__, event_data->payload_len);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = q6asm_send_stream_cmd(prtd->audio_client, event_data);
+ if (ret < 0)
+ pr_err("%s: failed to send stream event cmd, err = %d\n",
+ __func__, ret);
+done:
+ return ret;
+}
+
+static int msm_transcode_shm_ion_fd_map_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_transcode_loopback *prtd;
+ int ret = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ if (cstream == NULL) {
+ pr_err("%s cstream is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: prtd is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client == NULL) {
+ pr_err("%s: audio_client is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(&prtd->shm_ion_fd, ucontrol->value.bytes.data,
+ sizeof(prtd->shm_ion_fd));
+ ret = q6asm_audio_map_shm_fd(prtd->audio_client,
+ &prtd->shm_ion_client,
+ &prtd->shm_ion_handle, prtd->shm_ion_fd);
+ if (ret < 0)
+ pr_err("%s: failed to map shm mem\n", __func__);
+done:
+ return ret;
+}
+
+
+static int msm_transcode_lib_ion_fd_map_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ int ret = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(&pdata->ion_fd[fe_id], ucontrol->value.bytes.data,
+ sizeof(pdata->ion_fd[fe_id]));
+done:
+ return ret;
+}
+
+static int msm_transcode_rtic_event_ack_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_transcode_loopback *prtd;
+ int ret = 0;
+ int param_length = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received invalid fe_id %lu\n",
+ __func__, fe_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ if (cstream == NULL) {
+ pr_err("%s cstream is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: prtd is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client == NULL) {
+ pr_err("%s: audio_client is null\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(&param_length, ucontrol->value.bytes.data,
+ sizeof(param_length));
+ if ((param_length + sizeof(param_length))
+ >= sizeof(ucontrol->value.bytes.data)) {
+ pr_err("%s param length=%d exceeds limit",
+ __func__, param_length);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = q6asm_send_rtic_event_ack(prtd->audio_client,
+ ucontrol->value.bytes.data + sizeof(param_length),
+ param_length);
+ if (ret < 0)
+ pr_err("%s: failed to send rtic event ack, err = %d\n",
+ __func__, ret);
+done:
+ return ret;
+}
+
+static int msm_transcode_playback_app_type_cfg_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_BE_ID];
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ int ret = 0;
+
+ cfg_data.app_type = ucontrol->value.integer.value[
+ APP_TYPE_CONFIG_IDX_APP_TYPE];
+ cfg_data.acdb_dev_id = ucontrol->value.integer.value[
+ APP_TYPE_CONFIG_IDX_ACDB_ID];
+ if (ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_SAMPLE_RATE] != 0)
+ cfg_data.sample_rate = ucontrol->value.integer.value[
+ APP_TYPE_CONFIG_IDX_SAMPLE_RATE];
+ pr_debug("%s: fe_id %llu session_type %d be_id %d app_type %d acdb_dev_id %d sample_rate- %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, &cfg_data);
+ if (ret < 0)
+ pr_err("%s: msm_transcode_playback_stream_app_type_cfg set failed returned %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_transcode_playback_app_type_cfg_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_RX;
+ int be_id = 0;
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0};
+ int ret = 0;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ &be_id, &cfg_data);
+ if (ret < 0) {
+ pr_err("%s: msm_transcode_playback_stream_app_type_cfg get failed returned %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_APP_TYPE] =
+ cfg_data.app_type;
+ ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_ACDB_ID] =
+ cfg_data.acdb_dev_id;
+ ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_SAMPLE_RATE] =
+ cfg_data.sample_rate;
+ ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_BE_ID] = be_id;
+ pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ __func__, fe_id, session_type, be_id,
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+done:
+ return ret;
+}
+
+static int msm_transcode_set_volume(struct snd_compr_stream *cstream,
+ uint32_t master_gain)
+{
+ int rc = 0;
+ struct msm_transcode_loopback *prtd;
+ struct snd_soc_pcm_runtime *rtd;
+
+ pr_debug("%s: master_gain %d\n", __func__, master_gain);
+ if (!cstream || !cstream->runtime) {
+ pr_err("%s: session not active\n", __func__);
+ return -EPERM;
+ }
+ rtd = cstream->private_data;
+ prtd = cstream->runtime->private_data;
+
+ if (!rtd || !rtd->platform || !prtd || !prtd->audio_client) {
+ pr_err("%s: invalid rtd, prtd or audio client", __func__);
+ return -EINVAL;
+ }
+
+ rc = q6asm_set_volume(prtd->audio_client, master_gain);
+ if (rc < 0)
+ pr_err("%s: Send vol gain command failed rc=%d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+static int msm_transcode_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ uint32_t ret = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds fe_id %lu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ pdata->master_gain = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: fe_id %lu master_gain %d\n",
+ __func__, fe_id, pdata->master_gain);
+ if (cstream)
+ ret = msm_transcode_set_volume(cstream, pdata->master_gain);
+ return ret;
+}
+
+static int msm_transcode_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+
+ struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
+ snd_soc_component_get_drvdata(comp);
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: fe_id %lu\n", __func__, fe_id);
+ ucontrol->value.integer.value[0] = pdata->master_gain;
+
+ return 0;
+}
+
+static int msm_transcode_stream_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CMD;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_loopback_stream_cmd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_transcode_stream_cmd_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_loopback_stream_cmd_config_control[0].name = mixer_str;
+ fe_loopback_stream_cmd_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_loopback_stream_cmd_config_control,
+ ARRAY_SIZE(fe_loopback_stream_cmd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_transcode_stream_callback_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol *kctl;
+
+ struct snd_kcontrol_new fe_loopback_callback_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_callback_info,
+ .get = msm_adsp_stream_callback_get,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s: rtd is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_loopback_callback_config_control[0].name = mixer_str;
+ fe_loopback_callback_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_loopback_callback_config_control,
+ ARRAY_SIZE(fe_loopback_callback_config_control));
+ if (ret < 0) {
+ pr_err("%s: failed to add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl->private_data = NULL;
+free_mixer_str:
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_transcode_add_shm_ion_fd_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Playback ION FD";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_ion_fd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_transcode_shm_ion_fd_map_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_ion_fd_config_control[0].name = mixer_str;
+ fe_ion_fd_config_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_ion_fd_config_control,
+ ARRAY_SIZE(fe_ion_fd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_transcode_add_lib_ion_fd_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Playback ION LIB FD";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_ion_fd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_transcode_lib_ion_fd_map_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_ion_fd_config_control[0].name = mixer_str;
+ fe_ion_fd_config_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_ion_fd_config_control,
+ ARRAY_SIZE(fe_ion_fd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_transcode_add_event_ack_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Playback Event Ack";
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_event_ack_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_transcode_rtic_event_ack_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_event_ack_config_control[0].name = mixer_str;
+ fe_event_ack_config_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_event_ack_config_control,
+ ARRAY_SIZE(fe_event_ack_config_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_transcode_app_type_cfg_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 5;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
+static int msm_transcode_add_app_type_cfg_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ char mixer_str[32];
+ int rc = 0;
+ struct snd_kcontrol_new fe_app_type_cfg_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_transcode_app_type_cfg_info,
+ .put = msm_transcode_playback_app_type_cfg_put,
+ .get = msm_transcode_playback_app_type_cfg_get,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+
+ return -EINVAL;
+ }
+
+ if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) {
+
+ snprintf(mixer_str, sizeof(mixer_str),
+ "Audio Stream %d App Type Cfg",
+ rtd->pcm->device);
+
+ fe_app_type_cfg_control[0].name = mixer_str;
+ fe_app_type_cfg_control[0].private_value = rtd->dai_link->be_id;
+
+ fe_app_type_cfg_control[0].put =
+ msm_transcode_playback_app_type_cfg_put;
+ fe_app_type_cfg_control[0].get =
+ msm_transcode_playback_app_type_cfg_get;
+
+ pr_debug("Registering new mixer ctl %s", mixer_str);
+ snd_soc_add_platform_controls(rtd->platform,
+ fe_app_type_cfg_control,
+ ARRAY_SIZE(fe_app_type_cfg_control));
+ }
+
+ return rc;
+}
+static int msm_transcode_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = TRANSCODE_LR_VOL_MAX_STEPS;
+ return 0;
+}
+
+static int msm_transcode_add_volume_control(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_kcontrol_new fe_volume_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Transcode Loopback Rx Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_transcode_volume_info,
+ .get = msm_transcode_volume_get,
+ .put = msm_transcode_volume_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return -EINVAL;
+ }
+ if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) {
+ fe_volume_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s",
+ fe_volume_control[0].name);
+ snd_soc_add_platform_controls(rtd->platform, fe_volume_control,
+ ARRAY_SIZE(fe_volume_control));
+ }
+ return 0;
+}
+
+static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd)
+{
+ int rc;
+
+ rc = msm_transcode_stream_cmd_control(rtd);
+ if (rc)
+ pr_err("%s: ADSP Stream Cmd Control open failed\n", __func__);
+
+ rc = msm_transcode_stream_callback_control(rtd);
+ if (rc)
+ pr_err("%s: ADSP Stream callback Control open failed\n",
+ __func__);
+
+ rc = msm_transcode_add_shm_ion_fd_cmd_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add transcode shm ion fd Control\n",
+ __func__);
+
+ rc = msm_transcode_add_lib_ion_fd_cmd_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add transcode lib ion fd Control\n",
+ __func__);
+
+ rc = msm_transcode_add_event_ack_cmd_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add transcode event ack Control\n",
+ __func__);
+
+ rc = msm_transcode_add_app_type_cfg_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr App Type Cfg Control\n",
+ __func__);
+
+ rc = msm_transcode_add_volume_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add transcode volume Control\n",
+ __func__);
+
+ return 0;
+}
+
+static struct snd_compr_ops msm_transcode_loopback_ops = {
+ .open = msm_transcode_loopback_open,
+ .free = msm_transcode_loopback_free,
+ .trigger = msm_transcode_loopback_trigger,
+ .set_params = msm_transcode_loopback_set_params,
+ .get_caps = msm_transcode_loopback_get_caps,
+ .set_metadata = msm_transcode_loopback_set_metadata,
+};
+
+
+static int msm_transcode_loopback_probe(struct snd_soc_platform *platform)
+{
+ struct trans_loopback_pdata *pdata = NULL;
+
+ pr_debug("%s\n", __func__);
+ pdata = (struct trans_loopback_pdata *)
+ kzalloc(sizeof(struct trans_loopback_pdata),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->perf_mode = LOW_LATENCY_PCM_MODE;
+ snd_soc_platform_set_drvdata(platform, pdata);
+ return 0;
+}
+
+static int msm_transcode_loopback_remove(struct snd_soc_platform *platform)
+{
+ struct trans_loopback_pdata *pdata = NULL;
+
+ pdata = (struct trans_loopback_pdata *)
+ snd_soc_platform_get_drvdata(platform);
+ kfree(pdata);
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .probe = msm_transcode_loopback_probe,
+ .compr_ops = &msm_transcode_loopback_ops,
+ .pcm_new = msm_transcode_loopback_new,
+ .remove = msm_transcode_loopback_remove,
+};
+
+static int msm_transcode_dev_probe(struct platform_device *pdev)
+{
+
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ if (pdev->dev.of_node)
+ dev_set_name(&pdev->dev, "%s", "msm-transcode-loopback");
+
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_transcode_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_transcode_loopback_dt_match[] = {
+ {.compatible = "qcom,msm-transcode-loopback"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_transcode_loopback_dt_match);
+
+static struct platform_driver msm_transcode_loopback_driver = {
+ .driver = {
+ .name = "msm-transcode-loopback",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_transcode_loopback_dt_match,
+ },
+ .probe = msm_transcode_dev_probe,
+ .remove = msm_transcode_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ memset(&transcode_info, 0, sizeof(struct msm_transcode_loopback));
+ mutex_init(&transcode_info.lock);
+ return platform_driver_register(&msm_transcode_loopback_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ mutex_destroy(&transcode_info.lock);
+ platform_driver_unregister(&msm_transcode_loopback_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("Transcode loopback platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
new file mode 100644
index 000000000000..b40710cf3f45
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -0,0 +1,4735 @@
+/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+#include <linux/wait.h>
+#include <linux/qdsp6v2/apr.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6common.h>
+#include <sound/audio_cal_utils.h>
+#include <sound/asound.h>
+#include <sound/q6core.h>
+#include "msm-dts-srs-tm-config.h"
+#include <sound/adsp_err.h>
+
+#define TIMEOUT_MS 1000
+
+#define RESET_COPP_ID 99
+#define INVALID_COPP_ID 0xFF
+/* Used for inband payload copy, max size is 4k */
+/* 3 is to account for module, instance & param ID in payload */
+#define ADM_GET_PARAMETER_LENGTH (4096 - APR_HDR_SIZE - 3 * sizeof(uint32_t))
+
+#define ULL_SUPPORTED_BITS_PER_SAMPLE 16
+#define ULL_SUPPORTED_SAMPLE_RATE 48000
+
+#ifndef CONFIG_DOLBY_DAP
+#undef DOLBY_ADM_COPP_TOPOLOGY_ID
+#define DOLBY_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFE
+#endif
+
+#ifndef CONFIG_DOLBY_DS2
+#undef DS2_ADM_COPP_TOPOLOGY_ID
+#define DS2_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFF
+#endif
+
+struct adm_copp {
+
+ atomic_t id[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t cnt[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t topology[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t mode[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t stat[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t rate[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t bit_width[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t channels[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t app_type[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t acdb_id[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ wait_queue_head_t wait[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ wait_queue_head_t adm_delay_wait[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t adm_delay_stat[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ uint32_t adm_delay[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ unsigned long adm_status[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t token[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+};
+
+struct source_tracking_data {
+ struct ion_client *ion_client;
+ struct ion_handle *ion_handle;
+ struct param_outband memmap;
+ int apr_cmd_status;
+};
+
+struct adm_ctl {
+ void *apr;
+
+ struct adm_copp copp;
+
+ atomic_t matrix_map_stat;
+ wait_queue_head_t matrix_map_wait;
+
+ atomic_t adm_stat;
+ wait_queue_head_t adm_wait;
+
+ struct cal_type_data *cal_data[ADM_MAX_CAL_TYPES];
+
+ atomic_t mem_map_handles[ADM_MEM_MAP_INDEX_MAX];
+ atomic_t mem_map_index;
+
+ struct param_outband outband_memmap;
+ struct source_tracking_data sourceTrackingData;
+
+ int set_custom_topology;
+ int ec_ref_rx;
+ int num_ec_ref_rx_chans;
+ int ec_ref_rx_bit_width;
+ int ec_ref_rx_sampling_rate;
+
+ int native_mode;
+ u32 copp_token;
+};
+
+static struct adm_ctl this_adm;
+
+struct adm_multi_ch_map {
+ bool set_channel_map;
+ char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL_V2];
+};
+
+#define ADM_MCH_MAP_IDX_PLAYBACK 0
+#define ADM_MCH_MAP_IDX_REC 1
+static struct adm_multi_ch_map multi_ch_maps[2] = {
+ { false,
+ {0, 0, 0, 0, 0, 0, 0, 0}
+ },
+ { false,
+ {0, 0, 0, 0, 0, 0, 0, 0}
+ }
+};
+
+static int adm_get_parameters[MAX_COPPS_PER_PORT * ADM_GET_PARAMETER_LENGTH];
+static int adm_module_topo_list[MAX_COPPS_PER_PORT *
+ ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH];
+
+int adm_validate_and_get_port_index(int port_id)
+{
+ int index;
+ int ret;
+
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: port validation failed id 0x%x ret %d\n",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ index = afe_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: Invalid port idx %d port_id 0x%x\n",
+ __func__, index,
+ port_id);
+ return -EINVAL;
+ }
+ pr_debug("%s: port_idx- %d\n", __func__, index);
+ return index;
+}
+
+int adm_get_default_copp_idx(int port_id)
+{
+ int port_idx = adm_validate_and_get_port_index(port_id), idx;
+
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port id: 0x%x", __func__, port_id);
+ return -EINVAL;
+ }
+ pr_debug("%s: port_idx:%d\n", __func__, port_idx);
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+ if (atomic_read(&this_adm.copp.id[port_idx][idx]) !=
+ RESET_COPP_ID)
+ return idx;
+ }
+ return -EINVAL;
+}
+
+int adm_get_topology_for_port_from_copp_id(int port_id, int copp_id)
+{
+ int port_idx = adm_validate_and_get_port_index(port_id), idx;
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port id: 0x%x", __func__, port_id);
+ return 0;
+ }
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+ if (atomic_read(&this_adm.copp.id[port_idx][idx]) == copp_id)
+ return atomic_read(&this_adm.copp.topology[port_idx]
+ [idx]);
+ pr_err("%s: Invalid copp_id %d port_id 0x%x\n",
+ __func__, copp_id, port_id);
+ return 0;
+}
+
+int adm_get_topology_for_port_copp_idx(int port_id, int copp_idx)
+{
+ int port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid port: 0x%x copp id: 0x%x",
+ __func__, port_id, copp_idx);
+ return 0;
+ }
+ return atomic_read(&this_adm.copp.topology[port_idx][copp_idx]);
+}
+
+int adm_get_indexes_from_copp_id(int copp_id, int *copp_idx, int *port_idx)
+{
+ int p_idx, c_idx;
+ for (p_idx = 0; p_idx < AFE_MAX_PORTS; p_idx++) {
+ for (c_idx = 0; c_idx < MAX_COPPS_PER_PORT; c_idx++) {
+ if (atomic_read(&this_adm.copp.id[p_idx][c_idx])
+ == copp_id) {
+ if (copp_idx != NULL)
+ *copp_idx = c_idx;
+ if (port_idx != NULL)
+ *port_idx = p_idx;
+ return 0;
+ }
+ }
+ }
+ return -EINVAL;
+}
+
+static int adm_get_copp_id(int port_idx, int copp_idx)
+{
+ pr_debug("%s: port_idx:%d copp_idx:%d\n", __func__, port_idx, copp_idx);
+
+ if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+ return -EINVAL;
+ }
+ return atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+}
+
+static int adm_get_idx_if_single_copp_exists(int port_idx,
+ int topology, int mode,
+ int rate, int bit_width,
+ u32 copp_token)
+{
+ int idx;
+
+ pr_debug("%s: copp_token %d\n", __func__, copp_token);
+
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+ if ((topology ==
+ atomic_read(&this_adm.copp.topology[port_idx][idx])) &&
+ (mode ==
+ atomic_read(&this_adm.copp.mode[port_idx][idx])) &&
+ (rate ==
+ atomic_read(&this_adm.copp.rate[port_idx][idx])) &&
+ (bit_width ==
+ atomic_read(&this_adm.copp.bit_width[port_idx][idx])) &&
+ (copp_token ==
+ atomic_read(&this_adm.copp.token[port_idx][idx])))
+ return idx;
+ return -EINVAL;
+}
+
+static int adm_get_idx_if_copp_exists(int port_idx, int topology, int mode,
+ int rate, int bit_width, int app_type,
+ u32 copp_token)
+{
+ int idx;
+
+ pr_debug("%s: port_idx-%d, topology-0x%x, mode-%d, rate-%d, bit_width-%d\n",
+ __func__, port_idx, topology, mode, rate, bit_width);
+
+ if (copp_token)
+ return adm_get_idx_if_single_copp_exists(port_idx,
+ topology, mode,
+ rate, bit_width,
+ copp_token);
+
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+ if ((topology ==
+ atomic_read(&this_adm.copp.topology[port_idx][idx])) &&
+ (mode == atomic_read(&this_adm.copp.mode[port_idx][idx])) &&
+ (rate == atomic_read(&this_adm.copp.rate[port_idx][idx])) &&
+ (bit_width ==
+ atomic_read(&this_adm.copp.bit_width[port_idx][idx])) &&
+ (app_type ==
+ atomic_read(&this_adm.copp.app_type[port_idx][idx])))
+ return idx;
+ return -EINVAL;
+}
+
+static int adm_get_next_available_copp(int port_idx)
+{
+ int idx;
+
+ pr_debug("%s:\n", __func__);
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+ pr_debug("%s: copp_id:0x%x port_idx:%d idx:%d\n", __func__,
+ atomic_read(&this_adm.copp.id[port_idx][idx]),
+ port_idx, idx);
+ if (atomic_read(&this_adm.copp.id[port_idx][idx]) ==
+ RESET_COPP_ID)
+ break;
+ }
+ return idx;
+}
+
+static int adm_get_svc_version(uint32_t service_id)
+{
+ int ret = 0;
+ static int adm_cached_version;
+ size_t ver_size;
+ struct avcs_fwk_ver_info *ver_info = NULL;
+
+ if (service_id == AVCS_SERVICE_ID_ALL) {
+ pr_err("%s: Invalid service id: %d", __func__,
+ AVCS_SERVICE_ID_ALL);
+ return -EINVAL;
+ }
+
+ if (adm_cached_version != 0)
+ return adm_cached_version;
+
+ ver_size = sizeof(struct avcs_get_fwk_version) +
+ sizeof(struct avs_svc_api_info);
+ ver_info = kzalloc(ver_size, GFP_KERNEL);
+ if (ver_info == NULL)
+ return -ENOMEM;
+
+ ret = q6core_get_service_version(service_id, ver_info, ver_size);
+ if (ret < 0)
+ goto done;
+
+ ret = ver_info->services[0].api_version;
+ adm_cached_version = ret;
+done:
+ kfree(ver_info);
+ return ret;
+}
+int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id,
+ void *srs_params)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ struct mem_mapping_hdr mem_hdr = {0};
+ u32 total_param_size = 0;
+ bool outband = false;
+ int port_idx;
+ int ret = 0;
+
+ pr_debug("SRS - %s", __func__);
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+ return -EINVAL;
+ }
+
+ param_hdr.module_id = SRS_TRUMEDIA_MODULE_ID;
+ param_hdr.instance_id = INSTANCE_ID_0;
+
+ switch (srs_tech_id) {
+ case SRS_ID_GLOBAL: {
+ param_hdr.param_id = SRS_TRUMEDIA_PARAMS;
+ param_hdr.param_size =
+ sizeof(struct srs_trumedia_params_GLOBAL);
+ break;
+ }
+ case SRS_ID_WOWHD: {
+ param_hdr.param_id = SRS_TRUMEDIA_PARAMS_WOWHD;
+ param_hdr.param_size = sizeof(struct srs_trumedia_params_WOWHD);
+ break;
+ }
+ case SRS_ID_CSHP: {
+ param_hdr.param_id = SRS_TRUMEDIA_PARAMS_CSHP;
+ param_hdr.param_size = sizeof(struct srs_trumedia_params_CSHP);
+ break;
+ }
+ case SRS_ID_HPF: {
+ param_hdr.param_id = SRS_TRUMEDIA_PARAMS_HPF;
+ param_hdr.param_size = sizeof(struct srs_trumedia_params_HPF);
+ break;
+ }
+ case SRS_ID_AEQ: {
+ u8 *update_params_ptr = (u8 *) this_adm.outband_memmap.kvaddr;
+
+ outband = true;
+
+ if (update_params_ptr == NULL) {
+ pr_err("ADM_SRS_TRUMEDIA - %s: null memmap for AEQ params\n",
+ __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ param_hdr.param_id = SRS_TRUMEDIA_PARAMS_AEQ;
+ param_hdr.param_size = sizeof(struct srs_trumedia_params_AEQ);
+
+ ret = q6common_pack_pp_params(update_params_ptr, &param_hdr,
+ srs_params, &total_param_size);
+ if (ret) {
+ pr_err("%s: Failed to pack param header and data, error %d\n",
+ __func__, ret);
+ goto fail_cmd;
+ }
+ break;
+ }
+ case SRS_ID_HL: {
+ param_hdr.param_id = SRS_TRUMEDIA_PARAMS_HL;
+ param_hdr.param_size = sizeof(struct srs_trumedia_params_HL);
+ break;
+ }
+ case SRS_ID_GEQ: {
+ param_hdr.param_id = SRS_TRUMEDIA_PARAMS_GEQ;
+ param_hdr.param_size = sizeof(struct srs_trumedia_params_GEQ);
+ break;
+ }
+ default:
+ goto fail_cmd;
+ }
+
+ if (outband && this_adm.outband_memmap.paddr) {
+ mem_hdr.data_payload_addr_lsw =
+ lower_32_bits(this_adm.outband_memmap.paddr);
+ mem_hdr.data_payload_addr_msw =
+ msm_audio_populate_upper_32_bits(
+ this_adm.outband_memmap.paddr);
+ mem_hdr.mem_map_handle = atomic_read(
+ &this_adm.mem_map_handles[ADM_SRS_TRUMEDIA]);
+
+ ret = adm_set_pp_params(port_id, copp_idx, &mem_hdr, NULL,
+ total_param_size);
+ } else {
+ ret = adm_pack_and_set_one_pp_param(port_id, copp_idx,
+ param_hdr,
+ (u8 *) srs_params);
+ }
+
+ if (ret < 0)
+ pr_err("SRS - %s: ADM enable for port %d failed\n", __func__,
+ port_id);
+
+fail_cmd:
+ return ret;
+}
+
+static int adm_populate_channel_weight(u16 *ptr,
+ struct msm_pcm_channel_mixer *ch_mixer,
+ int channel_index)
+{
+ u16 i, j, start_index = 0;
+
+ if (channel_index > ch_mixer->output_channel) {
+ pr_err("%s: channel index %d is larger than output_channel %d\n",
+ __func__, channel_index, ch_mixer->output_channel);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ch_mixer->output_channel; i++) {
+ pr_debug("%s: weight for output %d:", __func__, i);
+ for (j = 0; j < ADM_MAX_CHANNELS; j++)
+ pr_debug(" %d",
+ ch_mixer->channel_weight[i][j]);
+ pr_debug("\n");
+ }
+
+ for (i = 0; i < channel_index; ++i)
+ start_index += ch_mixer->input_channels[i];
+
+ for (i = 0; i < ch_mixer->output_channel; ++i) {
+ for (j = start_index;
+ j < start_index +
+ ch_mixer->input_channels[channel_index]; j++) {
+ *ptr = ch_mixer->channel_weight[i][j];
+ pr_debug("%s: ptr[%d][%d] = %d\n",
+ __func__, i, j, *ptr);
+ ptr++;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * adm_programable_channel_mixer
+ *
+ * Receives port_id, copp_idx, session_id, session_type, ch_mixer
+ * and channel_index to send ADM command to mix COPP data.
+ *
+ * port_id - Passed value, port_id for which backend is wanted
+ * copp_idx - Passed value, copp_idx for which COPP is wanted
+ * session_id - Passed value, session_id for which session is needed
+ * session_type - Passed value, session_type for RX or TX
+ * ch_mixer - Passed value, ch_mixer for which channel mixer config is needed
+ * channel_index - Passed value, channel_index for which channel is needed
+ */
+int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id,
+ int session_type,
+ struct msm_pcm_channel_mixer *ch_mixer,
+ int channel_index)
+{
+ struct adm_cmd_set_pspd_mtmx_strtr_params_v5 *adm_params = NULL;
+ struct param_hdr_v1 data_v5 = {0,};
+ int ret = 0, port_idx, sz = 0, param_size = 0;
+ u16 *adm_pspd_params;
+ u16 *ptr;
+ int index = 0;
+
+ pr_debug("%s: port_id = %d\n", __func__, port_id);
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+ return -EINVAL;
+ }
+ /*
+ * First 8 bytes are 4 bytes as rule number, 2 bytes as output
+ * channel and 2 bytes as input channel.
+ * 2 * ch_mixer->output_channel means output channel mapping.
+ * 2 * ch_mixer->input_channels[channel_index]) means input
+ * channel mapping.
+ * 2 * ch_mixer->input_channels[channel_index] *
+ * ch_mixer->output_channel) means the channel mixer weighting
+ * coefficients.
+ * param_size needs to be a multiple of 4 bytes.
+ */
+
+ param_size = 2 * (4 + ch_mixer->output_channel +
+ ch_mixer->input_channels[channel_index] +
+ ch_mixer->input_channels[channel_index] *
+ ch_mixer->output_channel);
+ param_size = roundup(param_size, 4);
+
+ sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) +
+ sizeof(struct default_chmixer_param_id_coeff) +
+ sizeof(struct param_hdr_v3) + param_size;
+ pr_debug("%s: sz = %d\n", __func__, sz);
+ adm_params = kzalloc(sz, GFP_KERNEL);
+ if (!adm_params)
+ return -ENOMEM;
+
+ adm_params->payload_addr_lsw = 0;
+ adm_params->payload_addr_msw = 0;
+ adm_params->mem_map_handle = 0;
+ adm_params->direction = session_type;
+ adm_params->sessionid = session_id;
+ pr_debug("%s: copp_id = %d, session id %d\n", __func__,
+ atomic_read(&this_adm.copp.id[port_idx][copp_idx]),
+ session_id);
+ adm_params->deviceid = atomic_read(
+ &this_adm.copp.id[port_idx][copp_idx]);
+ adm_params->reserved = 0;
+
+ data_v5.module_id = MTMX_MODULE_ID_DEFAULT_CHMIXER;
+ data_v5.param_id = DEFAULT_CHMIXER_PARAM_ID_COEFF;
+ data_v5.reserved = 0;
+ data_v5.param_size = param_size;
+ adm_params->payload_size =
+ sizeof(struct default_chmixer_param_id_coeff) +
+ sizeof(struct param_hdr_v1) + data_v5.param_size;
+ adm_pspd_params = (u16 *)((u8 *)adm_params +
+ sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5));
+ memcpy(adm_pspd_params, &data_v5, sizeof(data_v5));
+
+ adm_pspd_params = (u16 *)((u8 *)adm_params +
+ sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5)
+ + sizeof(data_v5));
+
+ adm_pspd_params[0] = ch_mixer->rule;
+ adm_pspd_params[2] = ch_mixer->output_channel;
+ adm_pspd_params[3] = ch_mixer->input_channels[channel_index];
+ index = 4;
+
+ if (ch_mixer->override_cfg) {
+ memcpy(&adm_pspd_params[index], &ch_mixer->out_ch_map,
+ ch_mixer->output_channel * sizeof(uint16_t));
+ } else {
+ if (ch_mixer->output_channel == 1) {
+ adm_pspd_params[index] = PCM_CHANNEL_FC;
+ } else if (ch_mixer->output_channel == 2) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ } else if (ch_mixer->output_channel == 3) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_FC;
+ } else if (ch_mixer->output_channel == 4) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->output_channel == 5) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->output_channel == 6) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LFE;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 5] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->output_channel == 8) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LFE;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 5] = PCM_CHANNEL_RS;
+ adm_pspd_params[index + 6] = PCM_CHANNEL_LB;
+ adm_pspd_params[index + 7] = PCM_CHANNEL_RB;
+ }
+ }
+
+ index = index + ch_mixer->output_channel;
+ if (ch_mixer->override_cfg) {
+ memcpy(&adm_pspd_params[index], &ch_mixer->in_ch_map,
+ ch_mixer->input_channel * sizeof(uint16_t));
+ } else {
+ if (ch_mixer->input_channels[channel_index] == 1) {
+ adm_pspd_params[index] = PCM_CHANNEL_FC;
+ } else if (ch_mixer->input_channels[channel_index] == 2) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ } else if (ch_mixer->input_channels[channel_index] == 3) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_FC;
+ } else if (ch_mixer->input_channels[channel_index] == 4) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->input_channels[channel_index] == 5) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->input_channels[channel_index] == 6) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LFE;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 5] = PCM_CHANNEL_RS;
+ } else if (ch_mixer->input_channels[channel_index] == 8) {
+ adm_pspd_params[index] = PCM_CHANNEL_FL;
+ adm_pspd_params[index + 1] = PCM_CHANNEL_FR;
+ adm_pspd_params[index + 2] = PCM_CHANNEL_LFE;
+ adm_pspd_params[index + 3] = PCM_CHANNEL_FC;
+ adm_pspd_params[index + 4] = PCM_CHANNEL_LS;
+ adm_pspd_params[index + 5] = PCM_CHANNEL_RS;
+ adm_pspd_params[index + 6] = PCM_CHANNEL_LB;
+ adm_pspd_params[index + 7] = PCM_CHANNEL_RB;
+ }
+ }
+
+ index = index + ch_mixer->input_channels[channel_index];
+ ret = adm_populate_channel_weight(&adm_pspd_params[index],
+ ch_mixer, channel_index);
+ if (ret) {
+ pr_err("%s: fail to get channel weight with error %d\n",
+ __func__, ret);
+ goto fail_cmd;
+ }
+
+ adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ adm_params->hdr.src_svc = APR_SVC_ADM;
+ adm_params->hdr.src_domain = APR_DOMAIN_APPS;
+ adm_params->hdr.src_port = port_id;
+ adm_params->hdr.dest_svc = APR_SVC_ADM;
+ adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
+ adm_params->hdr.dest_port =
+ atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+ adm_params->hdr.token = port_idx << 16 | copp_idx;
+ adm_params->hdr.opcode = ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5;
+ adm_params->hdr.pkt_size = sz;
+ adm_params->payload_addr_lsw = 0;
+ adm_params->payload_addr_msw = 0;
+ adm_params->mem_map_handle = 0;
+ adm_params->reserved = 0;
+
+ ptr = (u16 *)adm_params;
+ for (index = 0; index < (sz / 2); index++)
+ pr_debug("%s: adm_params[%d] = 0x%x\n",
+ __func__, index, (unsigned int)ptr[index]);
+
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx], 0);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params);
+ if (ret < 0) {
+ pr_err("%s: Set params failed port %d rc %d\n", __func__,
+ port_id, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+ atomic_read(
+ &this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: set params timed out port = %d\n",
+ __func__, port_id);
+ ret = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ ret = 0;
+fail_cmd:
+ kfree(adm_params);
+
+ return ret;
+}
+
+int adm_set_pspd_matrix_params(int port_id, int copp_idx,
+ unsigned int session_id, char *params,
+ uint32_t params_length,
+ int session_type)
+{
+ struct adm_cmd_set_pspd_mtmx_strtr_params_v5 *adm_params = NULL;
+ int sz, rc = 0, port_idx;
+
+ pr_debug("%s:\n", __func__);
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ }
+
+ sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) +
+ params_length;
+ adm_params = kzalloc(sz, GFP_KERNEL);
+ if (!adm_params) {
+ pr_err("%s, adm params memory alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(((u8 *)adm_params +
+ sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5)),
+ params, params_length);
+ adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ adm_params->hdr.pkt_size = sz;
+ adm_params->hdr.src_svc = APR_SVC_ADM;
+ adm_params->hdr.src_domain = APR_DOMAIN_APPS;
+ adm_params->hdr.src_port = port_id;
+ adm_params->hdr.dest_svc = APR_SVC_ADM;
+ adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
+ adm_params->hdr.dest_port = 0; /* Ignored */;
+ adm_params->hdr.token = port_idx << 16 | copp_idx;
+ adm_params->hdr.opcode = ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5;
+ adm_params->payload_addr_lsw = 0;
+ adm_params->payload_addr_msw = 0;
+ adm_params->mem_map_handle = 0;
+ adm_params->payload_size = params_length;
+ /* direction RX as 0 */
+ adm_params->direction = session_type;
+ /* session id for this cmd to be applied on */
+ adm_params->sessionid = session_id;
+ adm_params->deviceid =
+ atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+ adm_params->reserved = 0;
+ pr_debug("%s: deviceid %d, session_id %d, src_port %d, dest_port %d\n",
+ __func__, adm_params->deviceid, adm_params->sessionid,
+ adm_params->hdr.src_port, adm_params->hdr.dest_port);
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+ rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params);
+ if (rc < 0) {
+ pr_err("%s: Set params failed port = 0x%x rc %d\n",
+ __func__, port_id, rc);
+ rc = -EINVAL;
+ goto set_stereo_to_custom_stereo_return;
+ }
+ /* Wait for the callback */
+ rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!rc) {
+ pr_err("%s: Set params timed out port = 0x%x\n", __func__,
+ port_id);
+ rc = -EINVAL;
+ goto set_stereo_to_custom_stereo_return;
+ } else if (atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]) > 0) {
+ pr_err("%s: DSP returned error[%s]\n", __func__,
+ adsp_err_get_err_str(atomic_read(
+ &this_adm.copp.stat
+ [port_idx][copp_idx])));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]));
+ goto set_stereo_to_custom_stereo_return;
+ }
+ rc = 0;
+set_stereo_to_custom_stereo_return:
+ kfree(adm_params);
+ return rc;
+}
+
+/*
+ * With pre-packed data, only the opcode differes from V5 and V6.
+ * Use q6common_pack_pp_params to pack the data correctly.
+ */
+int adm_set_pp_params(int port_id, int copp_idx,
+ struct mem_mapping_hdr *mem_hdr, u8 *param_data,
+ u32 param_size)
+{
+ struct adm_cmd_set_pp_params *adm_set_params = NULL;
+ int size = sizeof(struct adm_cmd_set_pp_params);
+ int port_idx = 0;
+ atomic_t *copp_stat = NULL;
+ int ret = 0;
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
+ pr_err("%s: Invalid port_idx 0x%x\n", __func__, port_idx);
+ return -EINVAL;
+ } else if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_idx 0x%x\n", __func__, copp_idx);
+ return -EINVAL;
+ }
+
+ /* Only add params_size in inband case */
+ if (param_data != NULL)
+ size += param_size;
+ adm_set_params = kzalloc(size, GFP_KERNEL);
+ if (!adm_set_params)
+ return -ENOMEM;
+
+ adm_set_params->apr_hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ adm_set_params->apr_hdr.pkt_size = size;
+ adm_set_params->apr_hdr.src_svc = APR_SVC_ADM;
+ adm_set_params->apr_hdr.src_domain = APR_DOMAIN_APPS;
+ adm_set_params->apr_hdr.src_port = port_id;
+ adm_set_params->apr_hdr.dest_svc = APR_SVC_ADM;
+ adm_set_params->apr_hdr.dest_domain = APR_DOMAIN_ADSP;
+ adm_set_params->apr_hdr.dest_port =
+ atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+ adm_set_params->apr_hdr.token = port_idx << 16 | copp_idx;
+
+ if (q6common_is_instance_id_supported())
+ adm_set_params->apr_hdr.opcode = ADM_CMD_SET_PP_PARAMS_V6;
+ else
+ adm_set_params->apr_hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+
+ adm_set_params->payload_size = param_size;
+
+ if (mem_hdr != NULL) {
+ /* Out of Band Case */
+ adm_set_params->mem_hdr = *mem_hdr;
+ } else if (param_data != NULL) {
+ /* In band case. Parameter data must be pre-packed with its
+ * header before calling this function. Use
+ * q6common_pack_pp_params to pack parameter data and header
+ * correctly.
+ */
+ memcpy(&adm_set_params->param_data, param_data, param_size);
+ } else {
+ pr_err("%s: Received NULL pointers for both memory header and param data\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ copp_stat = &this_adm.copp.stat[port_idx][copp_idx];
+ atomic_set(copp_stat, -1);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *) adm_set_params);
+ if (ret < 0) {
+ pr_err("%s: Set params APR send failed port = 0x%x ret %d\n",
+ __func__, port_id, ret);
+ goto done;
+ }
+ ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+ atomic_read(copp_stat) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Set params timed out port = 0x%x\n", __func__,
+ port_id);
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+ if (atomic_read(copp_stat) > 0) {
+ pr_err("%s: DSP returned error[%s]\n", __func__,
+ adsp_err_get_err_str(atomic_read(copp_stat)));
+ ret = adsp_err_get_lnx_err_code(atomic_read(copp_stat));
+ goto done;
+ }
+
+ ret = 0;
+done:
+ kfree(adm_set_params);
+ return ret;
+}
+EXPORT_SYMBOL(adm_set_pp_params);
+
+int adm_pack_and_set_one_pp_param(int port_id, int copp_idx,
+ struct param_hdr_v3 param_hdr, u8 *param_data)
+{
+ u8 *packed_data = NULL;
+ u32 total_size = 0;
+ int ret = 0;
+
+ total_size = sizeof(union param_hdrs) + param_hdr.param_size;
+ packed_data = kzalloc(total_size, GFP_KERNEL);
+ if (!packed_data)
+ return -ENOMEM;
+
+ ret = q6common_pack_pp_params(packed_data, &param_hdr, param_data,
+ &total_size);
+ if (ret) {
+ pr_err("%s: Failed to pack parameter data, error %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = adm_set_pp_params(port_id, copp_idx, NULL, packed_data,
+ total_size);
+ if (ret)
+ pr_err("%s: Failed to set parameter data, error %d\n", __func__,
+ ret);
+done:
+ kfree(packed_data);
+ return ret;
+}
+EXPORT_SYMBOL(adm_pack_and_set_one_pp_param);
+
+/*
+ * Only one parameter can be requested at a time. Therefore, packing and sending
+ * the request can be handled locally.
+ */
+int adm_get_pp_params(int port_id, int copp_idx, uint32_t client_id,
+ struct mem_mapping_hdr *mem_hdr,
+ struct param_hdr_v3 *param_hdr, u8 *returned_param_data)
+{
+ struct adm_cmd_get_pp_params adm_get_params;
+ int total_size = 0;
+ int get_param_array_sz = ARRAY_SIZE(adm_get_parameters);
+ int returned_param_size = 0;
+ int returned_param_size_in_bytes = 0;
+ int port_idx = 0;
+ int idx = 0;
+ atomic_t *copp_stat = NULL;
+ int ret = 0;
+
+ if (param_hdr == NULL) {
+ pr_err("%s: Received NULL pointer for parameter header\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
+ pr_err("%s: Invalid port_idx 0x%x\n", __func__, port_idx);
+ return -EINVAL;
+ }
+ if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_idx 0x%x\n", __func__, copp_idx);
+ return -EINVAL;
+ }
+
+ memset(&adm_get_params, 0, sizeof(adm_get_params));
+
+ if (mem_hdr != NULL)
+ adm_get_params.mem_hdr = *mem_hdr;
+
+ q6common_pack_pp_params((u8 *) &adm_get_params.param_hdr, param_hdr,
+ NULL, &total_size);
+
+ /* Pack APR header after filling body so total_size has correct value */
+ adm_get_params.apr_hdr.pkt_size = total_size;
+ adm_get_params.apr_hdr.src_svc = APR_SVC_ADM;
+ adm_get_params.apr_hdr.src_domain = APR_DOMAIN_APPS;
+ adm_get_params.apr_hdr.src_port = port_id;
+ adm_get_params.apr_hdr.dest_svc = APR_SVC_ADM;
+ adm_get_params.apr_hdr.dest_domain = APR_DOMAIN_ADSP;
+ adm_get_params.apr_hdr.dest_port =
+ atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+ adm_get_params.apr_hdr.token =
+ port_idx << 16 | client_id << 8 | copp_idx;
+
+ if (q6common_is_instance_id_supported())
+ adm_get_params.apr_hdr.opcode = ADM_CMD_GET_PP_PARAMS_V6;
+ else
+ adm_get_params.apr_hdr.opcode = ADM_CMD_GET_PP_PARAMS_V5;
+
+ copp_stat = &this_adm.copp.stat[port_idx][copp_idx];
+ atomic_set(copp_stat, -1);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *) &adm_get_params);
+ if (ret < 0) {
+ pr_err("%s: Get params APR send failed port = 0x%x ret %d\n",
+ __func__, port_id, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+ atomic_read(copp_stat) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Get params timed out port = 0x%x\n", __func__,
+ port_id);
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+ if (atomic_read(copp_stat) > 0) {
+ pr_err("%s: DSP returned error[%s]\n", __func__,
+ adsp_err_get_err_str(atomic_read(copp_stat)));
+ ret = adsp_err_get_lnx_err_code(atomic_read(copp_stat));
+ goto done;
+ }
+
+ ret = 0;
+
+ /* Copy data to caller if sent in band */
+ if (!returned_param_data) {
+ pr_debug("%s: Received NULL pointer for param destination, not copying payload\n",
+ __func__);
+ return 0;
+ }
+
+ idx = ADM_GET_PARAMETER_LENGTH * copp_idx;
+ returned_param_size = adm_get_parameters[idx];
+ if (returned_param_size < 0 ||
+ returned_param_size + idx + 1 > get_param_array_sz) {
+ pr_err("%s: Invalid parameter size %d\n", __func__,
+ returned_param_size);
+ return -EINVAL;
+ }
+
+ returned_param_size_in_bytes = returned_param_size * sizeof(uint32_t);
+ if (param_hdr->param_size < returned_param_size_in_bytes) {
+ pr_err("%s: Provided buffer is not big enough, provided buffer size(%d) size needed(%d)\n",
+ __func__, param_hdr->param_size,
+ returned_param_size_in_bytes);
+ return -EINVAL;
+ }
+
+ memcpy(returned_param_data, &adm_get_parameters[idx + 1],
+ returned_param_size_in_bytes);
+done:
+ return ret;
+}
+EXPORT_SYMBOL(adm_get_pp_params);
+
+int adm_get_pp_topo_module_list_v2(int port_id, int copp_idx,
+ int32_t param_length,
+ int32_t *returned_params)
+{
+ struct adm_cmd_get_pp_topo_module_list adm_get_module_list;
+ bool iid_supported = q6common_is_instance_id_supported();
+ int *topo_list;
+ int num_modules = 0;
+ int list_size = 0;
+ int port_idx, idx;
+ int i = 0;
+ atomic_t *copp_stat = NULL;
+ int ret = 0;
+
+ pr_debug("%s : port_id %x", __func__, port_id);
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ }
+ if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+ return -EINVAL;
+ }
+
+ memset(&adm_get_module_list, 0, sizeof(adm_get_module_list));
+
+ adm_get_module_list.apr_hdr.pkt_size = sizeof(adm_get_module_list);
+ adm_get_module_list.apr_hdr.src_svc = APR_SVC_ADM;
+ adm_get_module_list.apr_hdr.src_domain = APR_DOMAIN_APPS;
+ adm_get_module_list.apr_hdr.src_port = port_id;
+ adm_get_module_list.apr_hdr.dest_svc = APR_SVC_ADM;
+ adm_get_module_list.apr_hdr.dest_domain = APR_DOMAIN_ADSP;
+ adm_get_module_list.apr_hdr.dest_port =
+ atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+ adm_get_module_list.apr_hdr.token = port_idx << 16 | copp_idx;
+ /*
+ * Out of band functionality is not currently utilized.
+ * Assume in band.
+ */
+ if (iid_supported) {
+ adm_get_module_list.apr_hdr.opcode =
+ ADM_CMD_GET_PP_TOPO_MODULE_LIST_V2;
+ adm_get_module_list.param_max_size = param_length;
+ } else {
+ adm_get_module_list.apr_hdr.opcode =
+ ADM_CMD_GET_PP_TOPO_MODULE_LIST;
+
+ if (param_length > U16_MAX) {
+ pr_err("%s: Invalid param length for V1 %d\n", __func__,
+ param_length);
+ return -EINVAL;
+ }
+ adm_get_module_list.param_max_size = param_length << 16;
+ }
+
+ copp_stat = &this_adm.copp.stat[port_idx][copp_idx];
+ atomic_set(copp_stat, -1);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *) &adm_get_module_list);
+ if (ret < 0) {
+ pr_err("%s: APR send pkt failed for port_id: 0x%x failed ret %d\n",
+ __func__, port_id, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+ atomic_read(copp_stat) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Timeout for port_id: 0x%x\n", __func__, port_id);
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+ if (atomic_read(copp_stat) > 0) {
+ pr_err("%s: DSP returned error[%s]\n", __func__,
+ adsp_err_get_err_str(atomic_read(copp_stat)));
+ ret = adsp_err_get_lnx_err_code(atomic_read(copp_stat));
+ goto done;
+ }
+
+ ret = 0;
+
+ if (returned_params) {
+ /*
+ * When processing ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST IID is
+ * added since it is not present. Therefore, there is no need to
+ * do anything different if IID is not supported here as it is
+ * already taken care of.
+ */
+ idx = ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH * copp_idx;
+ num_modules = adm_module_topo_list[idx];
+ if (num_modules < 0 || num_modules > MAX_MODULES_IN_TOPO) {
+ pr_err("%s: Invalid number of modules returned %d\n",
+ __func__, num_modules);
+ return -EINVAL;
+ }
+
+ list_size = num_modules * sizeof(struct module_instance_info);
+ if (param_length < list_size) {
+ pr_err("%s: Provided buffer not big enough to hold module-instance list, provided size %d, needed size %d\n",
+ __func__, param_length, list_size);
+ return -EINVAL;
+ }
+
+ topo_list = (int32_t *) (&adm_module_topo_list[idx]);
+ memcpy(returned_params, topo_list, list_size);
+ for (i = 1; i <= num_modules; i += 2) {
+ pr_debug("module = 0x%x instance = 0x%x\n",
+ returned_params[i], returned_params[i + 1]);
+ }
+ }
+done:
+ return ret;
+}
+EXPORT_SYMBOL(adm_get_pp_topo_module_list_v2);
+
+static void adm_callback_debug_print(struct apr_client_data *data)
+{
+ uint32_t *payload;
+ payload = data->payload;
+
+ if (data->payload_size >= 8)
+ pr_debug("%s: code = 0x%x PL#0[0x%x], PL#1[0x%x], size = %d\n",
+ __func__, data->opcode, payload[0], payload[1],
+ data->payload_size);
+ else if (data->payload_size >= 4)
+ pr_debug("%s: code = 0x%x PL#0[0x%x], size = %d\n",
+ __func__, data->opcode, payload[0],
+ data->payload_size);
+ else
+ pr_debug("%s: code = 0x%x, size = %d\n",
+ __func__, data->opcode, data->payload_size);
+}
+
+int adm_set_multi_ch_map(char *channel_map, int path)
+{
+ int idx;
+
+ if (path == ADM_PATH_PLAYBACK) {
+ idx = ADM_MCH_MAP_IDX_PLAYBACK;
+ } else if (path == ADM_PATH_LIVE_REC) {
+ idx = ADM_MCH_MAP_IDX_REC;
+ } else {
+ pr_err("%s: invalid attempt to set path %d\n", __func__, path);
+ return -EINVAL;
+ }
+
+ memcpy(multi_ch_maps[idx].channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL_V2);
+ multi_ch_maps[idx].set_channel_map = true;
+
+ return 0;
+}
+
+int adm_get_multi_ch_map(char *channel_map, int path)
+{
+ int idx;
+
+ if (path == ADM_PATH_PLAYBACK) {
+ idx = ADM_MCH_MAP_IDX_PLAYBACK;
+ } else if (path == ADM_PATH_LIVE_REC) {
+ idx = ADM_MCH_MAP_IDX_REC;
+ } else {
+ pr_err("%s: invalid attempt to get path %d\n", __func__, path);
+ return -EINVAL;
+ }
+
+ if (multi_ch_maps[idx].set_channel_map) {
+ memcpy(channel_map, multi_ch_maps[idx].channel_mapping,
+ PCM_FORMAT_MAX_NUM_CHANNEL_V2);
+ }
+
+ return 0;
+}
+
+static int adm_process_get_param_response(u32 opcode, u32 idx, u32 *payload,
+ u32 payload_size)
+{
+ struct adm_cmd_rsp_get_pp_params_v5 *v5_rsp = NULL;
+ struct adm_cmd_rsp_get_pp_params_v6 *v6_rsp = NULL;
+ u32 *param_data = NULL;
+ int data_size = 0;
+ int struct_size;
+
+ if (payload == NULL) {
+ pr_err("%s: Payload is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (opcode) {
+ case ADM_CMDRSP_GET_PP_PARAMS_V5:
+ struct_size = sizeof(struct adm_cmd_rsp_get_pp_params_v5);
+ if (payload_size < struct_size) {
+ pr_err("%s: payload size %d < expected size %d\n",
+ __func__, payload_size, struct_size);
+ return -EINVAL;
+ }
+ v5_rsp = (struct adm_cmd_rsp_get_pp_params_v5 *) payload;
+ data_size = v5_rsp->param_hdr.param_size;
+ param_data = v5_rsp->param_data;
+ break;
+ case ADM_CMDRSP_GET_PP_PARAMS_V6:
+ struct_size = sizeof(struct adm_cmd_rsp_get_pp_params_v6);
+ if (payload_size < struct_size) {
+ pr_err("%s: payload size %d < expected size %d\n",
+ __func__, payload_size, struct_size);
+ return -EINVAL;
+ }
+ v6_rsp = (struct adm_cmd_rsp_get_pp_params_v6 *) payload;
+ data_size = v6_rsp->param_hdr.param_size;
+ param_data = v6_rsp->param_data;
+ break;
+ default:
+ pr_err("%s: Invalid opcode %d\n", __func__, opcode);
+ return -EINVAL;
+ }
+
+ /*
+ * Just store the returned parameter data, not the header. The calling
+ * function is expected to know what it asked for. Therefore, there is
+ * no difference between V5 and V6.
+ */
+ if ((payload_size >= struct_size + data_size) &&
+ (ARRAY_SIZE(adm_get_parameters) > idx) &&
+ (ARRAY_SIZE(adm_get_parameters) >= idx + 1 + data_size)) {
+ /*
+ * data_size is expressed in number of bytes, store in number of
+ * ints
+ */
+ adm_get_parameters[idx] =
+ data_size / sizeof(*adm_get_parameters);
+ pr_debug("%s: GET_PP PARAM: received parameter length: 0x%x\n",
+ __func__, adm_get_parameters[idx]);
+ /* store params after param_size */
+ if (param_data == NULL) {
+ pr_err("%s: Invalid parameter data got!\n", __func__);
+ return -EINVAL;
+ }
+ memcpy(&adm_get_parameters[idx + 1], param_data, data_size);
+ return 0;
+ }
+
+ pr_err("%s: Invlaid parameter combination, payload_size %d, idx %d\n",
+ __func__, payload_size, idx);
+ return -EINVAL;
+}
+
+static int adm_process_get_topo_list_response(u32 opcode, int copp_idx,
+ u32 num_modules, u32 *payload,
+ u32 payload_size)
+{
+ u32 *fill_list = NULL;
+ int idx = 0;
+ int i = 0;
+ int j = 0;
+
+ if (payload == NULL) {
+ pr_err("%s: Payload is NULL\n", __func__);
+ return -EINVAL;
+ } else if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT)
+ pr_err("%s: Invalid COPP index %d\n", __func__, copp_idx);
+ return -EINVAL;
+
+ idx = ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH * copp_idx;
+ fill_list = adm_module_topo_list + idx;
+ *fill_list++ = num_modules;
+ for (i = 0; i < num_modules; i++) {
+ if (j > payload_size / sizeof(u32)) {
+ pr_err("%s: Invalid number of modules specified %d\n",
+ __func__, num_modules);
+ return -EINVAL;
+ }
+
+ /* store module ID */
+ *fill_list++ = payload[j];
+ j++;
+
+ switch (opcode) {
+ case ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST_V2:
+ /* store instance ID */
+ *fill_list++ = payload[j];
+ j++;
+ break;
+ case ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST:
+ /* Insert IID 0 when repacking */
+ *fill_list++ = INSTANCE_ID_0;
+ break;
+ default:
+ pr_err("%s: Invalid opcode %d\n", __func__, opcode);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int32_t adm_callback(struct apr_client_data *data, void *priv)
+{
+ uint32_t *payload;
+ int i, j, port_idx, copp_idx, idx, client_id;
+ int num_modules;
+ int ret;
+
+ if (data == NULL) {
+ pr_err("%s: data parameter is null\n", __func__);
+ return -EINVAL;
+ }
+
+ payload = data->payload;
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("%s: Reset event is received: %d %d apr[%pK]\n",
+ __func__,
+ data->reset_event, data->reset_proc, this_adm.apr);
+ if (this_adm.apr) {
+ apr_reset(this_adm.apr);
+ for (i = 0; i < AFE_MAX_PORTS; i++) {
+ for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+ atomic_set(&this_adm.copp.id[i][j],
+ RESET_COPP_ID);
+ atomic_set(&this_adm.copp.cnt[i][j], 0);
+ atomic_set(
+ &this_adm.copp.topology[i][j],
+ 0);
+ atomic_set(&this_adm.copp.mode[i][j],
+ 0);
+ atomic_set(&this_adm.copp.stat[i][j],
+ 0);
+ atomic_set(&this_adm.copp.rate[i][j],
+ 0);
+ atomic_set(
+ &this_adm.copp.channels[i][j],
+ 0);
+ atomic_set(
+ &this_adm.copp.bit_width[i][j], 0);
+ atomic_set(
+ &this_adm.copp.app_type[i][j], 0);
+ atomic_set(
+ &this_adm.copp.acdb_id[i][j], 0);
+ this_adm.copp.adm_status[i][j] =
+ ADM_STATUS_CALIBRATION_REQUIRED;
+ }
+ }
+ this_adm.apr = NULL;
+ cal_utils_clear_cal_block_q6maps(ADM_MAX_CAL_TYPES,
+ this_adm.cal_data);
+ mutex_lock(&this_adm.cal_data
+ [ADM_CUSTOM_TOP_CAL]->lock);
+ this_adm.set_custom_topology = 1;
+ mutex_unlock(&this_adm.cal_data[
+ ADM_CUSTOM_TOP_CAL]->lock);
+ rtac_clear_mapping(ADM_RTAC_CAL);
+ /*
+ * Free the ION memory and clear the map handles
+ * for Source Tracking
+ */
+ if (this_adm.sourceTrackingData.memmap.paddr != 0) {
+ msm_audio_ion_free(
+ this_adm.sourceTrackingData.ion_client,
+ this_adm.sourceTrackingData.ion_handle);
+ this_adm.sourceTrackingData.ion_client = NULL;
+ this_adm.sourceTrackingData.ion_handle = NULL;
+ this_adm.sourceTrackingData.memmap.size = 0;
+ this_adm.sourceTrackingData.memmap.kvaddr =
+ NULL;
+ this_adm.sourceTrackingData.memmap.paddr = 0;
+ this_adm.sourceTrackingData.apr_cmd_status = -1;
+ atomic_set(&this_adm.mem_map_handles[
+ ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0);
+ }
+ }
+ return 0;
+ }
+
+ adm_callback_debug_print(data);
+ if (data->payload_size >= sizeof(uint32_t)) {
+ copp_idx = (data->token) & 0XFF;
+ port_idx = ((data->token) >> 16) & 0xFF;
+ client_id = ((data->token) >> 8) & 0xFF;
+ if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
+ pr_err("%s: Invalid port idx %d token %d\n",
+ __func__, port_idx, data->token);
+ return 0;
+ }
+ if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp idx %d token %d\n",
+ __func__, copp_idx, data->token);
+ return 0;
+ }
+ if (client_id < 0 || client_id >= ADM_CLIENT_ID_MAX) {
+ pr_err("%s: Invalid client id %d\n", __func__,
+ client_id);
+ return 0;
+ }
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ pr_debug("%s: APR_BASIC_RSP_RESULT id 0x%x\n",
+ __func__, payload[0]);
+ if (!((client_id != ADM_CLIENT_ID_SOURCE_TRACKING) &&
+ ((payload[0] == ADM_CMD_SET_PP_PARAMS_V5) ||
+ (payload[0] == ADM_CMD_SET_PP_PARAMS_V6)))) {
+ if (data->payload_size <
+ (2 * sizeof(uint32_t))) {
+ pr_err("%s: Invalid payload size %d\n",
+ __func__, data->payload_size);
+ return 0;
+ }
+ }
+ if (payload[1] != 0) {
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, payload[0], payload[1]);
+ }
+ switch (payload[0]) {
+ case ADM_CMD_SET_PP_PARAMS_V5:
+ case ADM_CMD_SET_PP_PARAMS_V6:
+ pr_debug("%s: ADM_CMD_SET_PP_PARAMS\n",
+ __func__);
+ if (client_id == ADM_CLIENT_ID_SOURCE_TRACKING)
+ this_adm.sourceTrackingData.
+ apr_cmd_status = payload[1];
+ else if (rtac_make_adm_callback(payload,
+ data->payload_size))
+ break;
+ /*
+ * if soft volume is called and already
+ * interrupted break out of the sequence here
+ */
+ case ADM_CMD_DEVICE_OPEN_V5:
+ case ADM_CMD_DEVICE_CLOSE_V5:
+ case ADM_CMD_DEVICE_OPEN_V6:
+ case ADM_CMD_DEVICE_OPEN_V8:
+ case ADM_CMD_SET_MTMX_STRTR_DEV_PARAMS_V1:
+ pr_debug("%s: Basic callback received, wake up.\n",
+ __func__);
+ atomic_set(&this_adm.copp.stat[port_idx]
+ [copp_idx], payload[1]);
+ wake_up(
+ &this_adm.copp.wait[port_idx][copp_idx]);
+ break;
+ case ADM_CMD_ADD_TOPOLOGIES:
+ pr_debug("%s: callback received, ADM_CMD_ADD_TOPOLOGIES.\n",
+ __func__);
+ atomic_set(&this_adm.adm_stat, payload[1]);
+ wake_up(&this_adm.adm_wait);
+ break;
+ case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
+ case ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5:
+ pr_debug("%s: Basic callback received, wake up.\n",
+ __func__);
+ atomic_set(&this_adm.matrix_map_stat,
+ payload[1]);
+ wake_up(&this_adm.matrix_map_wait);
+ break;
+ case ADM_CMD_SHARED_MEM_UNMAP_REGIONS:
+ pr_debug("%s: ADM_CMD_SHARED_MEM_UNMAP_REGIONS\n",
+ __func__);
+ atomic_set(&this_adm.adm_stat, payload[1]);
+ wake_up(&this_adm.adm_wait);
+ break;
+ case ADM_CMD_SHARED_MEM_MAP_REGIONS:
+ pr_debug("%s: ADM_CMD_SHARED_MEM_MAP_REGIONS\n",
+ __func__);
+ /* Should only come here if there is an APR */
+ /* error or malformed APR packet. Otherwise */
+ /* response will be returned as */
+ if (payload[1] != 0) {
+ pr_err("%s: ADM map error, resuming\n",
+ __func__);
+ atomic_set(&this_adm.adm_stat,
+ payload[1]);
+ wake_up(&this_adm.adm_wait);
+ }
+ break;
+ case ADM_CMD_GET_PP_PARAMS_V5:
+ case ADM_CMD_GET_PP_PARAMS_V6:
+ pr_debug("%s: ADM_CMD_GET_PP_PARAMS\n",
+ __func__);
+ /* Should only come here if there is an APR */
+ /* error or malformed APR packet. Otherwise */
+ /* response will be returned as */
+ /* ADM_CMDRSP_GET_PP_PARAMS_V5 */
+ if (client_id ==
+ ADM_CLIENT_ID_SOURCE_TRACKING) {
+ this_adm.sourceTrackingData.
+ apr_cmd_status = payload[1];
+ if (payload[1] != 0)
+ pr_err("%s: ADM get param error = %d\n",
+ __func__, payload[1]);
+
+ atomic_set(&this_adm.copp.stat
+ [port_idx][copp_idx],
+ payload[1]);
+ wake_up(&this_adm.copp.wait
+ [port_idx][copp_idx]);
+ } else {
+ if (payload[1] != 0) {
+ pr_err("%s: ADM get param error = %d, resuming\n",
+ __func__, payload[1]);
+
+ rtac_make_adm_callback(payload,
+ data->payload_size);
+ }
+ }
+ break;
+ case ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5:
+ pr_debug("%s: ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5\n",
+ __func__);
+ atomic_set(&this_adm.copp.stat[port_idx]
+ [copp_idx], payload[1]);
+ wake_up(
+ &this_adm.copp.wait[port_idx][copp_idx]);
+ break;
+ case ADM_CMD_GET_PP_TOPO_MODULE_LIST:
+ case ADM_CMD_GET_PP_TOPO_MODULE_LIST_V2:
+ pr_debug("%s:ADM_CMD_GET_PP_TOPO_MODULE_LIST\n",
+ __func__);
+ if (payload[1] != 0)
+ pr_err("%s: ADM get topo list error = %d\n",
+ __func__, payload[1]);
+ break;
+ default:
+ pr_err("%s: Unknown Cmd: 0x%x\n", __func__,
+ payload[0]);
+ break;
+ }
+ return 0;
+ }
+
+ switch (data->opcode) {
+ case ADM_CMDRSP_DEVICE_OPEN_V5:
+ case ADM_CMDRSP_DEVICE_OPEN_V6:
+ case ADM_CMDRSP_DEVICE_OPEN_V8: {
+ struct adm_cmd_rsp_device_open_v5 *open = NULL;
+
+ if (data->payload_size <
+ sizeof(struct adm_cmd_rsp_device_open_v5)) {
+ pr_err("%s: Invalid payload size %d\n",
+ __func__, data->payload_size);
+ return 0;
+ }
+ open =
+ (struct adm_cmd_rsp_device_open_v5 *)data->payload;
+ if (open->copp_id == INVALID_COPP_ID) {
+ pr_err("%s: invalid coppid rxed %d\n",
+ __func__, open->copp_id);
+ atomic_set(&this_adm.copp.stat[port_idx]
+ [copp_idx], ADSP_EBADPARAM);
+ wake_up(
+ &this_adm.copp.wait[port_idx][copp_idx]);
+ break;
+ }
+ atomic_set(&this_adm.copp.stat
+ [port_idx][copp_idx], payload[0]);
+ atomic_set(&this_adm.copp.id[port_idx][copp_idx],
+ open->copp_id);
+ pr_debug("%s: coppid rxed=%d\n", __func__,
+ open->copp_id);
+ wake_up(&this_adm.copp.wait[port_idx][copp_idx]);
+ }
+ break;
+ case ADM_CMDRSP_GET_PP_PARAMS_V5:
+ case ADM_CMDRSP_GET_PP_PARAMS_V6:
+ pr_debug("%s: ADM_CMDRSP_GET_PP_PARAMS\n", __func__);
+ if (client_id == ADM_CLIENT_ID_SOURCE_TRACKING)
+ this_adm.sourceTrackingData.apr_cmd_status =
+ payload[0];
+ else if (rtac_make_adm_callback(payload,
+ data->payload_size))
+ break;
+
+ idx = ADM_GET_PARAMETER_LENGTH * copp_idx;
+ if (payload[0] == 0 && data->payload_size > 0) {
+ pr_debug("%s: Received parameter data in band\n",
+ __func__);
+ ret = adm_process_get_param_response(
+ data->opcode, idx, payload,
+ data->payload_size);
+ if (ret)
+ pr_err("%s: Failed to process get param response, error %d\n",
+ __func__, ret);
+ } else if (payload[0] == 0 && data->payload_size == 0) {
+ adm_get_parameters[idx] = -1;
+ pr_debug("%s: Out of band case, setting size to %d\n",
+ __func__, adm_get_parameters[idx]);
+ } else {
+ adm_get_parameters[idx] = -1;
+ pr_err("%s: ADM_CMDRSP_GET_PP_PARAMS returned error 0x%x\n",
+ __func__, payload[0]);
+ }
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx],
+ payload[0]);
+ wake_up(&this_adm.copp.wait[port_idx][copp_idx]);
+ break;
+ case ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST:
+ case ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST_V2:
+ pr_debug("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST\n",
+ __func__);
+ if (data->payload_size >= (2 * sizeof(uint32_t))) {
+ num_modules = payload[1];
+ pr_debug("%s: Num modules %d\n", __func__,
+ num_modules);
+ if (payload[0]) {
+ pr_err("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST, error = %d\n",
+ __func__, payload[0]);
+ } else if (num_modules > MAX_MODULES_IN_TOPO) {
+ pr_err("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST invalid num modules received, num modules = %d\n",
+ __func__, num_modules);
+ } else {
+ ret = adm_process_get_topo_list_response
+ (data->opcode, copp_idx,
+ num_modules, payload,
+ data->payload_size);
+ if (ret)
+ pr_err("%s: Failed to process get topo modules list response, error %d\n",
+ __func__, ret);
+ }
+ } else {
+ pr_err("%s: Invalid payload size %d\n",
+ __func__, data->payload_size);
+ }
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx],
+ payload[0]);
+ wake_up(&this_adm.copp.wait[port_idx][copp_idx]);
+ break;
+ case ADM_CMDRSP_SHARED_MEM_MAP_REGIONS:
+ pr_debug("%s: ADM_CMDRSP_SHARED_MEM_MAP_REGIONS\n",
+ __func__);
+ atomic_set(&this_adm.mem_map_handles[
+ atomic_read(&this_adm.mem_map_index)],
+ *payload);
+ atomic_set(&this_adm.adm_stat, 0);
+ wake_up(&this_adm.adm_wait);
+ break;
+ default:
+ pr_err("%s: Unknown cmd:0x%x\n", __func__,
+ data->opcode);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int adm_memory_map_regions(phys_addr_t *buf_add, uint32_t mempool_id,
+ uint32_t *bufsz, uint32_t bufcnt)
+{
+ struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+ struct avs_shared_map_region_payload *mregions = NULL;
+ void *mmap_region_cmd = NULL;
+ void *payload = NULL;
+ int ret = 0;
+ int i = 0;
+ int cmd_size = 0;
+
+ pr_debug("%s:\n", __func__);
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_adm_handle(this_adm.apr);
+ }
+
+ cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions)
+ + sizeof(struct avs_shared_map_region_payload)
+ * bufcnt;
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (!mmap_region_cmd) {
+ pr_err("%s: allocate mmap_region_cmd failed\n", __func__);
+ return -ENOMEM;
+ }
+ mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
+ mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mmap_regions->hdr.pkt_size = cmd_size;
+ mmap_regions->hdr.src_port = 0;
+
+ mmap_regions->hdr.dest_port = 0;
+ mmap_regions->hdr.token = 0;
+ mmap_regions->hdr.opcode = ADM_CMD_SHARED_MEM_MAP_REGIONS;
+ mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL & 0x00ff;
+ mmap_regions->num_regions = bufcnt & 0x00ff;
+ mmap_regions->property_flag = 0x00;
+
+ pr_debug("%s: map_regions->num_regions = %d\n", __func__,
+ mmap_regions->num_regions);
+ payload = ((u8 *) mmap_region_cmd +
+ sizeof(struct avs_cmd_shared_mem_map_regions));
+ mregions = (struct avs_shared_map_region_payload *)payload;
+
+ for (i = 0; i < bufcnt; i++) {
+ mregions->shm_addr_lsw = lower_32_bits(buf_add[i]);
+ mregions->shm_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_add[i]);
+ mregions->mem_size_bytes = bufsz[i];
+ ++mregions;
+ }
+
+ atomic_set(&this_adm.adm_stat, -1);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *) mmap_region_cmd);
+ if (ret < 0) {
+ pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
+ mmap_regions->hdr.opcode, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_adm.adm_wait,
+ atomic_read(&this_adm.adm_stat) >= 0,
+ 5 * HZ);
+ if (!ret) {
+ pr_err("%s: timeout. waited for memory_map\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ } else if (atomic_read(&this_adm.adm_stat) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&this_adm.adm_stat)));
+ ret = adsp_err_get_lnx_err_code(
+ atomic_read(&this_adm.adm_stat));
+ goto fail_cmd;
+ }
+fail_cmd:
+ kfree(mmap_region_cmd);
+ return ret;
+}
+
+static int adm_memory_unmap_regions(void)
+{
+ struct avs_cmd_shared_mem_unmap_regions unmap_regions;
+ int ret = 0;
+
+ pr_debug("%s:\n", __func__);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ unmap_regions.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ unmap_regions.hdr.pkt_size = sizeof(unmap_regions);
+ unmap_regions.hdr.src_port = 0;
+ unmap_regions.hdr.dest_port = 0;
+ unmap_regions.hdr.token = 0;
+ unmap_regions.hdr.opcode = ADM_CMD_SHARED_MEM_UNMAP_REGIONS;
+ unmap_regions.mem_map_handle = atomic_read(&this_adm.
+ mem_map_handles[atomic_read(&this_adm.mem_map_index)]);
+ atomic_set(&this_adm.adm_stat, -1);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *) &unmap_regions);
+ if (ret < 0) {
+ pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
+ unmap_regions.hdr.opcode, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_adm.adm_wait,
+ atomic_read(&this_adm.adm_stat) >= 0,
+ 5 * HZ);
+ if (!ret) {
+ pr_err("%s: timeout. waited for memory_unmap\n",
+ __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ } else if (atomic_read(&this_adm.adm_stat) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&this_adm.adm_stat)));
+ ret = adsp_err_get_lnx_err_code(
+ atomic_read(&this_adm.adm_stat));
+ goto fail_cmd;
+ } else {
+ pr_debug("%s: Unmap handle 0x%x succeeded\n", __func__,
+ unmap_regions.mem_map_handle);
+ }
+fail_cmd:
+ return ret;
+}
+
+static int remap_cal_data(struct cal_block_data *cal_block, int cal_index)
+{
+ int ret = 0;
+
+ if (cal_block->map_data.ion_client == NULL) {
+ pr_err("%s: No ION allocation for cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((cal_block->map_data.map_size > 0) &&
+ (cal_block->map_data.q6map_handle == 0)) {
+ atomic_set(&this_adm.mem_map_index, cal_index);
+ ret = adm_memory_map_regions(&cal_block->cal_data.paddr, 0,
+ (uint32_t *)&cal_block->map_data.map_size, 1);
+ if (ret < 0) {
+ pr_err("%s: ADM mmap did not work! size = %zd ret %d\n",
+ __func__,
+ cal_block->map_data.map_size, ret);
+ pr_debug("%s: ADM mmap did not work! addr = 0x%pK, size = %zd ret %d\n",
+ __func__,
+ &cal_block->cal_data.paddr,
+ cal_block->map_data.map_size, ret);
+ goto done;
+ }
+ cal_block->map_data.q6map_handle = atomic_read(&this_adm.
+ mem_map_handles[cal_index]);
+ }
+done:
+ return ret;
+}
+
+static void send_adm_custom_topology(void)
+{
+ struct cal_block_data *cal_block = NULL;
+ struct cmd_set_topologies adm_top;
+ int cal_index = ADM_CUSTOM_TOP_CAL;
+ int result;
+
+ if (this_adm.cal_data[cal_index] == NULL)
+ goto done;
+
+ mutex_lock(&this_adm.cal_data[cal_index]->lock);
+ if (!this_adm.set_custom_topology)
+ goto unlock;
+ this_adm.set_custom_topology = 0;
+
+ cal_block = cal_utils_get_only_cal_block(this_adm.cal_data[cal_index]);
+ if (cal_block == NULL)
+ goto unlock;
+
+ pr_debug("%s: Sending cal_index %d\n", __func__, cal_index);
+
+ result = remap_cal_data(cal_block, cal_index);
+ if (result) {
+ pr_err("%s: Remap_cal_data failed for cal %d!\n",
+ __func__, cal_index);
+ goto unlock;
+ }
+ atomic_set(&this_adm.mem_map_index, cal_index);
+ atomic_set(&this_adm.mem_map_handles[cal_index],
+ cal_block->map_data.q6map_handle);
+
+ if (cal_block->cal_data.size == 0) {
+ pr_debug("%s: No ADM cal to send\n", __func__);
+ goto unlock;
+ }
+
+ adm_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ adm_top.hdr.pkt_size = sizeof(adm_top);
+ adm_top.hdr.src_svc = APR_SVC_ADM;
+ adm_top.hdr.src_domain = APR_DOMAIN_APPS;
+ adm_top.hdr.src_port = 0;
+ adm_top.hdr.dest_svc = APR_SVC_ADM;
+ adm_top.hdr.dest_domain = APR_DOMAIN_ADSP;
+ adm_top.hdr.dest_port = 0;
+ adm_top.hdr.token = 0;
+ adm_top.hdr.opcode = ADM_CMD_ADD_TOPOLOGIES;
+ adm_top.payload_addr_lsw = lower_32_bits(cal_block->cal_data.paddr);
+ adm_top.payload_addr_msw = msm_audio_populate_upper_32_bits(
+ cal_block->cal_data.paddr);
+ adm_top.mem_map_handle = cal_block->map_data.q6map_handle;
+ adm_top.payload_size = cal_block->cal_data.size;
+
+ atomic_set(&this_adm.adm_stat, -1);
+ pr_debug("%s: Sending ADM_CMD_ADD_TOPOLOGIES payload = 0x%pK, size = %d\n",
+ __func__, &cal_block->cal_data.paddr,
+ adm_top.payload_size);
+ result = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_top);
+ if (result < 0) {
+ pr_err("%s: Set topologies failed payload size = 0x%zd result %d\n",
+ __func__, cal_block->cal_data.size, result);
+ goto unlock;
+ }
+ /* Wait for the callback */
+ result = wait_event_timeout(this_adm.adm_wait,
+ atomic_read(&this_adm.adm_stat) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!result) {
+ pr_err("%s: Set topologies timed out payload size = 0x%zd\n",
+ __func__, cal_block->cal_data.size);
+ goto unlock;
+ } else if (atomic_read(&this_adm.adm_stat) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&this_adm.adm_stat)));
+ result = adsp_err_get_lnx_err_code(
+ atomic_read(&this_adm.adm_stat));
+ goto unlock;
+ }
+unlock:
+ mutex_unlock(&this_adm.cal_data[cal_index]->lock);
+done:
+ return;
+}
+
+static int send_adm_cal_block(int port_id, int copp_idx,
+ struct cal_block_data *cal_block, int perf_mode)
+{
+ struct mem_mapping_hdr mem_hdr = {0};
+ int payload_size = 0;
+ int port_idx = 0;
+ int topology;
+ int result = 0;
+
+ pr_debug("%s: Port id 0x%x,\n", __func__, port_id);
+
+ if (!cal_block) {
+ pr_debug("%s: No ADM cal to send for port_id = 0x%x!\n",
+ __func__, port_id);
+ result = -EINVAL;
+ goto done;
+ }
+ if (cal_block->cal_data.size <= 0) {
+ pr_debug("%s: No ADM cal sent for port_id = 0x%x!\n", __func__,
+ port_id);
+ result = -EINVAL;
+ goto done;
+ }
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ } else if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_idx 0x%x\n", __func__, copp_idx);
+ return -EINVAL;
+ }
+
+ topology = atomic_read(&this_adm.copp.topology[port_idx][copp_idx]);
+ if (perf_mode == LEGACY_PCM_MODE &&
+ topology == DS2_ADM_COPP_TOPOLOGY_ID) {
+ pr_err("%s: perf_mode %d, topology 0x%x\n", __func__, perf_mode,
+ topology);
+ goto done;
+ }
+
+ mem_hdr.data_payload_addr_lsw =
+ lower_32_bits(cal_block->cal_data.paddr);
+ mem_hdr.data_payload_addr_msw =
+ msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+ mem_hdr.mem_map_handle = cal_block->map_data.q6map_handle;
+ payload_size = cal_block->cal_data.size;
+
+ adm_set_pp_params(port_id, copp_idx, &mem_hdr, NULL, payload_size);
+
+done:
+ return result;
+}
+
+static struct cal_block_data *adm_find_cal_by_path(int cal_index, int path)
+{
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block = NULL;
+ struct audio_cal_info_audproc *audproc_cal_info = NULL;
+ struct audio_cal_info_audvol *audvol_cal_info = NULL;
+ pr_debug("%s:\n", __func__);
+
+ list_for_each_safe(ptr, next,
+ &this_adm.cal_data[cal_index]->cal_blocks) {
+
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+
+ if (cal_index == ADM_AUDPROC_CAL) {
+ audproc_cal_info = cal_block->cal_info;
+ if (audproc_cal_info->path == path)
+ return cal_block;
+ } else if (cal_index == ADM_AUDVOL_CAL) {
+ audvol_cal_info = cal_block->cal_info;
+ if (audvol_cal_info->path == path)
+ return cal_block;
+ }
+ }
+ pr_debug("%s: Can't find ADM cal for cal_index %d, path %d\n",
+ __func__, cal_index, path);
+ return NULL;
+}
+
+static struct cal_block_data *adm_find_cal_by_app_type(int cal_index, int path,
+ int app_type)
+{
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block = NULL;
+ struct audio_cal_info_audproc *audproc_cal_info = NULL;
+ struct audio_cal_info_audvol *audvol_cal_info = NULL;
+ pr_debug("%s\n", __func__);
+
+ list_for_each_safe(ptr, next,
+ &this_adm.cal_data[cal_index]->cal_blocks) {
+
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+
+ if (cal_index == ADM_AUDPROC_CAL) {
+ audproc_cal_info = cal_block->cal_info;
+ if ((audproc_cal_info->path == path) &&
+ (audproc_cal_info->app_type == app_type))
+ return cal_block;
+ } else if (cal_index == ADM_AUDVOL_CAL) {
+ audvol_cal_info = cal_block->cal_info;
+ if ((audvol_cal_info->path == path) &&
+ (audvol_cal_info->app_type == app_type))
+ return cal_block;
+ }
+ }
+ pr_debug("%s: Can't find ADM cali for cal_index %d, path %d, app %d, defaulting to search by path\n",
+ __func__, cal_index, path, app_type);
+ return adm_find_cal_by_path(cal_index, path);
+}
+
+
+static struct cal_block_data *adm_find_cal(int cal_index, int path,
+ int app_type, int acdb_id,
+ int sample_rate)
+{
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block = NULL;
+ struct audio_cal_info_audproc *audproc_cal_info = NULL;
+ struct audio_cal_info_audvol *audvol_cal_info = NULL;
+ pr_debug("%s:\n", __func__);
+
+ list_for_each_safe(ptr, next,
+ &this_adm.cal_data[cal_index]->cal_blocks) {
+
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+
+ if (cal_index == ADM_AUDPROC_CAL) {
+ audproc_cal_info = cal_block->cal_info;
+ if ((audproc_cal_info->path == path) &&
+ (audproc_cal_info->app_type == app_type) &&
+ (audproc_cal_info->acdb_id == acdb_id) &&
+ (audproc_cal_info->sample_rate == sample_rate))
+ return cal_block;
+ } else if (cal_index == ADM_AUDVOL_CAL) {
+ audvol_cal_info = cal_block->cal_info;
+ if ((audvol_cal_info->path == path) &&
+ (audvol_cal_info->app_type == app_type) &&
+ (audvol_cal_info->acdb_id == acdb_id))
+ return cal_block;
+ }
+ }
+ pr_debug("%s: Can't find ADM cal for cal_index %d, path %d, app %d, acdb_id %d sample_rate %d defaulting to search by app type\n",
+ __func__, cal_index, path, app_type, acdb_id, sample_rate);
+ return adm_find_cal_by_app_type(cal_index, path, app_type);
+}
+
+static int adm_remap_and_send_cal_block(int cal_index, int port_id,
+ int copp_idx, struct cal_block_data *cal_block, int perf_mode,
+ int app_type, int acdb_id, int sample_rate)
+{
+ int ret = 0;
+
+ pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index);
+ ret = remap_cal_data(cal_block, cal_index);
+ if (ret) {
+ pr_err("%s: Remap_cal_data failed for cal %d!\n",
+ __func__, cal_index);
+ goto done;
+ }
+ ret = send_adm_cal_block(port_id, copp_idx, cal_block, perf_mode);
+ if (ret < 0)
+ pr_debug("%s: No cal sent for cal_index %d, port_id = 0x%x! ret %d sample_rate %d\n",
+ __func__, cal_index, port_id, ret, sample_rate);
+done:
+ return ret;
+}
+
+static void send_adm_cal_type(int cal_index, int path, int port_id,
+ int copp_idx, int perf_mode, int app_type,
+ int acdb_id, int sample_rate)
+{
+ struct cal_block_data *cal_block = NULL;
+ int ret;
+
+ pr_debug("%s: cal index %d\n", __func__, cal_index);
+
+ if (this_adm.cal_data[cal_index] == NULL) {
+ pr_debug("%s: cal_index %d not allocated!\n",
+ __func__, cal_index);
+ goto done;
+ }
+
+ mutex_lock(&this_adm.cal_data[cal_index]->lock);
+ cal_block = adm_find_cal(cal_index, path, app_type, acdb_id,
+ sample_rate);
+ if (cal_block == NULL)
+ goto unlock;
+
+ ret = adm_remap_and_send_cal_block(cal_index, port_id, copp_idx,
+ cal_block, perf_mode, app_type, acdb_id, sample_rate);
+unlock:
+ mutex_unlock(&this_adm.cal_data[cal_index]->lock);
+done:
+ return;
+}
+
+static int get_cal_path(int path)
+{
+ if (path == 0x1)
+ return RX_DEVICE;
+ else
+ return TX_DEVICE;
+}
+
+static void send_adm_cal(int port_id, int copp_idx, int path, int perf_mode,
+ int app_type, int acdb_id, int sample_rate)
+{
+ pr_debug("%s: port id 0x%x copp_idx %d\n", __func__, port_id, copp_idx);
+
+ send_adm_cal_type(ADM_AUDPROC_CAL, path, port_id, copp_idx, perf_mode,
+ app_type, acdb_id, sample_rate);
+ send_adm_cal_type(ADM_AUDVOL_CAL, path, port_id, copp_idx, perf_mode,
+ app_type, acdb_id, sample_rate);
+ return;
+}
+
+int adm_connect_afe_port(int mode, int session_id, int port_id)
+{
+ struct adm_cmd_connect_afe_port_v5 cmd;
+ int ret = 0;
+ int port_idx, copp_idx = 0;
+
+ pr_debug("%s: port_id: 0x%x session id:%d mode:%d\n", __func__,
+ port_id, session_id, mode);
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ }
+
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_adm_handle(this_adm.apr);
+ }
+ pr_debug("%s: Port ID 0x%x, index %d\n", __func__, port_id, port_idx);
+
+ cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cmd.hdr.pkt_size = sizeof(cmd);
+ cmd.hdr.src_svc = APR_SVC_ADM;
+ cmd.hdr.src_domain = APR_DOMAIN_APPS;
+ cmd.hdr.src_port = port_id;
+ cmd.hdr.dest_svc = APR_SVC_ADM;
+ cmd.hdr.dest_domain = APR_DOMAIN_ADSP;
+ cmd.hdr.dest_port = 0; /* Ignored */
+ cmd.hdr.token = port_idx << 16 | copp_idx;
+ cmd.hdr.opcode = ADM_CMD_CONNECT_AFE_PORT_V5;
+
+ cmd.mode = mode;
+ cmd.session_id = session_id;
+ cmd.afe_port_id = port_id;
+
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&cmd);
+ if (ret < 0) {
+ pr_err("%s: ADM enable for port_id: 0x%x failed ret %d\n",
+ __func__, port_id, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+ atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: ADM connect timedout for port_id: 0x%x\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ } else if (atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx])));
+ ret = adsp_err_get_lnx_err_code(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]));
+ goto fail_cmd;
+ }
+ atomic_inc(&this_adm.copp.cnt[port_idx][copp_idx]);
+ return 0;
+
+fail_cmd:
+
+ return ret;
+}
+
+int adm_arrange_mch_map(struct adm_cmd_device_open_v5 *open, int path,
+ int channel_mode)
+{
+ int rc = 0, idx;
+ memset(open->dev_channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+ switch (path) {
+ case ADM_PATH_PLAYBACK:
+ idx = ADM_MCH_MAP_IDX_PLAYBACK;
+ break;
+ case ADM_PATH_LIVE_REC:
+ case ADM_PATH_NONLIVE_REC:
+ idx = ADM_MCH_MAP_IDX_REC;
+ break;
+ default:
+ goto non_mch_path;
+ };
+ if ((open->dev_num_channel > 2) && multi_ch_maps[idx].set_channel_map) {
+ memcpy(open->dev_channel_mapping,
+ multi_ch_maps[idx].channel_mapping,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ } else {
+ if (channel_mode == 1) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 2) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channel_mode == 3) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 4) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_LS;
+ open->dev_channel_mapping[3] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 5) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ open->dev_channel_mapping[3] = PCM_CHANNEL_LS;
+ open->dev_channel_mapping[4] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 6) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ open->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ open->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ open->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 7) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ open->dev_channel_mapping[3] = PCM_CHANNEL_LFE;
+ open->dev_channel_mapping[4] = PCM_CHANNEL_LB;
+ open->dev_channel_mapping[5] = PCM_CHANNEL_RB;
+ open->dev_channel_mapping[6] = PCM_CHANNEL_CS;
+ } else if (channel_mode == 8) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ open->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ open->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ open->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ open->dev_channel_mapping[6] = PCM_CHANNEL_LB;
+ open->dev_channel_mapping[7] = PCM_CHANNEL_RB;
+ } else {
+ pr_err("%s: invalid num_chan %d\n", __func__,
+ channel_mode);
+ rc = -EINVAL;
+ goto inval_ch_mod;
+ }
+ }
+
+non_mch_path:
+inval_ch_mod:
+ return rc;
+}
+
+int adm_arrange_mch_ep2_map(struct adm_cmd_device_open_v6 *open_v6,
+ int channel_mode)
+{
+ int rc = 0;
+
+ memset(open_v6->dev_channel_mapping_eid2, 0,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (channel_mode == 1) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 2) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ } else if (channel_mode == 3) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 4) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LS;
+ open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 5) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_FC;
+ open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_LS;
+ open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 6) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LFE;
+ open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_FC;
+ open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_LS;
+ open_v6->dev_channel_mapping_eid2[5] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 8) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LFE;
+ open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_FC;
+ open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_LS;
+ open_v6->dev_channel_mapping_eid2[5] = PCM_CHANNEL_RS;
+ open_v6->dev_channel_mapping_eid2[6] = PCM_CHANNEL_LB;
+ open_v6->dev_channel_mapping_eid2[7] = PCM_CHANNEL_RB;
+ } else {
+ pr_err("%s: invalid num_chan %d\n", __func__,
+ channel_mode);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int adm_arrange_mch_map_v8(
+ struct adm_device_endpoint_payload *ep_payload,
+ int path,
+ int channel_mode)
+{
+ int rc = 0, idx;
+
+ memset(ep_payload->dev_channel_mapping,
+ 0, PCM_FORMAT_MAX_NUM_CHANNEL_V2);
+ switch (path) {
+ case ADM_PATH_PLAYBACK:
+ idx = ADM_MCH_MAP_IDX_PLAYBACK;
+ break;
+ case ADM_PATH_LIVE_REC:
+ case ADM_PATH_NONLIVE_REC:
+ idx = ADM_MCH_MAP_IDX_REC;
+ break;
+ default:
+ goto non_mch_path;
+ };
+
+ if ((ep_payload->dev_num_channel > 2) &&
+ multi_ch_maps[idx].set_channel_map) {
+ memcpy(ep_payload->dev_channel_mapping,
+ multi_ch_maps[idx].channel_mapping,
+ PCM_FORMAT_MAX_NUM_CHANNEL_V2);
+ } else {
+ if (channel_mode == 1) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 2) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channel_mode == 3) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 4) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 5) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 6) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_LFE;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 7) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_LFE;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_CS;
+ } else if (channel_mode == 8) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_LFE;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LB;
+ ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RB;
+ ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 10) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB;
+ ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB;
+ ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_CS;
+ ep_payload->dev_channel_mapping[9] = PCM_CHANNELS;
+ } else if (channel_mode == 16) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB;
+ ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB;
+ ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_CS;
+ ep_payload->dev_channel_mapping[9] = PCM_CHANNELS;
+ ep_payload->dev_channel_mapping[10] = PCM_CHANNEL_CVH;
+ ep_payload->dev_channel_mapping[11] = PCM_CHANNEL_MS;
+ ep_payload->dev_channel_mapping[12] = PCM_CHANNEL_FLC;
+ ep_payload->dev_channel_mapping[13] = PCM_CHANNEL_FRC;
+ ep_payload->dev_channel_mapping[14] = PCM_CHANNEL_RLC;
+ ep_payload->dev_channel_mapping[15] = PCM_CHANNEL_RRC;
+ } else if (channel_mode == 32) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB;
+ ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB;
+ ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_CS;
+ ep_payload->dev_channel_mapping[9] = PCM_CHANNELS;
+ ep_payload->dev_channel_mapping[10] = PCM_CHANNEL_CVH;
+ ep_payload->dev_channel_mapping[11] = PCM_CHANNEL_MS;
+ ep_payload->dev_channel_mapping[12] = PCM_CHANNEL_FLC;
+ ep_payload->dev_channel_mapping[13] = PCM_CHANNEL_FRC;
+ ep_payload->dev_channel_mapping[14] = PCM_CHANNEL_RLC;
+ ep_payload->dev_channel_mapping[15] = PCM_CHANNEL_RRC;
+ ep_payload->dev_channel_mapping[16] = PCM_CHANNEL_LFE2;
+ ep_payload->dev_channel_mapping[17] = PCM_CHANNEL_SL;
+ ep_payload->dev_channel_mapping[18] = PCM_CHANNEL_SR;
+ ep_payload->dev_channel_mapping[19] = PCM_CHANNEL_TFL;
+ ep_payload->dev_channel_mapping[20] = PCM_CHANNEL_TFR;
+ ep_payload->dev_channel_mapping[21] = PCM_CHANNEL_TC;
+ ep_payload->dev_channel_mapping[22] = PCM_CHANNEL_TBL;
+ ep_payload->dev_channel_mapping[23] = PCM_CHANNEL_TBR;
+ ep_payload->dev_channel_mapping[24] = PCM_CHANNEL_TSL;
+ ep_payload->dev_channel_mapping[25] = PCM_CHANNEL_TSR;
+ ep_payload->dev_channel_mapping[26] = PCM_CHANNEL_TBC;
+ ep_payload->dev_channel_mapping[27] = PCM_CHANNEL_BFC;
+ ep_payload->dev_channel_mapping[28] = PCM_CHANNEL_BFL;
+ ep_payload->dev_channel_mapping[29] = PCM_CHANNEL_BFR;
+ ep_payload->dev_channel_mapping[30] = PCM_CHANNEL_LW;
+ ep_payload->dev_channel_mapping[31] = PCM_CHANNEL_RW;
+ } else {
+ pr_err("%s: invalid num_chan %d\n", __func__,
+ channel_mode);
+ rc = -EINVAL;
+ goto inval_ch_mod;
+ }
+ }
+
+non_mch_path:
+inval_ch_mod:
+ return rc;
+}
+
+static int adm_arrange_mch_ep2_map_v8(
+ struct adm_device_endpoint_payload *ep_payload,
+ int channel_mode)
+{
+ int rc = 0;
+
+ memset(ep_payload->dev_channel_mapping, 0,
+ PCM_FORMAT_MAX_NUM_CHANNEL_V2);
+
+ if (channel_mode == 1) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 2) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channel_mode == 3) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 4) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 5) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 6) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 8) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB;
+ ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB;
+ } else if (channel_mode == 10) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB;
+ ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB;
+ ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_CS;
+ ep_payload->dev_channel_mapping[9] = PCM_CHANNELS;
+ } else if (channel_mode == 16) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB;
+ ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB;
+ ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_CS;
+ ep_payload->dev_channel_mapping[9] = PCM_CHANNELS;
+ ep_payload->dev_channel_mapping[10] = PCM_CHANNEL_CVH;
+ ep_payload->dev_channel_mapping[11] = PCM_CHANNEL_MS;
+ ep_payload->dev_channel_mapping[12] = PCM_CHANNEL_FLC;
+ ep_payload->dev_channel_mapping[13] = PCM_CHANNEL_FRC;
+ ep_payload->dev_channel_mapping[14] = PCM_CHANNEL_RLC;
+ ep_payload->dev_channel_mapping[15] = PCM_CHANNEL_RRC;
+ } else if (channel_mode == 32) {
+ ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB;
+ ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB;
+ ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_CS;
+ ep_payload->dev_channel_mapping[9] = PCM_CHANNELS;
+ ep_payload->dev_channel_mapping[10] = PCM_CHANNEL_CVH;
+ ep_payload->dev_channel_mapping[11] = PCM_CHANNEL_MS;
+ ep_payload->dev_channel_mapping[12] = PCM_CHANNEL_FLC;
+ ep_payload->dev_channel_mapping[13] = PCM_CHANNEL_FRC;
+ ep_payload->dev_channel_mapping[14] = PCM_CHANNEL_RLC;
+ ep_payload->dev_channel_mapping[15] = PCM_CHANNEL_RRC;
+ ep_payload->dev_channel_mapping[16] = PCM_CHANNEL_LFE2;
+ ep_payload->dev_channel_mapping[17] = PCM_CHANNEL_SL;
+ ep_payload->dev_channel_mapping[18] = PCM_CHANNEL_SR;
+ ep_payload->dev_channel_mapping[19] = PCM_CHANNEL_TFL;
+ ep_payload->dev_channel_mapping[20] = PCM_CHANNEL_TFR;
+ ep_payload->dev_channel_mapping[21] = PCM_CHANNEL_TC;
+ ep_payload->dev_channel_mapping[22] = PCM_CHANNEL_TBL;
+ ep_payload->dev_channel_mapping[23] = PCM_CHANNEL_TBR;
+ ep_payload->dev_channel_mapping[24] = PCM_CHANNEL_TSL;
+ ep_payload->dev_channel_mapping[25] = PCM_CHANNEL_TSR;
+ ep_payload->dev_channel_mapping[26] = PCM_CHANNEL_TBC;
+ ep_payload->dev_channel_mapping[27] = PCM_CHANNEL_BFC;
+ ep_payload->dev_channel_mapping[28] = PCM_CHANNEL_BFL;
+ ep_payload->dev_channel_mapping[29] = PCM_CHANNEL_BFR;
+ ep_payload->dev_channel_mapping[30] = PCM_CHANNEL_LW;
+ ep_payload->dev_channel_mapping[31] = PCM_CHANNEL_RW;
+ } else {
+ pr_err("%s: invalid num_chan %d\n", __func__,
+ channel_mode);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int adm_open_v8(int tmp_port, int port_idx, int copp_idx,
+ int flags, int path,
+ int channel_mode, uint16_t bit_width,
+ int rate, int topology,
+ int perf_mode)
+{
+ int ret = 0;
+ struct adm_cmd_device_open_v8 open_v8;
+ struct adm_device_endpoint_payload ep1_payload;
+ struct adm_device_endpoint_payload ep2_payload;
+ int ep1_payload_size = 0;
+ int ep2_payload_size = 0;
+ void *adm_params = NULL;
+ int param_size;
+
+ if (channel_mode < 0 || channel_mode > 32) {
+ pr_err("%s: Invalid channel number 0x%x\n",
+ __func__, channel_mode);
+ return -EINVAL;
+ }
+
+ memset(&open_v8, 0, sizeof(open_v8));
+ memset(&ep1_payload, 0, sizeof(ep1_payload));
+ memset(&ep2_payload, 0, sizeof(ep2_payload));
+
+ open_v8.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ open_v8.hdr.src_svc = APR_SVC_ADM;
+ open_v8.hdr.src_domain = APR_DOMAIN_APPS;
+ open_v8.hdr.src_port = tmp_port;
+ open_v8.hdr.dest_svc = APR_SVC_ADM;
+ open_v8.hdr.dest_domain = APR_DOMAIN_ADSP;
+ open_v8.hdr.dest_port = tmp_port;
+ open_v8.hdr.token = port_idx << 16 | copp_idx;
+ open_v8.hdr.opcode = ADM_CMD_DEVICE_OPEN_V8;
+
+ if (this_adm.native_mode != 0) {
+ open_v8.flags = flags |
+ (this_adm.native_mode << 11);
+ this_adm.native_mode = 0;
+ } else {
+ open_v8.flags = flags;
+ }
+
+ open_v8.mode_of_operation = path;
+ open_v8.endpoint_id_1 = tmp_port;
+ open_v8.endpoint_id_2 = 0xFFFF;
+ open_v8.endpoint_id_3 = 0xFFFF;
+
+ if (this_adm.ec_ref_rx && (path != 1)) {
+ open_v8.endpoint_id_2 = this_adm.ec_ref_rx;
+ this_adm.ec_ref_rx = -1;
+ }
+
+ open_v8.topology_id = topology;
+ open_v8.reserved = 0;
+
+ /*variable endpoint payload*/
+ ep1_payload.dev_num_channel = channel_mode & 0x00FF;
+ ep1_payload.bit_width = bit_width;
+ ep1_payload.sample_rate = rate;
+ ret = adm_arrange_mch_map_v8(&ep1_payload, path,
+ channel_mode);
+ if (ret)
+ return ret;
+
+ pr_debug("%s: port_id=0x%x %x %x topology_id=0x%X flags %x ref_ch %x\n",
+ __func__, open_v8.endpoint_id_1,
+ open_v8.endpoint_id_2,
+ open_v8.endpoint_id_3,
+ open_v8.topology_id,
+ open_v8.flags,
+ this_adm.num_ec_ref_rx_chans);
+
+ ep1_payload_size = 8 +
+ roundup(ep1_payload.dev_num_channel, 4);
+ param_size = sizeof(struct adm_cmd_device_open_v8)
+ + ep1_payload_size;
+
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+
+ if ((this_adm.num_ec_ref_rx_chans != 0) && (path != 1)
+ && (open_v8.endpoint_id_2 != 0xFFFF)) {
+ ep2_payload.dev_num_channel =
+ this_adm.num_ec_ref_rx_chans;
+ this_adm.num_ec_ref_rx_chans = 0;
+
+ if (this_adm.ec_ref_rx_bit_width != 0) {
+ ep2_payload.bit_width =
+ this_adm.ec_ref_rx_bit_width;
+ this_adm.ec_ref_rx_bit_width = 0;
+ } else {
+ ep2_payload.bit_width = bit_width;
+ }
+
+ if (this_adm.ec_ref_rx_sampling_rate != 0) {
+ ep2_payload.sample_rate =
+ this_adm.ec_ref_rx_sampling_rate;
+ this_adm.ec_ref_rx_sampling_rate = 0;
+ } else {
+ ep2_payload.sample_rate = rate;
+ }
+
+ pr_debug("%s: adm open_v8 eid2_channels=%d eid2_bit_width=%d eid2_rate=%d\n",
+ __func__,
+ ep2_payload.dev_num_channel,
+ ep2_payload.bit_width,
+ ep2_payload.sample_rate);
+
+ ret = adm_arrange_mch_ep2_map_v8(&ep2_payload,
+ ep2_payload.dev_num_channel);
+
+ if (ret)
+ return ret;
+ ep2_payload_size = 8 +
+ roundup(ep2_payload.dev_num_channel, 4);
+ param_size += ep2_payload_size;
+ }
+
+ adm_params = kzalloc(param_size, GFP_KERNEL);
+ if (!adm_params)
+ return -ENOMEM;
+ open_v8.hdr.pkt_size = param_size;
+ memcpy(adm_params, &open_v8, sizeof(open_v8));
+ memcpy(adm_params + sizeof(open_v8),
+ (void *)&ep1_payload,
+ ep1_payload_size);
+ memcpy(adm_params + sizeof(open_v8)
+ + ep1_payload_size,
+ (void *)&ep2_payload,
+ ep2_payload_size);
+
+ ret = apr_send_pkt(this_adm.apr,
+ (uint32_t *)adm_params);
+ kfree(adm_params);
+ return ret;
+}
+
+static int adm_open_v5_v6(int tmp_port, int port_idx, int copp_idx,
+ int flags, int path,
+ int channel_mode, uint16_t bit_width,
+ int rate, int topology,
+ int perf_mode)
+{
+ int ret = 0;
+ struct adm_cmd_device_open_v5 open;
+ struct adm_cmd_device_open_v6 open_v6;
+
+ memset(&open, 0, sizeof(open));
+ memset(&open_v6, 0, sizeof(open_v6));
+
+ open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ open.hdr.pkt_size = sizeof(open);
+ open.hdr.src_svc = APR_SVC_ADM;
+ open.hdr.src_domain = APR_DOMAIN_APPS;
+ open.hdr.src_port = tmp_port;
+ open.hdr.dest_svc = APR_SVC_ADM;
+ open.hdr.dest_domain = APR_DOMAIN_ADSP;
+ open.hdr.dest_port = tmp_port;
+ open.hdr.token = port_idx << 16 | copp_idx;
+ open.hdr.opcode = ADM_CMD_DEVICE_OPEN_V5;
+ open.flags = flags;
+ open.mode_of_operation = path;
+ open.endpoint_id_1 = tmp_port;
+ open.endpoint_id_2 = 0xFFFF;
+
+ if (this_adm.ec_ref_rx && (path != 1)) {
+ open.endpoint_id_2 = this_adm.ec_ref_rx;
+ this_adm.ec_ref_rx = -1;
+ }
+
+ open.topology_id = topology;
+
+ open.dev_num_channel = channel_mode & 0x00FF;
+ open.bit_width = bit_width;
+ WARN_ON((perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) &&
+ (rate != ULL_SUPPORTED_SAMPLE_RATE));
+ open.sample_rate = rate;
+
+ ret = adm_arrange_mch_map(&open, path, channel_mode);
+
+ if (ret)
+ return ret;
+
+ pr_debug("%s: port_id=0x%x rate=%d topology_id=0x%X\n",
+ __func__, open.endpoint_id_1, open.sample_rate,
+ open.topology_id);
+
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+
+ if ((this_adm.num_ec_ref_rx_chans != 0) && (path != 1) &&
+ (open.endpoint_id_2 != 0xFFFF)) {
+ memset(&open_v6, 0,
+ sizeof(struct adm_cmd_device_open_v6));
+ memcpy(&open_v6, &open,
+ sizeof(struct adm_cmd_device_open_v5));
+ open_v6.hdr.opcode = ADM_CMD_DEVICE_OPEN_V6;
+ open_v6.hdr.pkt_size = sizeof(open_v6);
+ open_v6.dev_num_channel_eid2 =
+ this_adm.num_ec_ref_rx_chans;
+ this_adm.num_ec_ref_rx_chans = 0;
+
+ if (this_adm.ec_ref_rx_bit_width != 0) {
+ open_v6.bit_width_eid2 =
+ this_adm.ec_ref_rx_bit_width;
+ this_adm.ec_ref_rx_bit_width = 0;
+ } else {
+ open_v6.bit_width_eid2 = bit_width;
+ }
+
+ if (this_adm.ec_ref_rx_sampling_rate != 0) {
+ open_v6.sample_rate_eid2 =
+ this_adm.ec_ref_rx_sampling_rate;
+ this_adm.ec_ref_rx_sampling_rate = 0;
+ } else {
+ open_v6.sample_rate_eid2 = rate;
+ }
+
+ pr_debug("%s: eid2_channels=%d eid2_bit_width=%d eid2_rate=%d\n",
+ __func__, open_v6.dev_num_channel_eid2,
+ open_v6.bit_width_eid2,
+ open_v6.sample_rate_eid2);
+
+ ret = adm_arrange_mch_ep2_map(&open_v6,
+ open_v6.dev_num_channel_eid2);
+
+ if (ret)
+ return ret;
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open_v6);
+ } else {
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
+ }
+
+ return ret;
+}
+
+int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
+ int perf_mode, uint16_t bit_width, int app_type, int acdb_id,
+ u32 copp_token)
+{
+ int ret = 0;
+ int port_idx, flags;
+ int copp_idx = -1;
+ int tmp_port = q6audio_get_port_id(port_id);
+
+ pr_debug("%s:port %#x path:%d rate:%d mode:%d perf_mode:%d,topo_id %d\n",
+ __func__, port_id, path, rate, channel_mode, perf_mode,
+ topology);
+
+ port_id = q6audio_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ }
+
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ return -ENODEV;
+ }
+ rtac_set_adm_handle(this_adm.apr);
+ }
+
+ if (perf_mode == ULL_POST_PROCESSING_PCM_MODE) {
+ flags = ADM_ULL_POST_PROCESSING_DEVICE_SESSION;
+ if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) ||
+ (topology == DS2_ADM_COPP_TOPOLOGY_ID) ||
+ (topology == SRS_TRUMEDIA_TOPOLOGY_ID))
+ topology = DEFAULT_COPP_TOPOLOGY;
+ } else if (perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) {
+ flags = ADM_ULTRA_LOW_LATENCY_DEVICE_SESSION;
+ topology = NULL_COPP_TOPOLOGY;
+ rate = ULL_SUPPORTED_SAMPLE_RATE;
+ bit_width = ULL_SUPPORTED_BITS_PER_SAMPLE;
+ } else if (perf_mode == LOW_LATENCY_PCM_MODE) {
+ flags = ADM_LOW_LATENCY_DEVICE_SESSION;
+ if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) ||
+ (topology == DS2_ADM_COPP_TOPOLOGY_ID) ||
+ (topology == SRS_TRUMEDIA_TOPOLOGY_ID))
+ topology = DEFAULT_COPP_TOPOLOGY;
+ } else {
+ if ((path == ADM_PATH_COMPRESSED_RX) ||
+ (path == ADM_PATH_COMPRESSED_TX))
+ flags = 0;
+ else
+ flags = ADM_LEGACY_DEVICE_SESSION;
+ }
+
+ if ((topology == VPM_TX_SM_ECNS_COPP_TOPOLOGY) ||
+ (topology == VPM_TX_DM_FLUENCE_COPP_TOPOLOGY) ||
+ (topology == VPM_TX_DM_RFECNS_COPP_TOPOLOGY))
+ rate = 16000;
+
+ /*
+ * Routing driver reuses the same adm for streams with the same
+ * app_type, sample_rate etc.
+ * This isn't allowed for ULL streams as per the DSP interface
+ */
+ if (perf_mode != ULTRA_LOW_LATENCY_PCM_MODE)
+ copp_idx = adm_get_idx_if_copp_exists(port_idx, topology,
+ perf_mode,
+ rate, bit_width,
+ app_type, copp_token);
+
+ if (copp_idx < 0) {
+ copp_idx = adm_get_next_available_copp(port_idx);
+ if (copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: exceeded copp id %d\n",
+ __func__, copp_idx);
+ return -EINVAL;
+ } else {
+ atomic_set(&this_adm.copp.cnt[port_idx][copp_idx], 0);
+ atomic_set(&this_adm.copp.topology[port_idx][copp_idx],
+ topology);
+ atomic_set(&this_adm.copp.mode[port_idx][copp_idx],
+ perf_mode);
+ atomic_set(&this_adm.copp.rate[port_idx][copp_idx],
+ rate);
+ atomic_set(&this_adm.copp.channels[port_idx][copp_idx],
+ channel_mode);
+ atomic_set(&this_adm.copp.bit_width[port_idx][copp_idx],
+ bit_width);
+ atomic_set(&this_adm.copp.app_type[port_idx][copp_idx],
+ app_type);
+ atomic_set(&this_adm.copp.acdb_id[port_idx][copp_idx],
+ acdb_id);
+ atomic_set(&this_adm.copp.token[port_idx][copp_idx],
+ copp_token);
+ set_bit(ADM_STATUS_CALIBRATION_REQUIRED,
+ (void *)&this_adm.copp.adm_status[port_idx][copp_idx]);
+ if ((path != ADM_PATH_COMPRESSED_RX) &&
+ (path != ADM_PATH_COMPRESSED_TX))
+ send_adm_custom_topology();
+ }
+ }
+
+ if (this_adm.copp.adm_delay[port_idx][copp_idx] &&
+ perf_mode == LEGACY_PCM_MODE) {
+ atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx],
+ 1);
+ this_adm.copp.adm_delay[port_idx][copp_idx] = 0;
+ wake_up(&this_adm.copp.adm_delay_wait[port_idx][copp_idx]);
+ }
+
+ /* Create a COPP if port id are not enabled */
+ if (atomic_read(&this_adm.copp.cnt[port_idx][copp_idx]) == 0) {
+ pr_debug("%s: open ADM: port_idx: %d, copp_idx: %d\n", __func__,
+ port_idx, copp_idx);
+ if ((topology == SRS_TRUMEDIA_TOPOLOGY_ID) &&
+ perf_mode == LEGACY_PCM_MODE) {
+ int res;
+ atomic_set(&this_adm.mem_map_index, ADM_SRS_TRUMEDIA);
+ msm_dts_srs_tm_ion_memmap(&this_adm.outband_memmap);
+ res = adm_memory_map_regions(&this_adm.outband_memmap.paddr, 0,
+ (uint32_t *)&this_adm.outband_memmap.size, 1);
+ if (res < 0) {
+ pr_err("%s: SRS adm_memory_map_regions failed ! addr = 0x%pK, size = %d\n",
+ __func__, (void *)this_adm.outband_memmap.paddr,
+ (uint32_t)this_adm.outband_memmap.size);
+ }
+ }
+
+ if (adm_get_svc_version(APR_SVC_ADM) >=
+ ADSP_ADM_API_VERSION_V3) {
+ ret = adm_open_v8(tmp_port, port_idx, copp_idx,
+ flags, path,
+ channel_mode, bit_width,
+ rate, topology,
+ perf_mode);
+ } else {
+ ret = adm_open_v5_v6(tmp_port, port_idx, copp_idx,
+ flags, path,
+ channel_mode, bit_width,
+ rate, topology,
+ perf_mode);
+ }
+ if (ret < 0) {
+ pr_err("%s: port_id: 0x%x for[0x%x] failed %d\n",
+ __func__, tmp_port, port_id, ret);
+ return -EINVAL;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: ADM open timedout for port_id: 0x%x for [0x%x]\n",
+ __func__, tmp_port, port_id);
+ return -EINVAL;
+ } else if (atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx])));
+ return adsp_err_get_lnx_err_code(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]));
+ }
+ }
+ atomic_inc(&this_adm.copp.cnt[port_idx][copp_idx]);
+ return copp_idx;
+}
+
+void adm_copp_mfc_cfg(int port_id, int copp_idx, int dst_sample_rate)
+{
+ struct audproc_mfc_param_media_fmt mfc_cfg = {0};
+ struct adm_cmd_device_open_v5 open;
+ struct param_hdr_v3 param_hdr = {0};
+ int port_idx;
+ int rc = 0;
+ int i = 0;
+
+ port_id = q6audio_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+ goto fail_cmd;
+ }
+
+ if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+ goto fail_cmd;
+ }
+
+ memset(&open, 0, sizeof(open));
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_MFC;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT;
+ param_hdr.param_size = sizeof(mfc_cfg);
+
+ mfc_cfg.sampling_rate = dst_sample_rate;
+ mfc_cfg.bits_per_sample =
+ atomic_read(&this_adm.copp.bit_width[port_idx][copp_idx]);
+ open.dev_num_channel = mfc_cfg.num_channels =
+ atomic_read(&this_adm.copp.channels[port_idx][copp_idx]);
+
+ rc = adm_arrange_mch_map(&open, ADM_PATH_PLAYBACK,
+ mfc_cfg.num_channels);
+ if (rc < 0) {
+ pr_err("%s: unable to get channal map\n", __func__);
+ goto fail_cmd;
+ }
+
+ for (i = 0; i < mfc_cfg.num_channels; i++)
+ mfc_cfg.channel_type[i] =
+ (uint16_t) open.dev_channel_mapping[i];
+
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+
+ pr_debug("%s: mfc config: port_idx %d copp_idx %d copp SR %d copp BW %d copp chan %d o/p SR %d\n",
+ __func__, port_idx, copp_idx,
+ atomic_read(&this_adm.copp.rate[port_idx][copp_idx]),
+ mfc_cfg.bits_per_sample, mfc_cfg.num_channels,
+ mfc_cfg.sampling_rate);
+
+ rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
+ (uint8_t *) &mfc_cfg);
+ if (rc)
+ pr_err("%s: Failed to set media format configuration data, err %d\n",
+ __func__, rc);
+
+fail_cmd:
+ return;
+}
+
+
+static int adm_set_mtmx_params_v1(int port_idx, int copp_idx,
+ int params_length, void *params)
+{
+ struct adm_cmd_set_mtmx_params_v1 *adm_params = NULL;
+ int rc = 0;
+ int sz;
+
+ sz = sizeof(*adm_params) + params_length;
+ adm_params = kzalloc(sz, GFP_KERNEL);
+ if (!adm_params)
+ return -ENOMEM;
+
+ memcpy(((u8 *)adm_params + sizeof(*adm_params)),
+ params, params_length);
+ adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ adm_params->hdr.pkt_size = sz;
+ adm_params->hdr.src_svc = APR_SVC_ADM;
+ adm_params->hdr.src_domain = APR_DOMAIN_APPS;
+ adm_params->hdr.src_port = 0;
+ adm_params->hdr.dest_svc = APR_SVC_ADM;
+ adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
+ adm_params->hdr.dest_port =
+ atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+ adm_params->hdr.token = port_idx << 16 | copp_idx;
+ adm_params->hdr.opcode = ADM_CMD_SET_MTMX_STRTR_DEV_PARAMS_V1;
+ adm_params->payload_addr_lsw = 0;
+ adm_params->payload_addr_msw = 0;
+ adm_params->mem_map_handle = 0;
+ adm_params->payload_size = params_length;
+ adm_params->copp_id = atomic_read(&this_adm.copp.
+ id[port_idx][copp_idx]);
+
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+ rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params);
+ if (rc < 0) {
+ pr_err("%s: Set params failed port_idx = 0x%x rc %d\n",
+ __func__, port_idx, rc);
+ rc = -EINVAL;
+ goto send_param_return;
+ }
+ /* Wait for the callback */
+ rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+ atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!rc) {
+ pr_err("%s: Set params timed out port_idx = 0x%x\n",
+ __func__, port_idx);
+ rc = -EINVAL;
+ goto send_param_return;
+ } else if (atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx])));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]));
+ goto send_param_return;
+ }
+ rc = 0;
+send_param_return:
+ kfree(adm_params);
+ return rc;
+}
+
+static void adm_enable_mtmx_limiter(int port_idx, int copp_idx)
+{
+ int rc;
+ struct enable_param_v6 adm_param = { {0} };
+
+ adm_param.param.module_id = ADM_MTMX_MODULE_STREAM_LIMITER;
+ adm_param.param.param_id = AUDPROC_PARAM_ID_ENABLE;
+ adm_param.param.param_size = sizeof(adm_param.enable);
+ adm_param.enable = 1;
+
+ rc = adm_set_mtmx_params_v1(port_idx, copp_idx,
+ sizeof(adm_param), &adm_param);
+ if (rc < 0) {
+ pr_err("%s: adm_set_mtmx_params_v1 failed port_idx = 0x%x rc %d\n",
+ __func__, port_idx, rc);
+ goto done;
+ }
+ set_bit(ADM_STATUS_LIMITER,
+ (void *)&this_adm.copp.adm_status[port_idx][copp_idx]);
+done:
+ return;
+}
+
+static void route_set_opcode_matrix_id(
+ struct adm_cmd_matrix_map_routings_v5 **route_addr,
+ int path, uint32_t passthr_mode)
+{
+ struct adm_cmd_matrix_map_routings_v5 *route = *route_addr;
+
+ switch (path) {
+ case ADM_PATH_PLAYBACK:
+ route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
+ route->matrix_id = ADM_MATRIX_ID_AUDIO_RX;
+ break;
+ case ADM_PATH_LIVE_REC:
+ if (passthr_mode == LISTEN) {
+ route->hdr.opcode =
+ ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5;
+ route->matrix_id = ADM_MATRIX_ID_LISTEN_TX;
+ break;
+ }
+ /* fall through to set matrix id for non-listen case */
+ case ADM_PATH_NONLIVE_REC:
+ route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
+ route->matrix_id = ADM_MATRIX_ID_AUDIO_TX;
+ break;
+ case ADM_PATH_COMPRESSED_RX:
+ route->hdr.opcode = ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5;
+ route->matrix_id = ADM_MATRIX_ID_COMPRESSED_AUDIO_RX;
+ break;
+ case ADM_PATH_COMPRESSED_TX:
+ route->hdr.opcode = ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5;
+ route->matrix_id = ADM_MATRIX_ID_COMPRESSED_AUDIO_TX;
+ break;
+ default:
+ pr_err("%s: Wrong path set[%d]\n", __func__, path);
+ break;
+ }
+ pr_debug("%s: opcode 0x%x, matrix id %d\n",
+ __func__, route->hdr.opcode, route->matrix_id);
+}
+
+int adm_matrix_map(int path, struct route_payload payload_map, int perf_mode,
+ uint32_t passthr_mode)
+{
+ struct adm_cmd_matrix_map_routings_v5 *route;
+ struct adm_session_map_node_v5 *node;
+ uint16_t *copps_list;
+ int cmd_size = 0;
+ int ret = 0, i = 0;
+ void *payload = NULL;
+ void *matrix_map = NULL;
+ int port_idx, copp_idx;
+
+ /* Assumes port_ids have already been validated during adm_open */
+ cmd_size = (sizeof(struct adm_cmd_matrix_map_routings_v5) +
+ sizeof(struct adm_session_map_node_v5) +
+ (sizeof(uint32_t) * payload_map.num_copps));
+ matrix_map = kzalloc(cmd_size, GFP_KERNEL);
+ if (matrix_map == NULL) {
+ pr_err("%s: Mem alloc failed\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+ route = (struct adm_cmd_matrix_map_routings_v5 *)matrix_map;
+
+ route->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ route->hdr.pkt_size = cmd_size;
+ route->hdr.src_svc = 0;
+ route->hdr.src_domain = APR_DOMAIN_APPS;
+ route->hdr.src_port = 0; /* Ignored */;
+ route->hdr.dest_svc = APR_SVC_ADM;
+ route->hdr.dest_domain = APR_DOMAIN_ADSP;
+ route->hdr.dest_port = 0; /* Ignored */;
+ route->hdr.token = 0;
+ route->num_sessions = 1;
+ route_set_opcode_matrix_id(&route, path, passthr_mode);
+
+ payload = ((u8 *)matrix_map +
+ sizeof(struct adm_cmd_matrix_map_routings_v5));
+ node = (struct adm_session_map_node_v5 *)payload;
+
+ node->session_id = payload_map.session_id;
+ node->num_copps = payload_map.num_copps;
+ payload = (u8 *)node + sizeof(struct adm_session_map_node_v5);
+ copps_list = (uint16_t *)payload;
+ for (i = 0; i < payload_map.num_copps; i++) {
+ port_idx =
+ adm_validate_and_get_port_index(payload_map.port_id[i]);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__,
+ payload_map.port_id[i]);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ copp_idx = payload_map.copp_idx[i];
+ copps_list[i] = atomic_read(&this_adm.copp.id[port_idx]
+ [copp_idx]);
+ if (test_bit(ADM_STATUS_LIMITER,
+ (void *)&payload_map.route_status) &&
+ ((path == ADM_PATH_PLAYBACK) ||
+ (path == ADM_PATH_COMPRESSED_RX)))
+ adm_enable_mtmx_limiter(port_idx, copp_idx);
+ }
+ atomic_set(&this_adm.matrix_map_stat, -1);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)matrix_map);
+ if (ret < 0) {
+ pr_err("%s: routing for syream %d failed ret %d\n",
+ __func__, payload_map.session_id, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ ret = wait_event_timeout(this_adm.matrix_map_wait,
+ atomic_read(&this_adm.matrix_map_stat) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: routing for syream %d failed\n", __func__,
+ payload_map.session_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ } else if (atomic_read(&this_adm.matrix_map_stat) > 0) {
+ pr_err("%s: DSP returned error[%s]\n", __func__,
+ adsp_err_get_err_str(atomic_read(
+ &this_adm.matrix_map_stat)));
+ ret = adsp_err_get_lnx_err_code(
+ atomic_read(&this_adm.matrix_map_stat));
+ goto fail_cmd;
+ }
+
+ if ((perf_mode != ULTRA_LOW_LATENCY_PCM_MODE) &&
+ (path != ADM_PATH_COMPRESSED_RX)) {
+ for (i = 0; i < payload_map.num_copps; i++) {
+ port_idx = afe_get_port_index(payload_map.port_id[i]);
+ copp_idx = payload_map.copp_idx[i];
+ if (port_idx < 0 || copp_idx < 0 ||
+ (copp_idx > MAX_COPPS_PER_PORT - 1)) {
+ pr_err("%s: Invalid idx port_idx %d copp_idx %d\n",
+ __func__, port_idx, copp_idx);
+ continue;
+ }
+ rtac_add_adm_device(payload_map.port_id[i],
+ atomic_read(&this_adm.copp.id
+ [port_idx][copp_idx]),
+ get_cal_path(path),
+ payload_map.session_id,
+ payload_map.app_type[i],
+ payload_map.acdb_dev_id[i]);
+
+ if (!test_bit(ADM_STATUS_CALIBRATION_REQUIRED,
+ (void *)&this_adm.copp.adm_status[port_idx]
+ [copp_idx])) {
+ pr_debug("%s: adm copp[0x%x][%d] already sent",
+ __func__, port_idx, copp_idx);
+ continue;
+ }
+ send_adm_cal(payload_map.port_id[i], copp_idx,
+ get_cal_path(path), perf_mode,
+ payload_map.app_type[i],
+ payload_map.acdb_dev_id[i],
+ payload_map.sample_rate[i]);
+ /* ADM COPP calibration is already sent */
+ clear_bit(ADM_STATUS_CALIBRATION_REQUIRED,
+ (void *)&this_adm.copp.
+ adm_status[port_idx][copp_idx]);
+ pr_debug("%s: copp_id: %d\n", __func__,
+ atomic_read(&this_adm.copp.id[port_idx]
+ [copp_idx]));
+ }
+ }
+
+fail_cmd:
+ kfree(matrix_map);
+ return ret;
+}
+
+void adm_ec_ref_rx_id(int port_id)
+{
+ this_adm.ec_ref_rx = port_id;
+ pr_debug("%s: ec_ref_rx:%d\n", __func__, this_adm.ec_ref_rx);
+}
+
+void adm_num_ec_ref_rx_chans(int num_chans)
+{
+ this_adm.num_ec_ref_rx_chans = num_chans;
+ pr_debug("%s: num_ec_ref_rx_chans:%d\n",
+ __func__, this_adm.num_ec_ref_rx_chans);
+}
+
+void adm_ec_ref_rx_bit_width(int bit_width)
+{
+ this_adm.ec_ref_rx_bit_width = bit_width;
+ pr_debug("%s: ec_ref_rx_bit_width:%d\n",
+ __func__, this_adm.ec_ref_rx_bit_width);
+}
+
+void adm_ec_ref_rx_sampling_rate(int sampling_rate)
+{
+ this_adm.ec_ref_rx_sampling_rate = sampling_rate;
+ pr_debug("%s: ec_ref_rx_sampling_rate:%d\n",
+ __func__, this_adm.ec_ref_rx_sampling_rate);
+}
+
+void adm_set_native_mode(int mode)
+{
+ this_adm.native_mode = mode;
+ pr_debug("%s: enable native_mode :%d\n",
+ __func__, this_adm.native_mode);
+}
+
+int adm_close(int port_id, int perf_mode, int copp_idx)
+{
+ struct apr_hdr close;
+
+ int ret = 0, port_idx;
+ int copp_id = RESET_COPP_ID;
+
+ pr_debug("%s: port_id=0x%x perf_mode: %d copp_idx: %d\n", __func__,
+ port_id, perf_mode, copp_idx);
+
+ port_id = q6audio_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id 0x%x\n",
+ __func__, port_id);
+ return -EINVAL;
+ }
+
+ if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) {
+ pr_err("%s: Invalid copp idx: %d\n", __func__, copp_idx);
+ return -EINVAL;
+ }
+
+ if (this_adm.copp.adm_delay[port_idx][copp_idx] && perf_mode
+ == LEGACY_PCM_MODE) {
+ atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx],
+ 1);
+ this_adm.copp.adm_delay[port_idx][copp_idx] = 0;
+ wake_up(&this_adm.copp.adm_delay_wait[port_idx][copp_idx]);
+ }
+
+ atomic_dec(&this_adm.copp.cnt[port_idx][copp_idx]);
+ if (!(atomic_read(&this_adm.copp.cnt[port_idx][copp_idx]))) {
+ copp_id = adm_get_copp_id(port_idx, copp_idx);
+ pr_debug("%s: Closing ADM port_idx:%d copp_idx:%d copp_id:0x%x\n",
+ __func__, port_idx, copp_idx, copp_id);
+ if ((!perf_mode) && (this_adm.outband_memmap.paddr != 0) &&
+ (atomic_read(&this_adm.copp.topology[port_idx][copp_idx]) ==
+ SRS_TRUMEDIA_TOPOLOGY_ID)) {
+ atomic_set(&this_adm.mem_map_index,
+ ADM_SRS_TRUMEDIA);
+ ret = adm_memory_unmap_regions();
+ if (ret < 0) {
+ pr_err("%s: adm mem unmmap err %d",
+ __func__, ret);
+ } else {
+ atomic_set(&this_adm.mem_map_handles
+ [ADM_SRS_TRUMEDIA], 0);
+ }
+ }
+
+
+ if ((afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) &&
+ this_adm.sourceTrackingData.memmap.paddr) {
+ atomic_set(&this_adm.mem_map_index,
+ ADM_MEM_MAP_INDEX_SOURCE_TRACKING);
+ ret = adm_memory_unmap_regions();
+ if (ret < 0) {
+ pr_err("%s: adm mem unmmap err %d",
+ __func__, ret);
+ }
+ msm_audio_ion_free(
+ this_adm.sourceTrackingData.ion_client,
+ this_adm.sourceTrackingData.ion_handle);
+ this_adm.sourceTrackingData.ion_client = NULL;
+ this_adm.sourceTrackingData.ion_handle = NULL;
+ this_adm.sourceTrackingData.memmap.size = 0;
+ this_adm.sourceTrackingData.memmap.kvaddr = NULL;
+ this_adm.sourceTrackingData.memmap.paddr = 0;
+ this_adm.sourceTrackingData.apr_cmd_status = -1;
+ atomic_set(&this_adm.mem_map_handles[
+ ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0);
+ }
+
+ close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ close.pkt_size = sizeof(close);
+ close.src_svc = APR_SVC_ADM;
+ close.src_domain = APR_DOMAIN_APPS;
+ close.src_port = port_id;
+ close.dest_svc = APR_SVC_ADM;
+ close.dest_domain = APR_DOMAIN_ADSP;
+ close.dest_port = copp_id;
+ close.token = port_idx << 16 | copp_idx;
+ close.opcode = ADM_CMD_DEVICE_CLOSE_V5;
+
+ atomic_set(&this_adm.copp.id[port_idx][copp_idx],
+ RESET_COPP_ID);
+ atomic_set(&this_adm.copp.cnt[port_idx][copp_idx], 0);
+ atomic_set(&this_adm.copp.topology[port_idx][copp_idx], 0);
+ atomic_set(&this_adm.copp.mode[port_idx][copp_idx], 0);
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+ atomic_set(&this_adm.copp.rate[port_idx][copp_idx], 0);
+ atomic_set(&this_adm.copp.channels[port_idx][copp_idx], 0);
+ atomic_set(&this_adm.copp.bit_width[port_idx][copp_idx], 0);
+ atomic_set(&this_adm.copp.app_type[port_idx][copp_idx], 0);
+ atomic_set(&this_adm.copp.token[port_idx][copp_idx], 0);
+
+ clear_bit(ADM_STATUS_CALIBRATION_REQUIRED,
+ (void *)&this_adm.copp.adm_status[port_idx][copp_idx]);
+ clear_bit(ADM_STATUS_LIMITER,
+ (void *)&this_adm.copp.adm_status[port_idx][copp_idx]);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close);
+ if (ret < 0) {
+ pr_err("%s: ADM close failed %d\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: ADM cmd Route timedout for port 0x%x\n",
+ __func__, port_id);
+ return -EINVAL;
+ } else if (atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx])));
+ return adsp_err_get_lnx_err_code(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]));
+ }
+ }
+
+ if (perf_mode != ULTRA_LOW_LATENCY_PCM_MODE) {
+ pr_debug("%s: remove adm device from rtac\n", __func__);
+ rtac_remove_adm_device(port_id, copp_id);
+ }
+ return 0;
+}
+
+int send_rtac_audvol_cal(void)
+{
+ int ret = 0;
+ int ret2 = 0;
+ int i = 0;
+ int copp_idx, port_idx, acdb_id, app_id, path;
+ struct cal_block_data *cal_block = NULL;
+ struct audio_cal_info_audvol *audvol_cal_info = NULL;
+ struct rtac_adm rtac_adm_data;
+
+ mutex_lock(&this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]->lock);
+
+ cal_block = cal_utils_get_only_cal_block(
+ this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]);
+ if (cal_block == NULL) {
+ pr_err("%s: can't find cal block!\n", __func__);
+ goto unlock;
+ }
+
+ audvol_cal_info = cal_block->cal_info;
+ if (audvol_cal_info == NULL) {
+ pr_err("%s: audvol_cal_info is NULL!\n", __func__);
+ goto unlock;
+ }
+
+ get_rtac_adm_data(&rtac_adm_data);
+ for (; i < rtac_adm_data.num_of_dev; i++) {
+
+ acdb_id = rtac_adm_data.device[i].acdb_dev_id;
+ if (acdb_id == 0)
+ acdb_id = audvol_cal_info->acdb_id;
+
+ app_id = rtac_adm_data.device[i].app_type;
+ if (app_id == 0)
+ app_id = audvol_cal_info->app_type;
+
+ path = afe_get_port_type(rtac_adm_data.device[i].afe_port);
+ if ((acdb_id == audvol_cal_info->acdb_id) &&
+ (app_id == audvol_cal_info->app_type) &&
+ (path == audvol_cal_info->path)) {
+
+ if (adm_get_indexes_from_copp_id(rtac_adm_data.
+ device[i].copp, &copp_idx, &port_idx) != 0) {
+ pr_debug("%s: Copp Id %d is not active\n",
+ __func__,
+ rtac_adm_data.device[i].copp);
+ continue;
+ }
+
+ ret2 = adm_remap_and_send_cal_block(ADM_RTAC_AUDVOL_CAL,
+ rtac_adm_data.device[i].afe_port,
+ copp_idx, cal_block,
+ atomic_read(&this_adm.copp.
+ mode[port_idx][copp_idx]),
+ audvol_cal_info->app_type,
+ audvol_cal_info->acdb_id,
+ atomic_read(&this_adm.copp.
+ rate[port_idx][copp_idx]));
+ if (ret2 < 0) {
+ pr_debug("%s: remap and send failed for copp Id %d, acdb id %d, app type %d, path %d\n",
+ __func__, rtac_adm_data.device[i].copp,
+ audvol_cal_info->acdb_id,
+ audvol_cal_info->app_type,
+ audvol_cal_info->path);
+ ret = ret2;
+ }
+ }
+ }
+unlock:
+ mutex_unlock(&this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]->lock);
+ return ret;
+}
+
+int adm_map_rtac_block(struct rtac_cal_block_data *cal_block)
+{
+ int result = 0;
+ pr_debug("%s:\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("%s: cal_block is NULL!\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block->cal_data.paddr == 0) {
+ pr_debug("%s: No address to map!\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block->map_data.map_size == 0) {
+ pr_debug("%s: map size is 0!\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ /* valid port ID needed for callback use primary I2S */
+ atomic_set(&this_adm.mem_map_index, ADM_RTAC_APR_CAL);
+ result = adm_memory_map_regions(&cal_block->cal_data.paddr, 0,
+ &cal_block->map_data.map_size, 1);
+ if (result < 0) {
+ pr_err("%s: RTAC mmap did not work! size = %d result %d\n",
+ __func__,
+ cal_block->map_data.map_size, result);
+ pr_debug("%s: RTAC mmap did not work! addr = 0x%pK, size = %d\n",
+ __func__,
+ &cal_block->cal_data.paddr,
+ cal_block->map_data.map_size);
+ goto done;
+ }
+
+ cal_block->map_data.map_handle = atomic_read(
+ &this_adm.mem_map_handles[ADM_RTAC_APR_CAL]);
+done:
+ return result;
+}
+
+int adm_unmap_rtac_block(uint32_t *mem_map_handle)
+{
+ int result = 0;
+ pr_debug("%s:\n", __func__);
+
+ if (mem_map_handle == NULL) {
+ pr_debug("%s: Map handle is NULL, nothing to unmap\n",
+ __func__);
+ goto done;
+ }
+
+ if (*mem_map_handle == 0) {
+ pr_debug("%s: Map handle is 0, nothing to unmap\n",
+ __func__);
+ goto done;
+ }
+
+ if (*mem_map_handle != atomic_read(
+ &this_adm.mem_map_handles[ADM_RTAC_APR_CAL])) {
+ pr_err("%s: Map handles do not match! Unmapping RTAC, RTAC map 0x%x, ADM map 0x%x\n",
+ __func__, *mem_map_handle, atomic_read(
+ &this_adm.mem_map_handles[ADM_RTAC_APR_CAL]));
+
+ /* if mismatch use handle passed in to unmap */
+ atomic_set(&this_adm.mem_map_handles[ADM_RTAC_APR_CAL],
+ *mem_map_handle);
+ }
+
+ /* valid port ID needed for callback use primary I2S */
+ atomic_set(&this_adm.mem_map_index, ADM_RTAC_APR_CAL);
+ result = adm_memory_unmap_regions();
+ if (result < 0) {
+ pr_debug("%s: adm_memory_unmap_regions failed, error %d\n",
+ __func__, result);
+ } else {
+ atomic_set(&this_adm.mem_map_handles[ADM_RTAC_APR_CAL], 0);
+ *mem_map_handle = 0;
+ }
+done:
+ return result;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+ int ret = -EINVAL;
+
+ switch (cal_type) {
+ case ADM_AUDPROC_CAL_TYPE:
+ ret = ADM_AUDPROC_CAL;
+ break;
+ case ADM_AUDVOL_CAL_TYPE:
+ ret = ADM_AUDVOL_CAL;
+ break;
+ case ADM_CUST_TOPOLOGY_CAL_TYPE:
+ ret = ADM_CUSTOM_TOP_CAL;
+ break;
+ case ADM_RTAC_INFO_CAL_TYPE:
+ ret = ADM_RTAC_INFO_CAL;
+ break;
+ case ADM_RTAC_APR_CAL_TYPE:
+ ret = ADM_RTAC_APR_CAL;
+ break;
+ case ADM_RTAC_AUDVOL_CAL_TYPE:
+ ret = ADM_RTAC_AUDVOL_CAL;
+ break;
+ default:
+ pr_err("%s: invalid cal type %d!\n", __func__, cal_type);
+ }
+ return ret;
+}
+
+static int adm_alloc_cal(int32_t cal_type, size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_alloc_cal(data_size, data,
+ this_adm.cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int adm_dealloc_cal(int32_t cal_type, size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_dealloc_cal(data_size, data,
+ this_adm.cal_data[cal_index]);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int adm_set_cal(int32_t cal_type, size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_set_cal(data_size, data,
+ this_adm.cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (cal_index == ADM_CUSTOM_TOP_CAL) {
+ mutex_lock(&this_adm.cal_data[ADM_CUSTOM_TOP_CAL]->lock);
+ this_adm.set_custom_topology = 1;
+ mutex_unlock(&this_adm.cal_data[ADM_CUSTOM_TOP_CAL]->lock);
+ } else if (cal_index == ADM_RTAC_AUDVOL_CAL) {
+ send_rtac_audvol_cal();
+ }
+done:
+ return ret;
+}
+
+static int adm_map_cal_data(int32_t cal_type,
+ struct cal_block_data *cal_block)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ atomic_set(&this_adm.mem_map_index, cal_index);
+ ret = adm_memory_map_regions(&cal_block->cal_data.paddr, 0,
+ (uint32_t *)&cal_block->map_data.map_size, 1);
+ if (ret < 0) {
+ pr_err("%s: map did not work! cal_type %i ret %d\n",
+ __func__, cal_index, ret);
+ ret = -ENODEV;
+ goto done;
+ }
+ cal_block->map_data.q6map_handle = atomic_read(&this_adm.
+ mem_map_handles[cal_index]);
+done:
+ return ret;
+}
+
+static int adm_unmap_cal_data(int32_t cal_type,
+ struct cal_block_data *cal_block)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block == NULL) {
+ pr_err("%s: Cal block is NULL!\n",
+ __func__);
+ goto done;
+ }
+
+ if (cal_block->map_data.q6map_handle == 0) {
+ pr_err("%s: Map handle is NULL, nothing to unmap\n",
+ __func__);
+ goto done;
+ }
+
+ atomic_set(&this_adm.mem_map_handles[cal_index],
+ cal_block->map_data.q6map_handle);
+ atomic_set(&this_adm.mem_map_index, cal_index);
+ ret = adm_memory_unmap_regions();
+ if (ret < 0) {
+ pr_err("%s: unmap did not work! cal_type %i ret %d\n",
+ __func__, cal_index, ret);
+ ret = -ENODEV;
+ goto done;
+ }
+ cal_block->map_data.q6map_handle = 0;
+done:
+ return ret;
+}
+
+static void adm_delete_cal_data(void)
+{
+ pr_debug("%s:\n", __func__);
+
+ cal_utils_destroy_cal_types(ADM_MAX_CAL_TYPES, this_adm.cal_data);
+
+ return;
+}
+
+static int adm_init_cal_data(void)
+{
+ int ret = 0;
+ struct cal_type_info cal_type_info[] = {
+ {{ADM_CUST_TOPOLOGY_CAL_TYPE,
+ {adm_alloc_cal, adm_dealloc_cal, NULL,
+ adm_set_cal, NULL, NULL} },
+ {adm_map_cal_data, adm_unmap_cal_data,
+ cal_utils_match_buf_num} },
+
+ {{ADM_AUDPROC_CAL_TYPE,
+ {adm_alloc_cal, adm_dealloc_cal, NULL,
+ adm_set_cal, NULL, NULL} },
+ {adm_map_cal_data, adm_unmap_cal_data,
+ cal_utils_match_buf_num} },
+
+ {{ADM_AUDVOL_CAL_TYPE,
+ {adm_alloc_cal, adm_dealloc_cal, NULL,
+ adm_set_cal, NULL, NULL} },
+ {adm_map_cal_data, adm_unmap_cal_data,
+ cal_utils_match_buf_num} },
+
+ {{ADM_RTAC_INFO_CAL_TYPE,
+ {NULL, NULL, NULL, NULL, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{ADM_RTAC_APR_CAL_TYPE,
+ {NULL, NULL, NULL, NULL, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{SRS_TRUMEDIA_CAL_TYPE,
+ {NULL, NULL, NULL, NULL, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{ADM_RTAC_AUDVOL_CAL_TYPE,
+ {adm_alloc_cal, adm_dealloc_cal, NULL,
+ adm_set_cal, NULL, NULL} },
+ {adm_map_cal_data, adm_unmap_cal_data,
+ cal_utils_match_buf_num} },
+ };
+ pr_debug("%s:\n", __func__);
+
+ ret = cal_utils_create_cal_types(ADM_MAX_CAL_TYPES, this_adm.cal_data,
+ cal_type_info);
+ if (ret < 0) {
+ pr_err("%s: could not create cal type! ret %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return ret;
+err:
+ adm_delete_cal_data();
+ return ret;
+}
+
+int adm_set_volume(int port_id, int copp_idx, int volume)
+{
+ struct audproc_volume_ctrl_master_gain audproc_vol = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int rc = 0;
+
+ pr_debug("%s: port_id %d, volume %d\n", __func__, port_id, volume);
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_VOL_CTRL;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_VOL_CTRL_MASTER_GAIN;
+ param_hdr.param_size = sizeof(audproc_vol);
+
+ audproc_vol.master_gain = volume;
+
+ rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
+ (uint8_t *) &audproc_vol);
+ if (rc)
+ pr_err("%s: Failed to set volume, err %d\n", __func__, rc);
+
+ return rc;
+}
+
+int adm_set_softvolume(int port_id, int copp_idx,
+ struct audproc_softvolume_params *softvol_param)
+{
+ struct audproc_soft_step_volume_params audproc_softvol = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int rc = 0;
+
+ pr_debug("%s: period %d step %d curve %d\n", __func__,
+ softvol_param->period, softvol_param->step,
+ softvol_param->rampingcurve);
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_VOL_CTRL;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS;
+ param_hdr.param_size = sizeof(audproc_softvol);
+
+ audproc_softvol.period = softvol_param->period;
+ audproc_softvol.step = softvol_param->step;
+ audproc_softvol.ramping_curve = softvol_param->rampingcurve;
+
+ pr_debug("%s: period %d, step %d, curve %d\n", __func__,
+ audproc_softvol.period, audproc_softvol.step,
+ audproc_softvol.ramping_curve);
+
+ rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
+ (uint8_t *) &audproc_softvol);
+ if (rc)
+ pr_err("%s: Failed to set soft volume, err %d\n", __func__, rc);
+
+ return rc;
+}
+
+int adm_set_mic_gain(int port_id, int copp_idx, int volume)
+{
+ struct admx_mic_gain mic_gain_params = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int rc = 0;
+
+ pr_debug("%s: Setting mic gain to %d at port_id 0x%x\n", __func__,
+ volume, port_id);
+
+ param_hdr.module_id = ADM_MODULE_IDX_MIC_GAIN_CTRL;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = ADM_PARAM_IDX_MIC_GAIN;
+ param_hdr.param_size = sizeof(mic_gain_params);
+
+ mic_gain_params.tx_mic_gain = volume;
+
+ rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
+ (uint8_t *) &mic_gain_params);
+ if (rc)
+ pr_err("%s: Failed to set mic gain, err %d\n", __func__, rc);
+
+ return rc;
+}
+
+int adm_send_set_multichannel_ec_primary_mic_ch(int port_id, int copp_idx,
+ int primary_mic_ch)
+{
+ struct admx_sec_primary_mic_ch sec_primary_ch_params = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int rc = 0;
+
+ pr_debug("%s port_id 0x%x, copp_idx 0x%x, primary_mic_ch %d\n",
+ __func__, port_id, copp_idx, primary_mic_ch);
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_VOICE_TX_SECNS;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_IDX_SEC_PRIMARY_MIC_CH;
+ param_hdr.param_size = sizeof(sec_primary_ch_params);
+
+ sec_primary_ch_params.version = 0;
+ sec_primary_ch_params.sec_primary_mic_ch = primary_mic_ch;
+
+ rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
+ (uint8_t *) &sec_primary_ch_params);
+ if (rc)
+ pr_err("%s: Failed to set primary mic chanel, err %d\n",
+ __func__, rc);
+
+ return rc;
+}
+int adm_param_enable(int port_id, int copp_idx, int module_id, int enable)
+{
+ struct module_instance_info mod_inst_info = {0};
+
+ mod_inst_info.module_id = module_id;
+ mod_inst_info.instance_id = INSTANCE_ID_0;
+
+ return adm_param_enable_v2(port_id, copp_idx, mod_inst_info, enable);
+}
+
+int adm_param_enable_v2(int port_id, int copp_idx,
+ struct module_instance_info mod_inst_info, int enable)
+{
+ uint32_t enable_param;
+ struct param_hdr_v3 param_hdr = {0};
+ int rc = 0;
+
+ if (enable < 0 || enable > 1) {
+ pr_err("%s: Invalid value for enable %d\n", __func__, enable);
+ return -EINVAL;
+ }
+
+ pr_debug("%s port_id %d, module_id 0x%x, instance_id 0x%x, enable %d\n",
+ __func__, port_id, mod_inst_info.module_id,
+ mod_inst_info.instance_id, enable);
+
+ param_hdr.module_id = mod_inst_info.module_id;
+ param_hdr.instance_id = mod_inst_info.instance_id;
+ param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE;
+ param_hdr.param_size = sizeof(enable_param);
+
+ enable_param = enable;
+
+ rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
+ (uint8_t *) &enable_param);
+ if (rc)
+ pr_err("%s: Failed to set enable of module(%d) instance(%d) to %d, err %d\n",
+ __func__, mod_inst_info.module_id,
+ mod_inst_info.instance_id, enable, rc);
+
+ return rc;
+
+}
+
+/* Parameter data must be pre-packed at the specified location with its
+ * header before calling this function. Use
+ * q6common_pack_pp_params to pack parameter data and header
+ * correctly.
+ */
+int adm_send_calibration(int port_id, int copp_idx, int path, int perf_mode,
+ int cal_type, char *params, int size)
+{
+
+ int rc = 0;
+
+ pr_debug("%s:port_id %d, path %d, perf_mode %d, cal_type %d, size %d\n",
+ __func__, port_id, path, perf_mode, cal_type, size);
+
+ /* Maps audio_dev_ctrl path definition to ACDB definition */
+ if (get_cal_path(path) != RX_DEVICE) {
+ pr_err("%s: acdb_path %d\n", __func__, path);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ rc = adm_set_pp_params(port_id, copp_idx, NULL, (u8 *) params, size);
+
+end:
+ return rc;
+}
+
+/*
+ * adm_update_wait_parameters must be called with routing driver locks.
+ * adm_reset_wait_parameters must be called with routing driver locks.
+ * set and reset parmeters are seperated to make sure it is always called
+ * under routing driver lock.
+ * adm_wait_timeout is to block until timeout or interrupted. Timeout is
+ * not a an error.
+ */
+int adm_set_wait_parameters(int port_id, int copp_idx)
+{
+
+ int ret = 0, port_idx;
+ pr_debug("%s: port_id 0x%x, copp_idx %d\n", __func__, port_id,
+ copp_idx);
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+ return -EINVAL;
+ }
+
+ this_adm.copp.adm_delay[port_idx][copp_idx] = 1;
+ atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], 0);
+
+end:
+ return ret;
+
+}
+
+int adm_reset_wait_parameters(int port_id, int copp_idx)
+{
+ int ret = 0, port_idx;
+
+ pr_debug("%s: port_id 0x%x copp_idx %d\n", __func__, port_id,
+ copp_idx);
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+ return -EINVAL;
+ }
+
+ atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], 1);
+ this_adm.copp.adm_delay[port_idx][copp_idx] = 0;
+
+end:
+ return ret;
+}
+
+int adm_wait_timeout(int port_id, int copp_idx, int wait_time)
+{
+ int ret = 0, port_idx;
+
+ pr_debug("%s: port_id 0x%x, copp_idx %d, wait_time %d\n", __func__,
+ port_id, copp_idx, wait_time);
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+ return -EINVAL;
+ }
+
+ ret = wait_event_timeout(
+ this_adm.copp.adm_delay_wait[port_idx][copp_idx],
+ atomic_read(&this_adm.copp.adm_delay_stat[port_idx][copp_idx]),
+ msecs_to_jiffies(wait_time));
+ pr_debug("%s: return %d\n", __func__, ret);
+ if (ret != 0)
+ ret = -EINTR;
+end:
+ pr_debug("%s: return %d--\n", __func__, ret);
+ return ret;
+}
+
+int adm_store_cal_data(int port_id, int copp_idx, int path, int perf_mode,
+ int cal_index, char *params, int *size)
+{
+ int rc = 0;
+ struct cal_block_data *cal_block = NULL;
+ int app_type, acdb_id, port_idx, sample_rate;
+
+ if (this_adm.cal_data[cal_index] == NULL) {
+ pr_debug("%s: cal_index %d not allocated!\n",
+ __func__, cal_index);
+ goto end;
+ }
+
+ if (get_cal_path(path) != RX_DEVICE) {
+ pr_debug("%s: Invalid path to store calibration %d\n",
+ __func__, path);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+ return -EINVAL;
+ }
+
+ acdb_id = atomic_read(&this_adm.copp.acdb_id[port_idx][copp_idx]);
+ app_type = atomic_read(&this_adm.copp.app_type[port_idx][copp_idx]);
+ sample_rate = atomic_read(&this_adm.copp.rate[port_idx][copp_idx]);
+
+ mutex_lock(&this_adm.cal_data[cal_index]->lock);
+ cal_block = adm_find_cal(cal_index, get_cal_path(path), app_type,
+ acdb_id, sample_rate);
+ if (cal_block == NULL)
+ goto unlock;
+
+ if (cal_block->cal_data.size <= 0) {
+ pr_debug("%s: No ADM cal send for port_id = 0x%x!\n",
+ __func__, port_id);
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ if (cal_index == ADM_AUDPROC_CAL) {
+ if (cal_block->cal_data.size > AUD_PROC_BLOCK_SIZE) {
+ pr_err("%s:audproc:invalid size exp/actual[%zd, %d]\n",
+ __func__, cal_block->cal_data.size, *size);
+ rc = -ENOMEM;
+ goto unlock;
+ }
+ } else if (cal_index == ADM_AUDVOL_CAL) {
+ if (cal_block->cal_data.size > AUD_VOL_BLOCK_SIZE) {
+ pr_err("%s:aud_vol:invalid size exp/actual[%zd, %d]\n",
+ __func__, cal_block->cal_data.size, *size);
+ rc = -ENOMEM;
+ goto unlock;
+ }
+ } else {
+ pr_debug("%s: Not valid calibration for dolby topolgy\n",
+ __func__);
+ rc = -EINVAL;
+ goto unlock;
+ }
+ memcpy(params, cal_block->cal_data.kvaddr, cal_block->cal_data.size);
+ *size = cal_block->cal_data.size;
+
+ pr_debug("%s:port_id %d, copp_idx %d, path %d",
+ __func__, port_id, copp_idx, path);
+ pr_debug("perf_mode %d, cal_type %d, size %d\n",
+ perf_mode, cal_index, *size);
+
+unlock:
+ mutex_unlock(&this_adm.cal_data[cal_index]->lock);
+end:
+ return rc;
+}
+
+int adm_send_compressed_device_mute(int port_id, int copp_idx, bool mute_on)
+{
+ u32 mute_param = mute_on ? 1 : 0;
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ pr_debug("%s port_id: 0x%x, copp_idx %d, mute_on: %d\n",
+ __func__, port_id, copp_idx, mute_on);
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_COMPRESSED_MUTE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_COMPRESSED_MUTE;
+ param_hdr.param_size = sizeof(mute_param);
+
+ ret = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
+ (uint8_t *) &mute_param);
+ if (ret)
+ pr_err("%s: Failed to set mute, err %d\n", __func__, ret);
+
+ return ret;
+}
+
+int adm_send_compressed_device_latency(int port_id, int copp_idx, int latency)
+{
+ u32 latency_param;
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ pr_debug("%s port_id: 0x%x, copp_idx %d latency: %d\n", __func__,
+ port_id, copp_idx, latency);
+
+ if (latency < 0) {
+ pr_err("%s: Invalid value for latency %d", __func__, latency);
+ return -EINVAL;
+ }
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_COMPRESSED_LATENCY;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_COMPRESSED_LATENCY;
+ param_hdr.param_size = sizeof(latency_param);
+
+ latency_param = latency;
+
+ ret = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
+ (uint8_t *) &latency_param);
+ if (ret)
+ pr_err("%s: Failed to set latency, err %d\n", __func__, ret);
+
+ return ret;
+}
+
+/**
+ * adm_swap_speaker_channels
+ *
+ * Receives port_id, copp_idx, sample rate, spk_swap and
+ * send MFC command to swap speaker channel.
+ * Return zero on success. On failure returns nonzero.
+ *
+ * port_id - Passed value, port_id for which channels swap is wanted
+ * copp_idx - Passed value, copp_idx for which channels swap is wanted
+ * sample_rate - Passed value, sample rate used by app type config
+ * spk_swap - Passed value, spk_swap for check if swap flag is set
+*/
+int adm_swap_speaker_channels(int port_id, int copp_idx,
+ int sample_rate, bool spk_swap)
+{
+ struct audproc_mfc_param_media_fmt mfc_cfg;
+ struct param_hdr_v3 param_hdr = {0};
+ uint16_t num_channels;
+ int port_idx = 0;
+ int ret = 0;
+
+ pr_debug("%s: Enter, port_id %d, copp_idx %d\n",
+ __func__, port_id, copp_idx);
+ port_id = q6audio_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
+ pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+ return -EINVAL;
+ } else if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+ pr_err("%s: Invalid copp_idx 0x%x\n", __func__, copp_idx);
+ return -EINVAL;
+ }
+
+ num_channels = atomic_read(&this_adm.copp.channels[port_idx][copp_idx]);
+ if (num_channels != 2) {
+ pr_debug("%s: Invalid number of channels: %d\n",
+ __func__, num_channels);
+ return -EINVAL;
+ }
+
+ memset(&mfc_cfg, 0, sizeof(mfc_cfg));
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_MFC;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT;
+ param_hdr.param_size = sizeof(mfc_cfg);
+
+ mfc_cfg.sampling_rate = sample_rate;
+ mfc_cfg.bits_per_sample =
+ atomic_read(&this_adm.copp.bit_width[port_idx][copp_idx]);
+ mfc_cfg.num_channels = num_channels;
+
+ /* Currently applying speaker swap for only 2 channel use case */
+ if (spk_swap) {
+ mfc_cfg.channel_type[0] =
+ (uint16_t) PCM_CHANNEL_FR;
+ mfc_cfg.channel_type[1] =
+ (uint16_t) PCM_CHANNEL_FL;
+ } else {
+ mfc_cfg.channel_type[0] =
+ (uint16_t) PCM_CHANNEL_FL;
+ mfc_cfg.channel_type[1] =
+ (uint16_t) PCM_CHANNEL_FR;
+ }
+
+ ret = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
+ (u8 *) &mfc_cfg);
+ if (ret < 0) {
+ pr_err("%s: Failed to set swap speaker channels on port[0x%x] failed %d\n",
+ __func__, port_id, ret);
+ return ret;
+ }
+
+ pr_debug("%s: mfc_cfg Set params returned success", __func__);
+ return 0;
+}
+EXPORT_SYMBOL(adm_swap_speaker_channels);
+
+int adm_set_sound_focus(int port_id, int copp_idx,
+ struct sound_focus_param soundFocusData)
+{
+ struct adm_param_fluence_soundfocus_t soundfocus_params;
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+ int i;
+
+ pr_debug("%s: Enter, port_id %d, copp_idx %d\n",
+ __func__, port_id, copp_idx);
+
+ param_hdr.module_id = VOICEPROC_MODULE_ID_GENERIC_TX;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS;
+ param_hdr.param_size = sizeof(soundfocus_params);
+
+ memset(&(soundfocus_params), 0xFF, sizeof(soundfocus_params));
+ for (i = 0; i < MAX_SECTORS; i++) {
+ soundfocus_params.start_angles[i] =
+ soundFocusData.start_angle[i];
+ soundfocus_params.enables[i] = soundFocusData.enable[i];
+ pr_debug("%s: start_angle[%d] = %d\n",
+ __func__, i, soundFocusData.start_angle[i]);
+ pr_debug("%s: enable[%d] = %d\n",
+ __func__, i, soundFocusData.enable[i]);
+ }
+ soundfocus_params.gain_step = soundFocusData.gain_step;
+ pr_debug("%s: gain_step = %d\n", __func__, soundFocusData.gain_step);
+
+ soundfocus_params.reserved = 0;
+
+ ret = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr,
+ (uint8_t *) &soundfocus_params);
+ if (ret)
+ pr_err("%s: Failed to set sound focus params, err %d\n",
+ __func__, ret);
+
+ pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int adm_get_sound_focus(int port_id, int copp_idx,
+ struct sound_focus_param *soundFocusData)
+{
+ int ret = 0, i;
+ char *params_value;
+ uint32_t max_param_size = 0;
+ struct adm_param_fluence_soundfocus_t *soundfocus_params = NULL;
+ struct param_hdr_v3 param_hdr = {0};
+
+ pr_debug("%s: Enter, port_id %d, copp_idx %d\n",
+ __func__, port_id, copp_idx);
+
+ max_param_size = sizeof(struct adm_param_fluence_soundfocus_t) +
+ sizeof(union param_hdrs);
+ params_value = kzalloc(max_param_size, GFP_KERNEL);
+ if (!params_value)
+ return -ENOMEM;
+
+ param_hdr.module_id = VOICEPROC_MODULE_ID_GENERIC_TX;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS;
+ param_hdr.param_size = max_param_size;
+ ret = adm_get_pp_params(port_id, copp_idx,
+ ADM_CLIENT_ID_SOURCE_TRACKING, NULL, &param_hdr,
+ params_value);
+ if (ret) {
+ pr_err("%s: get parameters failed ret:%d\n", __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (this_adm.sourceTrackingData.apr_cmd_status != 0) {
+ pr_err("%s - get params returned error [%s]\n",
+ __func__, adsp_err_get_err_str(
+ this_adm.sourceTrackingData.apr_cmd_status));
+ ret = adsp_err_get_lnx_err_code(
+ this_adm.sourceTrackingData.apr_cmd_status);
+ goto done;
+ }
+
+ soundfocus_params = (struct adm_param_fluence_soundfocus_t *)
+ params_value;
+ for (i = 0; i < MAX_SECTORS; i++) {
+ soundFocusData->start_angle[i] =
+ soundfocus_params->start_angles[i];
+ soundFocusData->enable[i] = soundfocus_params->enables[i];
+ pr_debug("%s: start_angle[%d] = %d\n",
+ __func__, i, soundFocusData->start_angle[i]);
+ pr_debug("%s: enable[%d] = %d\n",
+ __func__, i, soundFocusData->enable[i]);
+ }
+ soundFocusData->gain_step = soundfocus_params->gain_step;
+ pr_debug("%s: gain_step = %d\n", __func__, soundFocusData->gain_step);
+
+done:
+ pr_debug("%s: Exit, ret = %d\n", __func__, ret);
+
+ kfree(params_value);
+ return ret;
+}
+
+static int adm_source_tracking_alloc_map_memory(void)
+{
+ int ret;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ ret = msm_audio_ion_alloc("SOURCE_TRACKING",
+ &this_adm.sourceTrackingData.ion_client,
+ &this_adm.sourceTrackingData.ion_handle,
+ AUD_PROC_BLOCK_SIZE,
+ &this_adm.sourceTrackingData.memmap.paddr,
+ &this_adm.sourceTrackingData.memmap.size,
+ &this_adm.sourceTrackingData.memmap.kvaddr);
+ if (ret) {
+ pr_err("%s: failed to allocate memory\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ atomic_set(&this_adm.mem_map_index, ADM_MEM_MAP_INDEX_SOURCE_TRACKING);
+ ret = adm_memory_map_regions(&this_adm.sourceTrackingData.memmap.paddr,
+ 0,
+ (uint32_t *)&this_adm.sourceTrackingData.memmap.size,
+ 1);
+ if (ret < 0) {
+ pr_err("%s: failed to map memory, paddr = 0x%pK, size = %d\n",
+ __func__,
+ (void *)this_adm.sourceTrackingData.memmap.paddr,
+ (uint32_t)this_adm.sourceTrackingData.memmap.size);
+
+ msm_audio_ion_free(this_adm.sourceTrackingData.ion_client,
+ this_adm.sourceTrackingData.ion_handle);
+ this_adm.sourceTrackingData.ion_client = NULL;
+ this_adm.sourceTrackingData.ion_handle = NULL;
+ this_adm.sourceTrackingData.memmap.size = 0;
+ this_adm.sourceTrackingData.memmap.kvaddr = NULL;
+ this_adm.sourceTrackingData.memmap.paddr = 0;
+ this_adm.sourceTrackingData.apr_cmd_status = -1;
+ atomic_set(&this_adm.mem_map_handles
+ [ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = 0;
+ pr_debug("%s: paddr = 0x%pK, size = %d, mem_map_handle = 0x%x\n",
+ __func__, (void *)this_adm.sourceTrackingData.memmap.paddr,
+ (uint32_t)this_adm.sourceTrackingData.memmap.size,
+ atomic_read(&this_adm.mem_map_handles
+ [ADM_MEM_MAP_INDEX_SOURCE_TRACKING]));
+
+done:
+ pr_debug("%s: Exit, ret = %d\n", __func__, ret);
+
+ return ret;
+}
+
+int adm_get_source_tracking(int port_id, int copp_idx,
+ struct source_tracking_param *sourceTrackingData)
+{
+ struct adm_param_fluence_sourcetracking_t *source_tracking_params =
+ NULL;
+ struct mem_mapping_hdr mem_hdr = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int i = 0;
+ int ret = 0;
+
+ pr_debug("%s: Enter, port_id %d, copp_idx %d\n",
+ __func__, port_id, copp_idx);
+
+ if (!this_adm.sourceTrackingData.memmap.paddr) {
+ /* Allocate and map shared memory for out of band usage */
+ ret = adm_source_tracking_alloc_map_memory();
+ if (ret != 0) {
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ mem_hdr.data_payload_addr_lsw =
+ lower_32_bits(this_adm.sourceTrackingData.memmap.paddr);
+ mem_hdr.data_payload_addr_msw = msm_audio_populate_upper_32_bits(
+ this_adm.sourceTrackingData.memmap.paddr);
+ mem_hdr.mem_map_handle = atomic_read(
+ &this_adm.mem_map_handles[ADM_MEM_MAP_INDEX_SOURCE_TRACKING]);
+
+ param_hdr.module_id = VOICEPROC_MODULE_ID_GENERIC_TX;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = VOICEPROC_PARAM_ID_FLUENCE_SOURCETRACKING;
+ /*
+ * This size should be the max size of the calibration data + header.
+ * Use the union size to ensure max size is used.
+ */
+ param_hdr.param_size =
+ sizeof(struct adm_param_fluence_sourcetracking_t) +
+ sizeof(union param_hdrs);
+
+ /*
+ * Retrieving parameters out of band, so no need to provide a buffer for
+ * the returned parameter data as it will be at the memory location
+ * provided.
+ */
+ ret = adm_get_pp_params(port_id, copp_idx,
+ ADM_CLIENT_ID_SOURCE_TRACKING, &mem_hdr,
+ &param_hdr, NULL);
+ if (ret) {
+ pr_err("%s: Failed to get params, error %d\n", __func__, ret);
+ goto done;
+ }
+
+ if (this_adm.sourceTrackingData.apr_cmd_status != 0) {
+ pr_err("%s - get params returned error [%s]\n",
+ __func__, adsp_err_get_err_str(
+ this_adm.sourceTrackingData.apr_cmd_status));
+
+ ret = adsp_err_get_lnx_err_code(
+ this_adm.sourceTrackingData.apr_cmd_status);
+ goto done;
+ }
+
+ /* How do we know what the param data was retrieved with for hdr size */
+ source_tracking_params =
+ (struct adm_param_fluence_sourcetracking_t
+ *) (this_adm.sourceTrackingData.memmap.kvaddr +
+ sizeof(struct param_hdr_v1));
+ for (i = 0; i < MAX_SECTORS; i++) {
+ sourceTrackingData->vad[i] = source_tracking_params->vad[i];
+ pr_debug("%s: vad[%d] = %d\n",
+ __func__, i, sourceTrackingData->vad[i]);
+ }
+ sourceTrackingData->doa_speech = source_tracking_params->doa_speech;
+ pr_debug("%s: doa_speech = %d\n",
+ __func__, sourceTrackingData->doa_speech);
+
+ for (i = 0; i < MAX_NOISE_SOURCE_INDICATORS; i++) {
+ sourceTrackingData->doa_noise[i] =
+ source_tracking_params->doa_noise[i];
+ pr_debug("%s: doa_noise[%d] = %d\n",
+ __func__, i, sourceTrackingData->doa_noise[i]);
+ }
+ for (i = 0; i < MAX_POLAR_ACTIVITY_INDICATORS; i++) {
+ sourceTrackingData->polar_activity[i] =
+ source_tracking_params->polar_activity[i];
+ pr_debug("%s: polar_activity[%d] = %d\n",
+ __func__, i, sourceTrackingData->polar_activity[i]);
+ }
+
+ ret = 0;
+
+done:
+ pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int __init adm_init(void)
+{
+ int i = 0, j;
+
+ this_adm.ec_ref_rx = -1;
+ init_waitqueue_head(&this_adm.matrix_map_wait);
+ init_waitqueue_head(&this_adm.adm_wait);
+
+ for (i = 0; i < AFE_MAX_PORTS; i++) {
+ for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+ atomic_set(&this_adm.copp.id[i][j], RESET_COPP_ID);
+ init_waitqueue_head(&this_adm.copp.wait[i][j]);
+ init_waitqueue_head(
+ &this_adm.copp.adm_delay_wait[i][j]);
+ }
+ }
+
+ if (adm_init_cal_data())
+ pr_err("%s: could not init cal data!\n", __func__);
+
+ this_adm.sourceTrackingData.apr_cmd_status = -1;
+
+ return 0;
+}
+
+static void __exit adm_exit(void)
+{
+ adm_delete_cal_data();
+}
+
+device_initcall(adm_init);
+module_exit(adm_exit);
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
new file mode 100644
index 000000000000..610604fcfe15
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -0,0 +1,7327 @@
+/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/delay.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6audio-v2.h>
+#include <sound/q6common.h>
+#include "msm-pcm-routing-v2.h"
+#include <sound/audio_cal_utils.h>
+#include <sound/adsp_err.h>
+#include <linux/qdsp6v2/apr_tal.h>
+#include <sound/q6core.h>
+
+#define WAKELOCK_TIMEOUT 5000
+enum {
+ AFE_COMMON_RX_CAL = 0,
+ AFE_COMMON_TX_CAL,
+ AFE_AANC_CAL,
+ AFE_FB_SPKR_PROT_CAL,
+ AFE_HW_DELAY_CAL,
+ AFE_SIDETONE_CAL,
+ AFE_SIDETONE_IIR_CAL,
+ AFE_TOPOLOGY_CAL,
+ AFE_CUST_TOPOLOGY_CAL,
+ AFE_FB_SPKR_PROT_TH_VI_CAL,
+ AFE_FB_SPKR_PROT_EX_VI_CAL,
+ MAX_AFE_CAL_TYPES
+};
+
+enum fbsp_state {
+ FBSP_INCORRECT_OP_MODE,
+ FBSP_INACTIVE,
+ FBSP_WARMUP,
+ FBSP_IN_PROGRESS,
+ FBSP_SUCCESS,
+ FBSP_FAILED,
+ MAX_FBSP_STATE
+};
+
+static char fbsp_state[MAX_FBSP_STATE][50] = {
+ [FBSP_INCORRECT_OP_MODE] = "incorrect operation mode",
+ [FBSP_INACTIVE] = "port not started",
+ [FBSP_WARMUP] = "waiting for warmup",
+ [FBSP_IN_PROGRESS] = "in progress state",
+ [FBSP_SUCCESS] = "success",
+ [FBSP_FAILED] = "failed"
+};
+
+enum {
+ USE_CALIBRATED_R0TO,
+ USE_SAFE_R0TO
+};
+
+enum {
+ QUICK_CALIB_DISABLE,
+ QUICK_CALIB_ENABLE
+};
+
+enum {
+ Q6AFE_MSM_SPKR_PROCESSING = 0,
+ Q6AFE_MSM_SPKR_CALIBRATION,
+ Q6AFE_MSM_SPKR_FTM_MODE
+};
+
+struct wlock {
+ struct wakeup_source ws;
+};
+
+static struct wlock wl;
+
+struct afe_ctl {
+ void *apr;
+ atomic_t state;
+ atomic_t status;
+ wait_queue_head_t wait[AFE_MAX_PORTS];
+ struct task_struct *task;
+ void (*tx_cb) (uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv);
+ void (*rx_cb) (uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv);
+ void *tx_private_data;
+ void *rx_private_data;
+ uint32_t mmap_handle;
+
+ int topology[AFE_MAX_PORTS];
+ struct cal_type_data *cal_data[MAX_AFE_CAL_TYPES];
+
+ atomic_t mem_map_cal_handles[MAX_AFE_CAL_TYPES];
+ atomic_t mem_map_cal_index;
+ u32 afe_cal_mode[AFE_MAX_PORTS];
+
+ u16 dtmf_gen_rx_portid;
+ struct audio_cal_info_spk_prot_cfg prot_cfg;
+ struct afe_spkr_prot_calib_get_resp calib_data;
+ struct audio_cal_info_sp_th_vi_ftm_cfg th_ftm_cfg;
+ struct audio_cal_info_sp_ex_vi_ftm_cfg ex_ftm_cfg;
+ struct afe_sp_th_vi_get_param_resp th_vi_resp;
+ struct afe_sp_ex_vi_get_param_resp ex_vi_resp;
+ struct afe_av_dev_drift_get_param_resp av_dev_drift_resp;
+ int vi_tx_port;
+ int vi_rx_port;
+ uint32_t afe_sample_rates[AFE_MAX_PORTS];
+ struct aanc_data aanc_info;
+ struct mutex afe_cmd_lock;
+ int set_custom_topology;
+ int dev_acdb_id[AFE_MAX_PORTS];
+ routing_cb rt_cb;
+ int num_alloced_rddma;
+ bool alloced_rddma[AFE_MAX_RDDMA];
+ int num_alloced_wrdma;
+ bool alloced_wrdma[AFE_MAX_WRDMA];
+};
+
+static atomic_t afe_ports_mad_type[SLIMBUS_PORT_LAST - SLIMBUS_0_RX];
+static unsigned long afe_configured_cmd;
+
+static struct afe_ctl this_afe;
+
+#define TIMEOUT_MS 1000
+#define Q6AFE_MAX_VOLUME 0x3FFF
+
+static int pcm_afe_instance[2];
+static int proxy_afe_instance[2];
+bool afe_close_done[2] = {true, true};
+
+#define SIZEOF_CFG_CMD(y) \
+ (sizeof(struct apr_hdr) + sizeof(u16) + (sizeof(struct y)))
+
+static int afe_get_cal_hw_delay(int32_t path,
+ struct audio_cal_hw_delay_entry *entry);
+static int remap_cal_data(struct cal_block_data *cal_block, int cal_index);
+
+int afe_get_svc_version(uint32_t service_id)
+{
+ int ret = 0;
+ static int afe_cached_version;
+ size_t ver_size;
+ struct avcs_fwk_ver_info *ver_info = NULL;
+
+ if (service_id == AVCS_SERVICE_ID_ALL) {
+ pr_err("%s: Invalid service id: %d", __func__,
+ AVCS_SERVICE_ID_ALL);
+ return -EINVAL;
+ }
+
+ if (afe_cached_version != 0)
+ return afe_cached_version;
+
+ ver_size = sizeof(struct avcs_get_fwk_version) +
+ sizeof(struct avs_svc_api_info);
+ ver_info = kzalloc(ver_size, GFP_KERNEL);
+ if (ver_info == NULL)
+ return -ENOMEM;
+
+ ret = q6core_get_service_version(service_id, ver_info, ver_size);
+ if (ret < 0)
+ goto done;
+
+ ret = ver_info->services[0].api_version;
+ afe_cached_version = ret;
+done:
+ kfree(ver_info);
+ return ret;
+}
+
+static atomic_t tdm_gp_en_ref[IDX_GROUP_TDM_MAX];
+
+static int afe_get_tdm_group_idx(u16 group_id)
+{
+ int gp_idx = -1;
+
+ switch (group_id) {
+ case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX:
+ gp_idx = IDX_GROUP_PRIMARY_TDM_RX;
+ break;
+ case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX:
+ gp_idx = IDX_GROUP_PRIMARY_TDM_TX;
+ break;
+ case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX:
+ gp_idx = IDX_GROUP_SECONDARY_TDM_RX;
+ break;
+ case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX:
+ gp_idx = IDX_GROUP_SECONDARY_TDM_TX;
+ break;
+ case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX:
+ gp_idx = IDX_GROUP_TERTIARY_TDM_RX;
+ break;
+ case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX:
+ gp_idx = IDX_GROUP_TERTIARY_TDM_TX;
+ break;
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX:
+ gp_idx = IDX_GROUP_QUATERNARY_TDM_RX;
+ break;
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX:
+ gp_idx = IDX_GROUP_QUATERNARY_TDM_TX;
+ break;
+ }
+
+ return gp_idx;
+}
+
+int afe_get_topology(int port_id)
+{
+ int topology;
+ int port_index = afe_get_port_index(port_id);
+
+ if ((port_index < 0) || (port_index >= AFE_MAX_PORTS)) {
+ pr_err("%s: Invalid port index %d\n", __func__, port_index);
+ topology = -EINVAL;
+ goto done;
+ }
+
+ topology = this_afe.topology[port_index];
+done:
+ return topology;
+}
+
+void afe_set_aanc_info(struct aanc_data *q6_aanc_info)
+{
+ this_afe.aanc_info.aanc_active = q6_aanc_info->aanc_active;
+ this_afe.aanc_info.aanc_rx_port = q6_aanc_info->aanc_rx_port;
+ this_afe.aanc_info.aanc_tx_port = q6_aanc_info->aanc_tx_port;
+
+ pr_debug("%s: aanc active is %d rx port is 0x%x, tx port is 0x%x\n",
+ __func__,
+ this_afe.aanc_info.aanc_active,
+ this_afe.aanc_info.aanc_rx_port,
+ this_afe.aanc_info.aanc_tx_port);
+}
+
+static void afe_callback_debug_print(struct apr_client_data *data)
+{
+ uint32_t *payload;
+ payload = data->payload;
+
+ if (data->payload_size >= 8)
+ pr_debug("%s: code = 0x%x PL#0[0x%x], PL#1[0x%x], size = %d\n",
+ __func__, data->opcode, payload[0], payload[1],
+ data->payload_size);
+ else if (data->payload_size >= 4)
+ pr_debug("%s: code = 0x%x PL#0[0x%x], size = %d\n",
+ __func__, data->opcode, payload[0],
+ data->payload_size);
+ else
+ pr_debug("%s: code = 0x%x, size = %d\n",
+ __func__, data->opcode, data->payload_size);
+}
+
+static void av_dev_drift_afe_cb_handler(uint32_t opcode, uint32_t *payload,
+ uint32_t payload_size)
+{
+ u32 param_id;
+ size_t expected_size =
+ sizeof(u32) + sizeof(struct afe_param_id_dev_timing_stats);
+
+ /* Get param ID depending on command type */
+ param_id = (opcode == AFE_PORT_CMDRSP_GET_PARAM_V3) ? payload[3] :
+ payload[2];
+ if (param_id != AFE_PARAM_ID_DEV_TIMING_STATS) {
+ pr_err("%s: Unrecognized param ID %d\n", __func__, param_id);
+ return;
+ }
+
+ switch (opcode) {
+ case AFE_PORT_CMDRSP_GET_PARAM_V2:
+ expected_size += sizeof(struct param_hdr_v1);
+ if (payload_size < expected_size) {
+ pr_err("%s: Error: received size %d, expected size %zu\n",
+ __func__, payload_size, expected_size);
+ return;
+ }
+ /* Repack response to add IID */
+ this_afe.av_dev_drift_resp.status = payload[0];
+ this_afe.av_dev_drift_resp.pdata.module_id = payload[1];
+ this_afe.av_dev_drift_resp.pdata.instance_id = INSTANCE_ID_0;
+ this_afe.av_dev_drift_resp.pdata.param_id = payload[2];
+ this_afe.av_dev_drift_resp.pdata.param_size = payload[3];
+ memcpy(&this_afe.av_dev_drift_resp.timing_stats, &payload[4],
+ sizeof(struct afe_param_id_dev_timing_stats));
+ break;
+ case AFE_PORT_CMDRSP_GET_PARAM_V3:
+ expected_size += sizeof(struct param_hdr_v3);
+ if (payload_size < expected_size) {
+ pr_err("%s: Error: received size %d, expected size %zu\n",
+ __func__, payload_size, expected_size);
+ return;
+ }
+ memcpy(&this_afe.av_dev_drift_resp, payload,
+ sizeof(this_afe.av_dev_drift_resp));
+ break;
+ default:
+ pr_err("%s: Unrecognized command %d\n", __func__, opcode);
+ return;
+ }
+
+ if (!this_afe.av_dev_drift_resp.status) {
+ atomic_set(&this_afe.state, 0);
+ } else {
+ pr_debug("%s: av_dev_drift_resp status: %d", __func__,
+ this_afe.av_dev_drift_resp.status);
+ atomic_set(&this_afe.state, -1);
+ }
+}
+
+static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload,
+ uint32_t payload_size)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ u32 *data_dest = NULL;
+ u32 *data_start = NULL;
+ size_t expected_size = sizeof(u32);
+
+ /* Set command specific details */
+ switch (opcode) {
+ case AFE_PORT_CMDRSP_GET_PARAM_V2:
+ if (payload_size < (5 * sizeof(uint32_t))) {
+ pr_err("%s: Error: size %d is less than expected\n",
+ __func__, payload_size);
+ return -EINVAL;
+ }
+ expected_size += sizeof(struct param_hdr_v1);
+ param_hdr.module_id = payload[1];
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = payload[2];
+ param_hdr.param_size = payload[3];
+ data_start = &payload[4];
+ break;
+ case AFE_PORT_CMDRSP_GET_PARAM_V3:
+ if (payload_size < (6 * sizeof(uint32_t))) {
+ pr_err("%s: Error: size %d is less than expected\n",
+ __func__, payload_size);
+ return -EINVAL;
+ }
+ expected_size += sizeof(struct param_hdr_v3);
+ if (payload_size < expected_size) {
+ pr_err("%s: Error: size %d is less than expected\n",
+ __func__, payload_size);
+ return -EINVAL;
+ }
+ memcpy(&param_hdr, &payload[1], sizeof(struct param_hdr_v3));
+ data_start = &payload[5];
+ break;
+ default:
+ pr_err("%s: Unrecognized command %d\n", __func__, opcode);
+ return -EINVAL;
+ }
+
+ switch (param_hdr.param_id) {
+ case AFE_PARAM_ID_CALIB_RES_CFG_V2:
+ expected_size += sizeof(struct asm_calib_res_cfg);
+ data_dest = (u32 *) &this_afe.calib_data;
+ break;
+ case AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS:
+ expected_size += sizeof(struct afe_sp_th_vi_ftm_params);
+ data_dest = (u32 *) &this_afe.th_vi_resp;
+ break;
+ case AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS:
+ expected_size += sizeof(struct afe_sp_ex_vi_ftm_params);
+ data_dest = (u32 *) &this_afe.ex_vi_resp;
+ break;
+ default:
+ pr_err("%s: Unrecognized param ID %d\n", __func__,
+ param_hdr.param_id);
+ return -EINVAL;
+ }
+
+ if (payload_size < expected_size) {
+ pr_err("%s: Error: received size %d, expected size %zu for param %d\n",
+ __func__, payload_size, expected_size,
+ param_hdr.param_id);
+ return -EINVAL;
+ }
+
+ data_dest[0] = payload[0];
+ memcpy(&data_dest[1], &param_hdr, sizeof(struct param_hdr_v3));
+ memcpy(&data_dest[5], data_start, param_hdr.param_size);
+
+ if (!data_dest[0]) {
+ atomic_set(&this_afe.state, 0);
+ } else {
+ pr_debug("%s: status: %d", __func__, data_dest[0]);
+ atomic_set(&this_afe.state, -1);
+ }
+
+ return 0;
+}
+
+static int32_t afe_lpass_resources_callback(struct apr_client_data *data)
+{
+ uint8_t *payload = data->payload;
+ struct afe_cmdrsp_request_lpass_resources *resources =
+ (struct afe_cmdrsp_request_lpass_resources *) payload;
+ struct afe_cmdrsp_request_lpass_dma_resources *dma_resources = NULL;
+ uint8_t *dma_channels_id_payload = NULL;
+
+ if (!payload || (data->token >= AFE_MAX_PORTS)) {
+ pr_err("%s: Error: size %d payload %pK token %d\n",
+ __func__, data->payload_size,
+ payload, data->token);
+ atomic_set(&this_afe.status, ADSP_EBADPARAM);
+ return -EINVAL;
+ }
+
+ if (resources->status != 0) {
+ pr_debug("%s: Error: Requesting LPASS resources ret %d\n",
+ __func__, resources->status);
+ atomic_set(&this_afe.status, ADSP_EBADPARAM);
+ return -EINVAL;
+ }
+
+ if (resources->resource_id == AFE_LPAIF_DMA_RESOURCE_ID) {
+ int i;
+
+ payload += sizeof(
+ struct afe_cmdrsp_request_lpass_resources);
+ dma_resources = (struct
+ afe_cmdrsp_request_lpass_dma_resources *)
+ payload;
+
+ pr_debug("%s: DMA Type allocated = %d\n",
+ __func__,
+ dma_resources->dma_type);
+
+ if (dma_resources->num_read_dma_channels > AFE_MAX_RDDMA) {
+ pr_err("%s: Allocated Read DMA %d exceeds max %d\n",
+ __func__,
+ dma_resources->num_read_dma_channels,
+ AFE_MAX_RDDMA);
+ dma_resources->num_read_dma_channels = AFE_MAX_RDDMA;
+ }
+
+ if (dma_resources->num_write_dma_channels > AFE_MAX_WRDMA) {
+ pr_err("%s: Allocated Write DMA %d exceeds max %d\n",
+ __func__,
+ dma_resources->num_write_dma_channels,
+ AFE_MAX_WRDMA);
+ dma_resources->num_write_dma_channels = AFE_MAX_WRDMA;
+ }
+
+ this_afe.num_alloced_rddma =
+ dma_resources->num_read_dma_channels;
+ this_afe.num_alloced_wrdma =
+ dma_resources->num_write_dma_channels;
+
+ pr_debug("%s: Number of allocated Read DMA channels= %d\n",
+ __func__,
+ dma_resources->num_read_dma_channels);
+ pr_debug("%s: Number of allocated Write DMA channels= %d\n",
+ __func__,
+ dma_resources->num_write_dma_channels);
+
+ payload += sizeof(
+ struct afe_cmdrsp_request_lpass_dma_resources);
+ dma_channels_id_payload = payload;
+
+ for (i = 0; i < this_afe.num_alloced_rddma; i++) {
+ pr_debug("%s: Read DMA Index %d allocated\n",
+ __func__, *dma_channels_id_payload);
+ this_afe.alloced_rddma
+ [*dma_channels_id_payload] = 1;
+ dma_channels_id_payload++;
+ }
+
+ for (i = 0; i < this_afe.num_alloced_wrdma; i++) {
+ pr_debug("%s: Write DMA Index %d allocated\n",
+ __func__, *dma_channels_id_payload);
+ this_afe.alloced_wrdma
+ [*dma_channels_id_payload] = 1;
+ dma_channels_id_payload++;
+ }
+ } else {
+ pr_err("%s: Error: Unknown resource ID %d",
+ __func__, resources->resource_id);
+ atomic_set(&this_afe.status, ADSP_EBADPARAM);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool afe_token_is_valid(uint32_t token)
+{
+ if (token >= AFE_MAX_PORTS) {
+ pr_err("%s: token %d is invalid.\n", __func__, token);
+ return false;
+ }
+ return true;
+}
+
+static int32_t afe_callback(struct apr_client_data *data, void *priv)
+{
+ if (!data) {
+ pr_err("%s: Invalid param data\n", __func__);
+ return -EINVAL;
+ }
+ if (data->opcode == RESET_EVENTS) {
+ int i = 0;
+ pr_debug("%s: reset event = %d %d apr[%pK]\n",
+ __func__,
+ data->reset_event, data->reset_proc, this_afe.apr);
+
+ cal_utils_clear_cal_block_q6maps(MAX_AFE_CAL_TYPES,
+ this_afe.cal_data);
+
+ /* Reset the custom topology mode: to resend again to AFE. */
+ mutex_lock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock);
+ this_afe.set_custom_topology = 1;
+ mutex_unlock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock);
+ rtac_clear_mapping(AFE_RTAC_CAL);
+
+ if (this_afe.apr) {
+ apr_reset(this_afe.apr);
+ atomic_set(&this_afe.state, 0);
+ this_afe.apr = NULL;
+ rtac_set_afe_handle(this_afe.apr);
+ }
+ /* send info to user */
+ if (this_afe.task == NULL)
+ this_afe.task = current;
+ pr_debug("%s: task_name = %s pid = %d\n",
+ __func__,
+ this_afe.task->comm, this_afe.task->pid);
+
+ /*
+ * Pass reset events to proxy driver, if cb is registered
+ */
+ if (this_afe.tx_cb) {
+ this_afe.tx_cb(data->opcode, data->token,
+ data->payload,
+ this_afe.tx_private_data);
+ this_afe.tx_cb = NULL;
+ }
+ if (this_afe.rx_cb) {
+ this_afe.rx_cb(data->opcode, data->token,
+ data->payload,
+ this_afe.rx_private_data);
+ this_afe.rx_cb = NULL;
+ }
+
+ /*
+ * reset TDM group enable ref cnt
+ */
+ for (i = 0; i < IDX_GROUP_TDM_MAX; i++)
+ atomic_set(&tdm_gp_en_ref[i], 0);
+
+ return 0;
+ }
+ afe_callback_debug_print(data);
+ if (data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V2 ||
+ data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V3) {
+ uint32_t *payload = data->payload;
+ uint32_t param_id;
+ uint32_t param_id_pos = 0;
+
+ if (!payload || (data->token >= AFE_MAX_PORTS)) {
+ pr_err("%s: Error: size %d payload %pK token %d\n",
+ __func__, data->payload_size,
+ payload, data->token);
+ return -EINVAL;
+ }
+
+ if (rtac_make_afe_callback(data->payload,
+ data->payload_size))
+ return 0;
+
+ if (data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V3)
+ param_id_pos = 4;
+ else
+ param_id_pos = 3;
+
+ if (data->payload_size >= param_id_pos * sizeof(uint32_t))
+ param_id = payload[param_id_pos - 1];
+ else {
+ pr_err("%s: Error: size %d is less than expected\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
+ if (param_id == AFE_PARAM_ID_DEV_TIMING_STATS) {
+ av_dev_drift_afe_cb_handler(data->opcode, data->payload,
+ data->payload_size);
+ } else {
+ if (sp_make_afe_callback(data->opcode, data->payload,
+ data->payload_size))
+ return -EINVAL;
+ }
+ wake_up(&this_afe.wait[data->token]);
+ } else if (data->opcode == AFE_CMDRSP_REQUEST_LPASS_RESOURCES) {
+ uint32_t ret = 0;
+
+ ret = afe_lpass_resources_callback(data);
+ atomic_set(&this_afe.state, 0);
+ if (afe_token_is_valid(data->token))
+ wake_up(&this_afe.wait[data->token]);
+ else
+ return -EINVAL;
+ if (!ret) {
+ return ret;
+ }
+ } else if (data->payload_size) {
+ uint32_t *payload;
+ uint16_t port_id = 0;
+ payload = data->payload;
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ if (data->payload_size < (2 * sizeof(uint32_t))) {
+ pr_err("%s: Error: size %d is less than expected\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
+ pr_debug("%s:opcode = 0x%x cmd = 0x%x status = 0x%x token=%d\n",
+ __func__, data->opcode,
+ payload[0], payload[1], data->token);
+ /* payload[1] contains the error status for response */
+ if (payload[1] != 0) {
+ atomic_set(&this_afe.status, payload[1]);
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, payload[0], payload[1]);
+ }
+ switch (payload[0]) {
+ case AFE_PORT_CMD_SET_PARAM_V2:
+ case AFE_PORT_CMD_SET_PARAM_V3:
+ if (rtac_make_afe_callback(payload,
+ data->payload_size))
+ return 0;
+ case AFE_PORT_CMD_DEVICE_STOP:
+ case AFE_PORT_CMD_DEVICE_START:
+ case AFE_PSEUDOPORT_CMD_START:
+ case AFE_PSEUDOPORT_CMD_STOP:
+ case AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS:
+ case AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS:
+ case AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER:
+ case AFE_PORTS_CMD_DTMF_CTL:
+ case AFE_SVC_CMD_SET_PARAM:
+ case AFE_SVC_CMD_SET_PARAM_V2:
+ case AFE_CMD_REQUEST_LPASS_RESOURCES:
+ atomic_set(&this_afe.state, 0);
+ if (afe_token_is_valid(data->token))
+ wake_up(&this_afe.wait[data->token]);
+ else
+ return -EINVAL;
+ break;
+ case AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER:
+ break;
+ case AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2:
+ port_id = RT_PROXY_PORT_001_TX;
+ break;
+ case AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2:
+ port_id = RT_PROXY_PORT_001_RX;
+ break;
+ case AFE_CMD_ADD_TOPOLOGIES:
+ atomic_set(&this_afe.state, 0);
+ if (afe_token_is_valid(data->token))
+ wake_up(&this_afe.wait[data->token]);
+ else
+ return -EINVAL;
+ pr_debug("%s: AFE_CMD_ADD_TOPOLOGIES cmd 0x%x\n",
+ __func__, payload[1]);
+ break;
+ case AFE_PORT_CMD_GET_PARAM_V2:
+ case AFE_PORT_CMD_GET_PARAM_V3:
+ /*
+ * Should only come here if there is an APR
+ * error or malformed APR packet. Otherwise
+ * response will be returned as
+ * AFE_PORT_CMDRSP_GET_PARAM_V2/3
+ */
+ pr_debug("%s: AFE Get Param opcode 0x%x token 0x%x src %d dest %d\n",
+ __func__, data->opcode, data->token,
+ data->src_port, data->dest_port);
+ if (payload[1] != 0) {
+ pr_err("%s: ADM Get Param failed with error %d\n",
+ __func__, payload[1]);
+ if (rtac_make_afe_callback(
+ payload,
+ data->payload_size))
+ return 0;
+ }
+ atomic_set(&this_afe.state, payload[1]);
+ wake_up(&this_afe.wait[data->token]);
+ break;
+ case AFE_CMD_RELEASE_LPASS_RESOURCES:
+ memset(&this_afe.alloced_rddma[0],
+ 0,
+ AFE_MAX_RDDMA);
+ memset(&this_afe.alloced_wrdma[0],
+ 0,
+ AFE_MAX_WRDMA);
+ this_afe.num_alloced_rddma = 0;
+ this_afe.num_alloced_wrdma = 0;
+ atomic_set(&this_afe.state, 0);
+ wake_up(&this_afe.wait[data->token]);
+ break;
+ default:
+ pr_err("%s: Unknown cmd 0x%x\n", __func__,
+ payload[0]);
+ break;
+ }
+ } else if (data->opcode ==
+ AFE_SERVICE_CMDRSP_SHARED_MEM_MAP_REGIONS) {
+ pr_debug("%s: mmap_handle: 0x%x, cal index %d\n",
+ __func__, payload[0],
+ atomic_read(&this_afe.mem_map_cal_index));
+ if (atomic_read(&this_afe.mem_map_cal_index) != -1)
+ atomic_set(&this_afe.mem_map_cal_handles[
+ atomic_read(
+ &this_afe.mem_map_cal_index)],
+ (uint32_t)payload[0]);
+ else
+ this_afe.mmap_handle = payload[0];
+ atomic_set(&this_afe.state, 0);
+ if (afe_token_is_valid(data->token))
+ wake_up(&this_afe.wait[data->token]);
+ else
+ return -EINVAL;
+ } else if (data->opcode == AFE_EVENT_RT_PROXY_PORT_STATUS) {
+ port_id = (uint16_t)(0x0000FFFF & payload[0]);
+ }
+ pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+ switch (port_id) {
+ case RT_PROXY_PORT_001_TX: {
+ if (this_afe.tx_cb) {
+ this_afe.tx_cb(data->opcode, data->token,
+ data->payload,
+ this_afe.tx_private_data);
+ }
+ break;
+ }
+ case RT_PROXY_PORT_001_RX: {
+ if (this_afe.rx_cb) {
+ this_afe.rx_cb(data->opcode, data->token,
+ data->payload,
+ this_afe.rx_private_data);
+ }
+ break;
+ }
+ default:
+ pr_debug("%s: default case 0x%x\n", __func__, port_id);
+ break;
+ }
+ }
+ return 0;
+}
+
+int afe_get_port_type(u16 port_id)
+{
+ int ret;
+
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case SECONDARY_I2S_RX:
+ case MI2S_RX:
+ case HDMI_RX:
+ case DISPLAY_PORT_RX:
+ case AFE_PORT_ID_SPDIF_RX:
+ case SLIMBUS_0_RX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_3_RX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_5_RX:
+ case SLIMBUS_6_RX:
+ case SLIMBUS_7_RX:
+ case SLIMBUS_8_RX:
+ case INT_BT_SCO_RX:
+ case INT_BT_A2DP_RX:
+ case INT_FM_RX:
+ case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
+ case RT_PROXY_PORT_001_RX:
+ case RT_PROXY_PORT_002_RX:
+ case RT_PROXY_PORT_002_TX:
+ case AUDIO_PORT_ID_I2S_RX:
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ case AFE_PORT_ID_QUINARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_SECONDARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ case AFE_PORT_ID_USB_RX:
+ case AFE_PORT_ID_INT0_MI2S_RX:
+ case AFE_PORT_ID_INT1_MI2S_RX:
+ case AFE_PORT_ID_INT2_MI2S_RX:
+ case AFE_PORT_ID_INT3_MI2S_RX:
+ case AFE_PORT_ID_INT4_MI2S_RX:
+ case AFE_PORT_ID_INT5_MI2S_RX:
+ case AFE_PORT_ID_INT6_MI2S_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ ret = MSM_AFE_PORT_TYPE_RX;
+ break;
+
+ case PRIMARY_I2S_TX:
+ case SECONDARY_I2S_TX:
+ case MI2S_TX:
+ case DIGI_MIC_TX:
+ case VOICE_RECORD_TX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_TX:
+ case SLIMBUS_4_TX:
+ case SLIMBUS_5_TX:
+ case SLIMBUS_6_TX:
+ case SLIMBUS_7_TX:
+ case SLIMBUS_8_TX:
+ case INT_FM_TX:
+ case VOICE_RECORD_RX:
+ case INT_BT_SCO_TX:
+ case RT_PROXY_PORT_001_TX:
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ case AFE_PORT_ID_QUINARY_MI2S_TX:
+ case AFE_PORT_ID_SENARY_MI2S_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ case AFE_PORT_ID_USB_TX:
+ case AFE_PORT_ID_INT0_MI2S_TX:
+ case AFE_PORT_ID_INT1_MI2S_TX:
+ case AFE_PORT_ID_INT2_MI2S_TX:
+ case AFE_PORT_ID_INT3_MI2S_TX:
+ case AFE_PORT_ID_INT4_MI2S_TX:
+ case AFE_PORT_ID_INT5_MI2S_TX:
+ case AFE_PORT_ID_INT6_MI2S_TX:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ ret = MSM_AFE_PORT_TYPE_TX;
+ break;
+
+ default:
+ WARN_ON(1);
+ pr_err("%s: Invalid port id = 0x%x\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int afe_sizeof_cfg_cmd(u16 port_id)
+{
+ int ret_size;
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case MI2S_RX:
+ case MI2S_TX:
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ case AFE_PORT_ID_QUINARY_MI2S_RX:
+ case AFE_PORT_ID_QUINARY_MI2S_TX:
+ ret_size = SIZEOF_CFG_CMD(afe_param_id_i2s_cfg);
+ break;
+ case HDMI_RX:
+ case DISPLAY_PORT_RX:
+ ret_size =
+ SIZEOF_CFG_CMD(afe_param_id_hdmi_multi_chan_audio_cfg);
+ break;
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_RX:
+ case SLIMBUS_3_TX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_4_TX:
+ case SLIMBUS_5_RX:
+ case SLIMBUS_5_TX:
+ case SLIMBUS_6_RX:
+ case SLIMBUS_6_TX:
+ case SLIMBUS_7_RX:
+ case SLIMBUS_7_TX:
+ case SLIMBUS_8_RX:
+ case SLIMBUS_8_TX:
+ ret_size = SIZEOF_CFG_CMD(afe_param_id_slimbus_cfg);
+ break;
+ case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ ret_size = SIZEOF_CFG_CMD(afe_param_id_pseudo_port_cfg);
+ break;
+ case RT_PROXY_PORT_001_RX:
+ case RT_PROXY_PORT_001_TX:
+ ret_size = SIZEOF_CFG_CMD(afe_param_id_rt_proxy_port_cfg);
+ break;
+ case AFE_PORT_ID_USB_RX:
+ case AFE_PORT_ID_USB_TX:
+ ret_size = SIZEOF_CFG_CMD(afe_param_id_usb_audio_cfg);
+ break;
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ case AFE_PORT_ID_SECONDARY_PCM_RX:
+ case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ default:
+ pr_debug("%s: default case 0x%x\n", __func__, port_id);
+ ret_size = SIZEOF_CFG_CMD(afe_param_id_pcm_cfg);
+ break;
+ }
+ return ret_size;
+}
+
+int afe_q6_interface_prepare(void)
+{
+ int ret = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ }
+ rtac_set_afe_handle(this_afe.apr);
+ }
+ return ret;
+}
+
+/*
+ * afe_apr_send_pkt : returns 0 on success, negative otherwise.
+ */
+static int afe_apr_send_pkt(void *data, wait_queue_head_t *wait)
+{
+ int ret;
+
+ if (wait)
+ atomic_set(&this_afe.state, 1);
+ atomic_set(&this_afe.status, 0);
+ ret = apr_send_pkt(this_afe.apr, data);
+ if (ret > 0) {
+ if (wait) {
+ ret = wait_event_timeout(*wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ } else if (atomic_read(&this_afe.status) > 0) {
+ pr_err("%s: DSP returned error[%s]\n", __func__,
+ adsp_err_get_err_str(atomic_read(
+ &this_afe.status)));
+ ret = adsp_err_get_lnx_err_code(
+ atomic_read(&this_afe.status));
+ } else {
+ ret = 0;
+ }
+ } else {
+ ret = 0;
+ }
+ } else if (ret == 0) {
+ pr_err("%s: packet not transmitted\n", __func__);
+ /* apr_send_pkt can return 0 when nothing is transmitted */
+ ret = -EINVAL;
+ }
+
+ pr_debug("%s: leave %d\n", __func__, ret);
+ return ret;
+}
+
+/* This function shouldn't be called directly. Instead call q6afe_set_params. */
+static int q6afe_set_params_v2(u16 port_id, int index,
+ struct mem_mapping_hdr *mem_hdr,
+ u8 *packed_param_data, u32 packed_data_size)
+{
+ struct afe_port_cmd_set_param_v2 *set_param = NULL;
+ uint32_t size = sizeof(struct afe_port_cmd_set_param_v2);
+ int rc = 0;
+
+ if (packed_param_data != NULL)
+ size += packed_data_size;
+ set_param = kzalloc(size, GFP_KERNEL);
+ if (set_param == NULL)
+ return -ENOMEM;
+
+ set_param->apr_hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ set_param->apr_hdr.pkt_size = size;
+ set_param->apr_hdr.src_port = 0;
+ set_param->apr_hdr.dest_port = 0;
+ set_param->apr_hdr.token = index;
+ set_param->apr_hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ set_param->port_id = port_id;
+ if (packed_data_size > U16_MAX) {
+ pr_err("%s: Invalid data size for set params V2 %d\n", __func__,
+ packed_data_size);
+ rc = -EINVAL;
+ goto done;
+ }
+ set_param->payload_size = packed_data_size;
+ if (mem_hdr != NULL) {
+ set_param->mem_hdr = *mem_hdr;
+ } else if (packed_param_data != NULL) {
+ memcpy(&set_param->param_data, packed_param_data,
+ packed_data_size);
+ } else {
+ pr_err("%s: Both memory header and param data are NULL\n",
+ __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rc = afe_apr_send_pkt(set_param, &this_afe.wait[index]);
+done:
+ kfree(set_param);
+ return rc;
+}
+
+/* This function shouldn't be called directly. Instead call q6afe_set_params. */
+static int q6afe_set_params_v3(u16 port_id, int index,
+ struct mem_mapping_hdr *mem_hdr,
+ u8 *packed_param_data, u32 packed_data_size)
+{
+ struct afe_port_cmd_set_param_v3 *set_param = NULL;
+ uint32_t size = sizeof(struct afe_port_cmd_set_param_v3);
+ int rc = 0;
+
+ if (packed_param_data != NULL)
+ size += packed_data_size;
+ set_param = kzalloc(size, GFP_KERNEL);
+ if (set_param == NULL)
+ return -ENOMEM;
+
+ set_param->apr_hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ set_param->apr_hdr.pkt_size = size;
+ set_param->apr_hdr.src_port = 0;
+ set_param->apr_hdr.dest_port = 0;
+ set_param->apr_hdr.token = index;
+ set_param->apr_hdr.opcode = AFE_PORT_CMD_SET_PARAM_V3;
+ set_param->port_id = port_id;
+ set_param->payload_size = packed_data_size;
+ if (mem_hdr != NULL) {
+ set_param->mem_hdr = *mem_hdr;
+ } else if (packed_param_data != NULL) {
+ memcpy(&set_param->param_data, packed_param_data,
+ packed_data_size);
+ } else {
+ pr_err("%s: Both memory header and param data are NULL\n",
+ __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rc = afe_apr_send_pkt(set_param, &this_afe.wait[index]);
+done:
+ kfree(set_param);
+ return rc;
+}
+
+static int q6afe_set_params(u16 port_id, int index,
+ struct mem_mapping_hdr *mem_hdr,
+ u8 *packed_param_data, u32 packed_data_size)
+{
+ int ret = 0;
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ ;
+ }
+
+ port_id = q6audio_get_port_id(port_id);
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Not a valid port id = 0x%x ret %d\n", __func__,
+ port_id, ret);
+ return -EINVAL;
+ }
+
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid\n", __func__, index);
+ return -EINVAL;
+ }
+
+ if (q6common_is_instance_id_supported())
+ return q6afe_set_params_v3(port_id, index, mem_hdr,
+ packed_param_data, packed_data_size);
+ else
+ return q6afe_set_params_v2(port_id, index, mem_hdr,
+ packed_param_data, packed_data_size);
+}
+
+static int q6afe_pack_and_set_param_in_band(u16 port_id, int index,
+ struct param_hdr_v3 param_hdr,
+ u8 *param_data)
+{
+ u8 *packed_param_data = NULL;
+ int packed_data_size = sizeof(union param_hdrs) + param_hdr.param_size;
+ int ret;
+
+ packed_param_data = kzalloc(packed_data_size, GFP_KERNEL);
+ if (packed_param_data == NULL)
+ return -ENOMEM;
+
+ ret = q6common_pack_pp_params(packed_param_data, &param_hdr, param_data,
+ &packed_data_size);
+ if (ret) {
+ pr_err("%s: Failed to pack param header and data, error %d\n",
+ __func__, ret);
+ goto fail_cmd;
+ }
+
+ ret = q6afe_set_params(port_id, index, NULL, packed_param_data,
+ packed_data_size);
+
+fail_cmd:
+ kfree(packed_param_data);
+ return ret;
+}
+
+/* This function shouldn't be called directly. Instead call q6afe_get_param. */
+static int q6afe_get_params_v2(u16 port_id, int index,
+ struct mem_mapping_hdr *mem_hdr,
+ struct param_hdr_v3 *param_hdr)
+{
+ struct afe_port_cmd_get_param_v2 afe_get_param;
+ u32 param_size = param_hdr->param_size;
+
+ memset(&afe_get_param, 0, sizeof(afe_get_param));
+ afe_get_param.apr_hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ afe_get_param.apr_hdr.pkt_size = sizeof(afe_get_param);
+ afe_get_param.apr_hdr.src_port = 0;
+ afe_get_param.apr_hdr.dest_port = 0;
+ afe_get_param.apr_hdr.token = index;
+ afe_get_param.apr_hdr.opcode = AFE_PORT_CMD_GET_PARAM_V2;
+ afe_get_param.port_id = port_id;
+ afe_get_param.payload_size = sizeof(struct param_hdr_v1) + param_size;
+ if (mem_hdr != NULL)
+ afe_get_param.mem_hdr = *mem_hdr;
+ /* Set MID and PID in command */
+ afe_get_param.module_id = param_hdr->module_id;
+ afe_get_param.param_id = param_hdr->param_id;
+ /* Set param header in payload */
+ afe_get_param.param_hdr.module_id = param_hdr->module_id;
+ afe_get_param.param_hdr.param_id = param_hdr->param_id;
+ afe_get_param.param_hdr.param_size = param_size;
+
+ return afe_apr_send_pkt(&afe_get_param, &this_afe.wait[index]);
+}
+
+/* This function shouldn't be called directly. Instead call q6afe_get_param. */
+static int q6afe_get_params_v3(u16 port_id, int index,
+ struct mem_mapping_hdr *mem_hdr,
+ struct param_hdr_v3 *param_hdr)
+{
+ struct afe_port_cmd_get_param_v3 afe_get_param;
+
+ memset(&afe_get_param, 0, sizeof(afe_get_param));
+ afe_get_param.apr_hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ afe_get_param.apr_hdr.pkt_size = sizeof(afe_get_param);
+ afe_get_param.apr_hdr.src_port = 0;
+ afe_get_param.apr_hdr.dest_port = 0;
+ afe_get_param.apr_hdr.token = index;
+ afe_get_param.apr_hdr.opcode = AFE_PORT_CMD_GET_PARAM_V3;
+ afe_get_param.port_id = port_id;
+ if (mem_hdr != NULL)
+ afe_get_param.mem_hdr = *mem_hdr;
+ /* Set param header in command, no payload in V3 */
+ afe_get_param.param_hdr = *param_hdr;
+
+ return afe_apr_send_pkt(&afe_get_param, &this_afe.wait[index]);
+}
+
+/*
+ * Calling functions copy param data directly from this_afe. Do not copy data
+ * back to caller here.
+ */
+static int q6afe_get_params(u16 port_id, struct mem_mapping_hdr *mem_hdr,
+ struct param_hdr_v3 *param_hdr)
+{
+ int index;
+ int ret;
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ port_id = q6audio_get_port_id(port_id);
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Not a valid port id = 0x%x ret %d\n", __func__,
+ port_id, ret);
+ return -EINVAL;
+ }
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid\n", __func__, index);
+ return -EINVAL;
+ }
+
+ if (q6common_is_instance_id_supported())
+ return q6afe_get_params_v3(port_id, index, NULL, param_hdr);
+ else
+ return q6afe_get_params_v2(port_id, index, NULL, param_hdr);
+}
+
+/*
+ * This function shouldn't be called directly. Instead call
+ * q6afe_svc_set_params.
+ */
+static int q6afe_svc_set_params_v1(int index, struct mem_mapping_hdr *mem_hdr,
+ u8 *packed_param_data, u32 packed_data_size)
+{
+ struct afe_svc_cmd_set_param_v1 *svc_set_param = NULL;
+ uint32_t size = sizeof(struct afe_svc_cmd_set_param_v1);
+ int rc = 0;
+
+ if (packed_param_data != NULL)
+ size += packed_data_size;
+ svc_set_param = kzalloc(size, GFP_KERNEL);
+ if (svc_set_param == NULL)
+ return -ENOMEM;
+
+ svc_set_param->apr_hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ svc_set_param->apr_hdr.pkt_size = size;
+ svc_set_param->apr_hdr.src_port = 0;
+ svc_set_param->apr_hdr.dest_port = 0;
+ svc_set_param->apr_hdr.token = index;
+ svc_set_param->apr_hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+ svc_set_param->payload_size = packed_data_size;
+
+ if (mem_hdr != NULL) {
+ /* Out of band case. */
+ svc_set_param->mem_hdr = *mem_hdr;
+ } else if (packed_param_data != NULL) {
+ /* In band case. */
+ memcpy(&svc_set_param->param_data, packed_param_data,
+ packed_data_size);
+ } else {
+ pr_err("%s: Both memory header and param data are NULL\n",
+ __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rc = afe_apr_send_pkt(svc_set_param, &this_afe.wait[index]);
+done:
+ kfree(svc_set_param);
+ return rc;
+}
+
+/*
+ * This function shouldn't be called directly. Instead call
+ * q6afe_svc_set_params.
+ */
+static int q6afe_svc_set_params_v2(int index, struct mem_mapping_hdr *mem_hdr,
+ u8 *packed_param_data, u32 packed_data_size)
+{
+ struct afe_svc_cmd_set_param_v2 *svc_set_param = NULL;
+ uint16_t size = sizeof(struct afe_svc_cmd_set_param_v2);
+ int rc = 0;
+
+ if (packed_param_data != NULL)
+ size += packed_data_size;
+ svc_set_param = kzalloc(size, GFP_KERNEL);
+ if (svc_set_param == NULL)
+ return -ENOMEM;
+
+ svc_set_param->apr_hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ svc_set_param->apr_hdr.pkt_size = size;
+ svc_set_param->apr_hdr.src_port = 0;
+ svc_set_param->apr_hdr.dest_port = 0;
+ svc_set_param->apr_hdr.token = index;
+ svc_set_param->apr_hdr.opcode = AFE_SVC_CMD_SET_PARAM_V2;
+ svc_set_param->payload_size = packed_data_size;
+
+ if (mem_hdr != NULL) {
+ /* Out of band case. */
+ svc_set_param->mem_hdr = *mem_hdr;
+ } else if (packed_param_data != NULL) {
+ /* In band case. */
+ memcpy(&svc_set_param->param_data, packed_param_data,
+ packed_data_size);
+ } else {
+ pr_err("%s: Both memory header and param data are NULL\n",
+ __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rc = afe_apr_send_pkt(svc_set_param, &this_afe.wait[index]);
+done:
+ kfree(svc_set_param);
+ return rc;
+}
+
+static int q6afe_svc_set_params(int index, struct mem_mapping_hdr *mem_hdr,
+ u8 *packed_param_data, u32 packed_data_size)
+{
+ int ret;
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ if (q6common_is_instance_id_supported())
+ return q6afe_svc_set_params_v2(index, mem_hdr,
+ packed_param_data,
+ packed_data_size);
+ else
+ return q6afe_svc_set_params_v1(index, mem_hdr,
+ packed_param_data,
+ packed_data_size);
+}
+
+static int q6afe_svc_pack_and_set_param_in_band(int index,
+ struct param_hdr_v3 param_hdr,
+ u8 *param_data)
+{
+ u8 *packed_param_data = NULL;
+ u32 packed_data_size =
+ sizeof(struct param_hdr_v3) + param_hdr.param_size;
+ int ret = 0;
+
+ packed_param_data = kzalloc(packed_data_size, GFP_KERNEL);
+ if (!packed_param_data)
+ return -ENOMEM;
+
+ ret = q6common_pack_pp_params(packed_param_data, &param_hdr, param_data,
+ &packed_data_size);
+ if (ret) {
+ pr_err("%s: Failed to pack parameter header and data, error %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = q6afe_svc_set_params(index, NULL, packed_param_data,
+ packed_data_size);
+
+done:
+ kfree(packed_param_data);
+ return ret;
+}
+
+static int afe_send_cal_block(u16 port_id, struct cal_block_data *cal_block)
+{
+ struct mem_mapping_hdr mem_hdr = {0};
+ int payload_size = 0;
+ int result = 0;
+
+ if (!cal_block) {
+ pr_debug("%s: No AFE cal to send!\n", __func__);
+ result = -EINVAL;
+ goto done;
+ }
+ if (cal_block->cal_data.size <= 0) {
+ pr_debug("%s: AFE cal has invalid size!\n", __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ payload_size = cal_block->cal_data.size;
+ mem_hdr.data_payload_addr_lsw =
+ lower_32_bits(cal_block->cal_data.paddr);
+ mem_hdr.data_payload_addr_msw =
+ msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+ mem_hdr.mem_map_handle = cal_block->map_data.q6map_handle;
+
+ pr_debug("%s: AFE cal sent for device port = 0x%x, cal size = %zd, cal addr = 0x%pK\n",
+ __func__, port_id,
+ cal_block->cal_data.size, &cal_block->cal_data.paddr);
+
+ result = q6afe_set_params(port_id, q6audio_get_port_index(port_id),
+ &mem_hdr, NULL, payload_size);
+ if (result)
+ pr_err("%s: AFE cal for port 0x%x failed %d\n",
+ __func__, port_id, result);
+
+done:
+ return result;
+}
+
+
+static int afe_send_custom_topology_block(struct cal_block_data *cal_block)
+{
+ int result = 0;
+ int index = 0;
+ struct cmd_set_topologies afe_cal;
+
+ if (!cal_block) {
+ pr_err("%s: No AFE SVC cal to send!\n", __func__);
+ return -EINVAL;
+ }
+ if (cal_block->cal_data.size <= 0) {
+ pr_err("%s: AFE SVC cal has invalid size: %zd!\n",
+ __func__, cal_block->cal_data.size);
+ return -EINVAL;
+ }
+
+ afe_cal.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ afe_cal.hdr.pkt_size = sizeof(afe_cal);
+ afe_cal.hdr.src_port = 0;
+ afe_cal.hdr.dest_port = 0;
+ afe_cal.hdr.token = index;
+ afe_cal.hdr.opcode = AFE_CMD_ADD_TOPOLOGIES;
+
+ afe_cal.payload_size = cal_block->cal_data.size;
+ afe_cal.payload_addr_lsw =
+ lower_32_bits(cal_block->cal_data.paddr);
+ afe_cal.payload_addr_msw =
+ msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+ afe_cal.mem_map_handle = cal_block->map_data.q6map_handle;
+
+ pr_debug("%s:cmd_id:0x%x calsize:%zd memmap_hdl:0x%x caladdr:0x%pK",
+ __func__, AFE_CMD_ADD_TOPOLOGIES, cal_block->cal_data.size,
+ afe_cal.mem_map_handle, &cal_block->cal_data.paddr);
+
+ result = afe_apr_send_pkt(&afe_cal, &this_afe.wait[index]);
+ if (result)
+ pr_err("%s: AFE send topology for command 0x%x failed %d\n",
+ __func__, AFE_CMD_ADD_TOPOLOGIES, result);
+
+ return result;
+}
+
+static void afe_send_custom_topology(void)
+{
+ struct cal_block_data *cal_block = NULL;
+ int cal_index = AFE_CUST_TOPOLOGY_CAL;
+ int ret;
+
+ if (this_afe.cal_data[cal_index] == NULL) {
+ pr_err("%s: cal_index %d not allocated!\n",
+ __func__, cal_index);
+ return;
+ }
+ mutex_lock(&this_afe.cal_data[cal_index]->lock);
+
+ if (!this_afe.set_custom_topology)
+ goto unlock;
+ this_afe.set_custom_topology = 0;
+ cal_block = cal_utils_get_only_cal_block(this_afe.cal_data[cal_index]);
+ if (cal_block == NULL) {
+ pr_err("%s cal_block not found!!\n", __func__);
+ goto unlock;
+ }
+
+ pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index);
+
+ ret = remap_cal_data(cal_block, cal_index);
+ if (ret) {
+ pr_err("%s: Remap_cal_data failed for cal %d!\n",
+ __func__, cal_index);
+ goto unlock;
+ }
+ ret = afe_send_custom_topology_block(cal_block);
+ if (ret < 0) {
+ pr_err("%s: No cal sent for cal_index %d! ret %d\n",
+ __func__, cal_index, ret);
+ goto unlock;
+ }
+ pr_debug("%s:sent custom topology for AFE\n", __func__);
+unlock:
+ mutex_unlock(&this_afe.cal_data[cal_index]->lock);
+}
+
+static int afe_spk_ramp_dn_cfg(int port)
+{
+ struct param_hdr_v3 param_info = {0};
+ int ret = -EINVAL;
+
+ if (afe_get_port_type(port) != MSM_AFE_PORT_TYPE_RX) {
+ pr_debug("%s: port doesn't match 0x%x\n", __func__, port);
+ return 0;
+ }
+ if (this_afe.prot_cfg.mode == MSM_SPKR_PROT_DISABLED ||
+ (this_afe.vi_rx_port != port)) {
+ pr_debug("%s: spkr protection disabled port 0x%x %d 0x%x\n",
+ __func__, port, ret, this_afe.vi_rx_port);
+ return 0;
+ }
+ param_info.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = AFE_PARAM_ID_FBSP_PTONE_RAMP_CFG;
+ param_info.param_size = 0;
+
+ ret = q6afe_pack_and_set_param_in_band(port,
+ q6audio_get_port_index(port),
+ param_info, NULL);
+ if (ret) {
+ pr_err("%s: Failed to set speaker ramp duration param, err %d\n",
+ __func__, ret);
+ goto fail_cmd;
+ }
+
+ /* dsp needs atleast 15ms to ramp down pilot tone*/
+ usleep_range(15000, 15010);
+ ret = 0;
+fail_cmd:
+ pr_debug("%s: config.pdata.param_id 0x%x status %d\n", __func__,
+ param_info.param_id, ret);
+ return ret;
+}
+
+static int afe_spk_prot_prepare(int src_port, int dst_port, int param_id,
+ union afe_spkr_prot_config *prot_config)
+{
+ struct param_hdr_v3 param_info = {0};
+ int ret = -EINVAL;
+
+ ret = q6audio_validate_port(src_port);
+ if (ret < 0) {
+ pr_err("%s: Invalid src port 0x%x ret %d", __func__, src_port,
+ ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ ret = q6audio_validate_port(dst_port);
+ if (ret < 0) {
+ pr_err("%s: Invalid dst port 0x%x ret %d", __func__,
+ dst_port, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ switch (param_id) {
+ case AFE_PARAM_ID_FBSP_MODE_RX_CFG:
+ param_info.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX;
+ break;
+ case AFE_PARAM_ID_FEEDBACK_PATH_CFG:
+ this_afe.vi_tx_port = src_port;
+ this_afe.vi_rx_port = dst_port;
+ param_info.module_id = AFE_MODULE_FEEDBACK;
+ break;
+ /*
+ * AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2 is same as
+ * AFE_PARAM_ID_SP_V2_TH_VI_MODE_CFG
+ */
+ case AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2:
+ case AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG:
+ param_info.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI;
+ break;
+ case AFE_PARAM_ID_SP_V2_EX_VI_MODE_CFG:
+ case AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG:
+ param_info.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI;
+ break;
+ default:
+ pr_err("%s: default case 0x%x\n", __func__, param_id);
+ goto fail_cmd;
+ break;
+ }
+
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = param_id;
+ param_info.param_size = sizeof(union afe_spkr_prot_config);
+
+ ret = q6afe_pack_and_set_param_in_band(src_port,
+ q6audio_get_port_index(src_port),
+ param_info, (u8 *) prot_config);
+ if (ret)
+ pr_err("%s: port = 0x%x param = 0x%x failed %d\n", __func__,
+ src_port, param_id, ret);
+
+fail_cmd:
+ pr_debug("%s: config.pdata.param_id 0x%x status %d 0x%x\n", __func__,
+ param_info.param_id, ret, src_port);
+ return ret;
+}
+
+static void afe_send_cal_spkr_prot_tx(int port_id)
+{
+ union afe_spkr_prot_config afe_spk_config;
+
+ if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL ||
+ this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL ||
+ this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL)
+ return;
+
+ mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+ if ((this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED) &&
+ (this_afe.vi_tx_port == port_id)) {
+ if (this_afe.prot_cfg.mode ==
+ MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) {
+ afe_spk_config.vi_proc_cfg.operation_mode =
+ Q6AFE_MSM_SPKR_CALIBRATION;
+ afe_spk_config.vi_proc_cfg.quick_calib_flag =
+ this_afe.prot_cfg.quick_calib_flag;
+ } else {
+ afe_spk_config.vi_proc_cfg.operation_mode =
+ Q6AFE_MSM_SPKR_PROCESSING;
+ }
+
+ if (this_afe.th_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE)
+ afe_spk_config.vi_proc_cfg.operation_mode =
+ Q6AFE_MSM_SPKR_FTM_MODE;
+ afe_spk_config.vi_proc_cfg.minor_version = 1;
+ afe_spk_config.vi_proc_cfg.r0_cali_q24[SP_V2_SPKR_1] =
+ (uint32_t) this_afe.prot_cfg.r0[SP_V2_SPKR_1];
+ afe_spk_config.vi_proc_cfg.r0_cali_q24[SP_V2_SPKR_2] =
+ (uint32_t) this_afe.prot_cfg.r0[SP_V2_SPKR_2];
+ afe_spk_config.vi_proc_cfg.t0_cali_q6[SP_V2_SPKR_1] =
+ (uint32_t) this_afe.prot_cfg.t0[SP_V2_SPKR_1];
+ afe_spk_config.vi_proc_cfg.t0_cali_q6[SP_V2_SPKR_2] =
+ (uint32_t) this_afe.prot_cfg.t0[SP_V2_SPKR_2];
+ if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_NOT_CALIBRATED) {
+ struct asm_spkr_calib_vi_proc_cfg *vi_proc_cfg;
+ vi_proc_cfg = &afe_spk_config.vi_proc_cfg;
+ vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_1] =
+ USE_CALIBRATED_R0TO;
+ vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_2] =
+ USE_CALIBRATED_R0TO;
+ } else {
+ struct asm_spkr_calib_vi_proc_cfg *vi_proc_cfg;
+ vi_proc_cfg = &afe_spk_config.vi_proc_cfg;
+ vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_1] =
+ USE_SAFE_R0TO;
+ vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_2] =
+ USE_SAFE_R0TO;
+ }
+ if (afe_spk_prot_prepare(port_id, 0,
+ AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2,
+ &afe_spk_config))
+ pr_err("%s: SPKR_CALIB_VI_PROC_CFG failed\n",
+ __func__);
+ }
+ mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+
+ mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+ if ((this_afe.th_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) &&
+ (this_afe.vi_tx_port == port_id)) {
+ afe_spk_config.th_vi_ftm_cfg.minor_version = 1;
+ afe_spk_config.th_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_1] =
+ this_afe.th_ftm_cfg.wait_time[SP_V2_SPKR_1];
+ afe_spk_config.th_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_2] =
+ this_afe.th_ftm_cfg.wait_time[SP_V2_SPKR_2];
+ afe_spk_config.th_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_1] =
+ this_afe.th_ftm_cfg.ftm_time[SP_V2_SPKR_1];
+ afe_spk_config.th_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_2] =
+ this_afe.th_ftm_cfg.ftm_time[SP_V2_SPKR_2];
+
+ if (afe_spk_prot_prepare(port_id, 0,
+ AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG,
+ &afe_spk_config))
+ pr_err("%s: th vi ftm cfg failed\n", __func__);
+ this_afe.th_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
+ }
+ mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+
+ mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+ if ((this_afe.ex_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) &&
+ (this_afe.vi_tx_port == port_id)) {
+ afe_spk_config.ex_vi_mode_cfg.minor_version = 1;
+ afe_spk_config.ex_vi_mode_cfg.operation_mode =
+ Q6AFE_MSM_SPKR_FTM_MODE;
+ if (afe_spk_prot_prepare(port_id, 0,
+ AFE_PARAM_ID_SP_V2_EX_VI_MODE_CFG,
+ &afe_spk_config))
+ pr_err("%s: ex vi mode cfg failed\n", __func__);
+
+ afe_spk_config.ex_vi_ftm_cfg.minor_version = 1;
+ afe_spk_config.ex_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_1] =
+ this_afe.ex_ftm_cfg.wait_time[SP_V2_SPKR_1];
+ afe_spk_config.ex_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_2] =
+ this_afe.ex_ftm_cfg.wait_time[SP_V2_SPKR_2];
+ afe_spk_config.ex_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_1] =
+ this_afe.ex_ftm_cfg.ftm_time[SP_V2_SPKR_1];
+ afe_spk_config.ex_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_2] =
+ this_afe.ex_ftm_cfg.ftm_time[SP_V2_SPKR_2];
+
+ if (afe_spk_prot_prepare(port_id, 0,
+ AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG,
+ &afe_spk_config))
+ pr_err("%s: ex vi ftm cfg failed\n", __func__);
+ this_afe.ex_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
+ }
+ mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+
+}
+
+static void afe_send_cal_spkr_prot_rx(int port_id)
+{
+ union afe_spkr_prot_config afe_spk_config;
+
+ if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL)
+ goto done;
+
+ mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+
+ if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED &&
+ (this_afe.vi_rx_port == port_id)) {
+ if (this_afe.prot_cfg.mode ==
+ MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS)
+ afe_spk_config.mode_rx_cfg.mode =
+ Q6AFE_MSM_SPKR_CALIBRATION;
+ else
+ afe_spk_config.mode_rx_cfg.mode =
+ Q6AFE_MSM_SPKR_PROCESSING;
+ afe_spk_config.mode_rx_cfg.minor_version = 1;
+ if (afe_spk_prot_prepare(port_id, 0,
+ AFE_PARAM_ID_FBSP_MODE_RX_CFG,
+ &afe_spk_config))
+ pr_err("%s: RX MODE_VI_PROC_CFG failed\n",
+ __func__);
+ }
+ mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+done:
+ return;
+}
+
+static int afe_send_hw_delay(u16 port_id, u32 rate)
+{
+ struct audio_cal_hw_delay_entry delay_entry = {0};
+ struct afe_param_id_device_hw_delay_cfg hw_delay;
+ struct param_hdr_v3 param_info = {0};
+ int ret = -EINVAL;
+
+ pr_debug("%s:\n", __func__);
+
+ delay_entry.sample_rate = rate;
+ if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX)
+ ret = afe_get_cal_hw_delay(TX_DEVICE, &delay_entry);
+ else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX)
+ ret = afe_get_cal_hw_delay(RX_DEVICE, &delay_entry);
+
+ /*
+ * HW delay is only used for IMS calls to sync audio with video
+ * It is only needed for devices & sample rates used for IMS video
+ * calls. Values are received from ACDB calbration files
+ */
+ if (ret != 0) {
+ pr_debug("%s: debug: HW delay info not available %d\n",
+ __func__, ret);
+ goto fail_cmd;
+ }
+
+ param_info.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = AFE_PARAM_ID_DEVICE_HW_DELAY;
+ param_info.param_size = sizeof(hw_delay);
+
+ hw_delay.delay_in_us = delay_entry.delay_usec;
+ hw_delay.device_hw_delay_minor_version =
+ AFE_API_VERSION_DEVICE_HW_DELAY;
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_info, (u8 *) &hw_delay);
+ if (ret)
+ pr_err("%s: AFE hw delay for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+
+fail_cmd:
+ pr_debug("%s: port_id 0x%x rate %u delay_usec %d status %d\n",
+ __func__, port_id, rate, delay_entry.delay_usec, ret);
+ return ret;
+}
+
+static struct cal_block_data *afe_find_cal_topo_id_by_port(
+ struct cal_type_data *cal_type, u16 port_id)
+{
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block = NULL;
+ int32_t path;
+ struct audio_cal_info_afe_top *afe_top;
+ int afe_port_index = q6audio_get_port_index(port_id);
+
+ if (afe_port_index < 0)
+ goto err_exit;
+
+ list_for_each_safe(ptr, next,
+ &cal_type->cal_blocks) {
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+
+ path = ((afe_get_port_type(port_id) ==
+ MSM_AFE_PORT_TYPE_TX)?(TX_DEVICE):(RX_DEVICE));
+ afe_top =
+ (struct audio_cal_info_afe_top *)cal_block->cal_info;
+ if (afe_top->path == path) {
+ if (this_afe.dev_acdb_id[afe_port_index] > 0) {
+ if (afe_top->acdb_id ==
+ this_afe.dev_acdb_id[afe_port_index]) {
+ pr_debug("%s: top_id:%x acdb_id:%d afe_port_id:%d\n",
+ __func__, afe_top->topology,
+ afe_top->acdb_id,
+ q6audio_get_port_id(port_id));
+ return cal_block;
+ }
+ } else {
+ pr_debug("%s: top_id:%x acdb_id:%d afe_port:%d\n",
+ __func__, afe_top->topology, afe_top->acdb_id,
+ q6audio_get_port_id(port_id));
+ return cal_block;
+ }
+ }
+ }
+
+err_exit:
+ return NULL;
+}
+
+static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id)
+{
+ int ret = 0;
+
+ struct cal_block_data *cal_block = NULL;
+ struct audio_cal_info_afe_top *afe_top_info = NULL;
+
+ if (this_afe.cal_data[AFE_TOPOLOGY_CAL] == NULL) {
+ pr_err("%s: [AFE_TOPOLOGY_CAL] not initialized\n", __func__);
+ return -EINVAL;
+ }
+ if (topology_id == NULL) {
+ pr_err("%s: topology_id is NULL\n", __func__);
+ return -EINVAL;
+ }
+ *topology_id = 0;
+
+ mutex_lock(&this_afe.cal_data[AFE_TOPOLOGY_CAL]->lock);
+ cal_block = afe_find_cal_topo_id_by_port(
+ this_afe.cal_data[AFE_TOPOLOGY_CAL], port_id);
+ if (cal_block == NULL) {
+ pr_err("%s: [AFE_TOPOLOGY_CAL] not initialized for this port %d\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ afe_top_info = ((struct audio_cal_info_afe_top *)
+ cal_block->cal_info);
+ if (!afe_top_info->topology) {
+ pr_err("%s: invalid topology id : [%d, %d]\n",
+ __func__, afe_top_info->acdb_id, afe_top_info->topology);
+ ret = -EINVAL;
+ goto unlock;
+ }
+ *topology_id = (u32)afe_top_info->topology;
+
+ pr_debug("%s: port_id = %u acdb_id = %d topology_id = %u ret=%d\n",
+ __func__, port_id, afe_top_info->acdb_id,
+ afe_top_info->topology, ret);
+unlock:
+ mutex_unlock(&this_afe.cal_data[AFE_TOPOLOGY_CAL]->lock);
+ return ret;
+}
+
+static int afe_send_port_topology_id(u16 port_id)
+{
+ struct afe_param_id_set_topology_cfg topology = {0};
+ struct param_hdr_v3 param_info = {0};
+ u32 topology_id = 0;
+ int index = 0;
+ int ret = 0;
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+
+ ret = afe_get_cal_topology_id(port_id, &topology_id);
+ if (ret || !topology_id) {
+ pr_debug("%s: AFE port[%d] get_cal_topology[%d] invalid!\n",
+ __func__, port_id, topology_id);
+ goto done;
+ }
+
+ param_info.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = AFE_PARAM_ID_SET_TOPOLOGY;
+ param_info.param_size = sizeof(topology);
+
+ topology.minor_version = AFE_API_VERSION_TOPOLOGY_V1;
+ topology.topology_id = topology_id;
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_info, (u8 *) &topology);
+ if (ret) {
+ pr_err("%s: AFE set topology id enable for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ goto done;
+ }
+
+ this_afe.topology[index] = topology_id;
+ rtac_update_afe_topology(port_id);
+done:
+ pr_debug("%s: AFE set topology id 0x%x enable for port 0x%x ret %d\n",
+ __func__, topology_id, port_id, ret);
+ return ret;
+
+}
+
+static int remap_cal_data(struct cal_block_data *cal_block, int cal_index)
+{
+ int ret = 0;
+
+ if (cal_block->map_data.ion_client == NULL) {
+ pr_err("%s: No ION allocation for cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((cal_block->map_data.map_size > 0) &&
+ (cal_block->map_data.q6map_handle == 0)) {
+ atomic_set(&this_afe.mem_map_cal_index, cal_index);
+ ret = afe_cmd_memory_map(cal_block->cal_data.paddr,
+ cal_block->map_data.map_size);
+ atomic_set(&this_afe.mem_map_cal_index, -1);
+ if (ret < 0) {
+ pr_err("%s: mmap did not work! size = %zd ret %d\n",
+ __func__,
+ cal_block->map_data.map_size, ret);
+ pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n",
+ __func__,
+ &cal_block->cal_data.paddr,
+ cal_block->map_data.map_size);
+ goto done;
+ }
+ cal_block->map_data.q6map_handle = atomic_read(&this_afe.
+ mem_map_cal_handles[cal_index]);
+ }
+done:
+ return ret;
+}
+
+static struct cal_block_data *afe_find_cal(int cal_index, int port_id)
+{
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block = NULL;
+ struct audio_cal_info_afe *afe_cal_info = NULL;
+ int afe_port_index = q6audio_get_port_index(port_id);
+
+ pr_debug("%s: cal_index %d port_id %d port_index %d\n", __func__,
+ cal_index, port_id, afe_port_index);
+ if (afe_port_index < 0) {
+ pr_err("%s: Error getting AFE port index %d\n",
+ __func__, afe_port_index);
+ goto exit;
+ }
+
+ list_for_each_safe(ptr, next,
+ &this_afe.cal_data[cal_index]->cal_blocks) {
+ cal_block = list_entry(ptr, struct cal_block_data, list);
+ afe_cal_info = cal_block->cal_info;
+ if ((afe_cal_info->acdb_id ==
+ this_afe.dev_acdb_id[afe_port_index]) &&
+ (afe_cal_info->sample_rate ==
+ this_afe.afe_sample_rates[afe_port_index])) {
+ pr_debug("%s: cal block is a match, size is %zd\n",
+ __func__, cal_block->cal_data.size);
+ goto exit;
+ }
+ }
+ pr_err("%s: no matching cal_block found\n", __func__);
+ cal_block = NULL;
+
+exit:
+ return cal_block;
+}
+
+static void send_afe_cal_type(int cal_index, int port_id)
+{
+ struct cal_block_data *cal_block = NULL;
+ int ret;
+ int afe_port_index = q6audio_get_port_index(port_id);
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.cal_data[cal_index] == NULL) {
+ pr_warn("%s: cal_index %d not allocated!\n",
+ __func__, cal_index);
+ goto done;
+ }
+
+ if (afe_port_index < 0) {
+ pr_err("%s: Error getting AFE port index %d\n",
+ __func__, afe_port_index);
+ goto done;
+ }
+
+ mutex_lock(&this_afe.cal_data[cal_index]->lock);
+
+ if (((cal_index == AFE_COMMON_RX_CAL) ||
+ (cal_index == AFE_COMMON_TX_CAL)) &&
+ (this_afe.dev_acdb_id[afe_port_index] > 0))
+ cal_block = afe_find_cal(cal_index, port_id);
+ else
+ cal_block = cal_utils_get_only_cal_block(
+ this_afe.cal_data[cal_index]);
+
+ if (cal_block == NULL) {
+ pr_err("%s cal_block not found!!\n", __func__);
+ goto unlock;
+ }
+
+ pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index);
+
+ ret = remap_cal_data(cal_block, cal_index);
+ if (ret) {
+ pr_err("%s: Remap_cal_data failed for cal %d!\n",
+ __func__, cal_index);
+ goto unlock;
+ }
+ ret = afe_send_cal_block(port_id, cal_block);
+ if (ret < 0)
+ pr_debug("%s: No cal sent for cal_index %d, port_id = 0x%x! ret %d\n",
+ __func__, cal_index, port_id, ret);
+unlock:
+ mutex_unlock(&this_afe.cal_data[cal_index]->lock);
+done:
+ return;
+}
+
+void afe_send_cal(u16 port_id)
+{
+ pr_debug("%s: port_id=0x%x\n", __func__, port_id);
+
+ if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) {
+ afe_send_cal_spkr_prot_tx(port_id);
+ send_afe_cal_type(AFE_COMMON_TX_CAL, port_id);
+ } else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX) {
+ afe_send_cal_spkr_prot_rx(port_id);
+ send_afe_cal_type(AFE_COMMON_RX_CAL, port_id);
+ }
+}
+
+int afe_turn_onoff_hw_mad(u16 mad_type, u16 enable)
+{
+ struct afe_param_hw_mad_ctrl mad_enable_param = {0};
+ struct param_hdr_v3 param_info = {0};
+ int ret;
+
+ pr_debug("%s: enter\n", __func__);
+
+ param_info.module_id = AFE_MODULE_HW_MAD;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = AFE_PARAM_ID_HW_MAD_CTRL;
+ param_info.param_size = sizeof(mad_enable_param);
+
+ mad_enable_param.minor_version = 1;
+ mad_enable_param.mad_type = mad_type;
+ mad_enable_param.mad_enable = enable;
+
+ ret = q6afe_pack_and_set_param_in_band(SLIMBUS_5_TX, IDX_GLOBAL_CFG,
+ param_info,
+ (u8 *) &mad_enable_param);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_HW_MAD_CTRL failed %d\n", __func__,
+ ret);
+ return ret;
+}
+
+static int afe_send_slimbus_slave_cfg(
+ struct afe_param_cdc_slimbus_slave_cfg *sb_slave_cfg)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret;
+
+ pr_debug("%s: enter\n", __func__);
+
+ param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG;
+ param_hdr.param_size = sizeof(struct afe_param_cdc_slimbus_slave_cfg);
+
+ ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr,
+ (u8 *) sb_slave_cfg);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG failed %d\n",
+ __func__, ret);
+
+ pr_debug("%s: leave %d\n", __func__, ret);
+ return ret;
+}
+
+static int afe_send_codec_reg_page_config(
+ struct afe_param_cdc_reg_page_cfg *cdc_reg_page_cfg)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret;
+
+ param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_CDC_REG_PAGE_CFG;
+ param_hdr.param_size = sizeof(struct afe_param_cdc_reg_page_cfg);
+
+ ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr,
+ (u8 *) cdc_reg_page_cfg);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_CDC_REG_PAGE_CFG failed %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int afe_send_codec_reg_config(
+ struct afe_param_cdc_reg_cfg_data *cdc_reg_cfg)
+{
+ u8 *packed_param_data = NULL;
+ u32 packed_data_size = 0;
+ u32 single_param_size = 0;
+ u32 max_data_size = 0;
+ u32 max_single_param = 0;
+ struct param_hdr_v3 param_hdr = {0};
+ int idx = 0;
+ int ret = -EINVAL;
+
+ max_single_param = sizeof(struct param_hdr_v3) +
+ sizeof(struct afe_param_cdc_reg_cfg);
+ max_data_size = APR_MAX_BUF - sizeof(struct afe_svc_cmd_set_param_v2);
+ packed_param_data = kzalloc(max_data_size, GFP_KERNEL);
+ if (!packed_param_data)
+ return -ENOMEM;
+
+ /* param_hdr is the same for all params sent, set once at top */
+ param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_CDC_REG_CFG;
+ param_hdr.param_size = sizeof(struct afe_param_cdc_reg_cfg);
+
+ while (idx < cdc_reg_cfg->num_registers) {
+ memset(packed_param_data, 0, max_data_size);
+ packed_data_size = 0;
+ single_param_size = 0;
+
+ while (packed_data_size + max_single_param < max_data_size &&
+ idx < cdc_reg_cfg->num_registers) {
+ ret = q6common_pack_pp_params(
+ packed_param_data + packed_data_size,
+ &param_hdr, (u8 *) &cdc_reg_cfg->reg_data[idx],
+ &single_param_size);
+ if (ret) {
+ pr_err("%s: Failed to pack parameters with error %d\n",
+ __func__, ret);
+ goto done;
+ }
+ packed_data_size += single_param_size;
+ idx++;
+ }
+
+ ret = q6afe_svc_set_params(IDX_GLOBAL_CFG, NULL,
+ packed_param_data, packed_data_size);
+ if (ret) {
+ pr_err("%s: AFE_PARAM_ID_CDC_REG_CFG failed %d\n",
+ __func__, ret);
+ break;
+ }
+ }
+done:
+ kfree(packed_param_data);
+ return ret;
+}
+
+static int afe_init_cdc_reg_config(void)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret;
+
+ pr_debug("%s: enter\n", __func__);
+ param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_CDC_REG_CFG_INIT;
+
+ ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr,
+ NULL);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_CDC_INIT_REG_CFG failed %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int afe_send_slimbus_slave_port_cfg(
+ struct afe_param_slimbus_slave_port_cfg *slim_slave_config, u16 port_id)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret;
+
+ pr_debug("%s: enter, port_id = 0x%x\n", __func__, port_id);
+ param_hdr.module_id = AFE_MODULE_HW_MAD;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.reserved = 0;
+ param_hdr.param_id = AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG;
+ param_hdr.param_size = sizeof(struct afe_param_slimbus_slave_port_cfg);
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr,
+ (u8 *) slim_slave_config);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG failed %d\n",
+ __func__, ret);
+
+ pr_debug("%s: leave %d\n", __func__, ret);
+ return ret;
+}
+static int afe_aanc_port_cfg(void *apr, uint16_t tx_port, uint16_t rx_port)
+{
+ struct afe_param_aanc_port_cfg aanc_port_cfg = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ pr_debug("%s: tx_port 0x%x, rx_port 0x%x\n",
+ __func__, tx_port, rx_port);
+
+ pr_debug("%s: AANC sample rate tx rate: %d rx rate %d\n", __func__,
+ this_afe.aanc_info.aanc_tx_port_sample_rate,
+ this_afe.aanc_info.aanc_rx_port_sample_rate);
+ /*
+ * If aanc tx sample rate or rx sample rate is zero, skip aanc
+ * configuration as AFE resampler will fail for invalid sample
+ * rates.
+ */
+ if (!this_afe.aanc_info.aanc_tx_port_sample_rate ||
+ !this_afe.aanc_info.aanc_rx_port_sample_rate) {
+ return -EINVAL;
+ }
+
+ param_hdr.module_id = AFE_MODULE_AANC;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_AANC_PORT_CONFIG;
+ param_hdr.param_size = sizeof(struct afe_param_aanc_port_cfg);
+
+ aanc_port_cfg.aanc_port_cfg_minor_version =
+ AFE_API_VERSION_AANC_PORT_CONFIG;
+ aanc_port_cfg.tx_port_sample_rate =
+ this_afe.aanc_info.aanc_tx_port_sample_rate;
+ aanc_port_cfg.tx_port_channel_map[0] = AANC_TX_VOICE_MIC;
+ aanc_port_cfg.tx_port_channel_map[1] = AANC_TX_NOISE_MIC;
+ aanc_port_cfg.tx_port_channel_map[2] = AANC_TX_ERROR_MIC;
+ aanc_port_cfg.tx_port_channel_map[3] = AANC_TX_MIC_UNUSED;
+ aanc_port_cfg.tx_port_channel_map[4] = AANC_TX_MIC_UNUSED;
+ aanc_port_cfg.tx_port_channel_map[5] = AANC_TX_MIC_UNUSED;
+ aanc_port_cfg.tx_port_channel_map[6] = AANC_TX_MIC_UNUSED;
+ aanc_port_cfg.tx_port_channel_map[7] = AANC_TX_MIC_UNUSED;
+ aanc_port_cfg.tx_port_num_channels = 3;
+ aanc_port_cfg.rx_path_ref_port_id = rx_port;
+ aanc_port_cfg.ref_port_sample_rate =
+ this_afe.aanc_info.aanc_rx_port_sample_rate;
+
+ ret = q6afe_pack_and_set_param_in_band(tx_port,
+ q6audio_get_port_index(tx_port),
+ param_hdr,
+ (u8 *) &aanc_port_cfg);
+ if (ret)
+ pr_err("%s: AFE AANC port config failed for tx_port 0x%x, rx_port 0x%x ret %d\n",
+ __func__, tx_port, rx_port, ret);
+
+ return ret;
+}
+
+static int afe_aanc_mod_enable(void *apr, uint16_t tx_port, uint16_t enable)
+{
+ struct afe_mod_enable_param mod_enable = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ pr_debug("%s: tx_port 0x%x\n", __func__, tx_port);
+
+ param_hdr.module_id = AFE_MODULE_AANC;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_ENABLE;
+ param_hdr.param_size = sizeof(struct afe_mod_enable_param);
+
+ mod_enable.enable = enable;
+ mod_enable.reserved = 0;
+
+ ret = q6afe_pack_and_set_param_in_band(tx_port,
+ q6audio_get_port_index(tx_port),
+ param_hdr, (u8 *) &mod_enable);
+ if (ret)
+ pr_err("%s: AFE AANC enable failed for tx_port 0x%x ret %d\n",
+ __func__, tx_port, ret);
+ return ret;
+}
+
+static int afe_send_bank_selection_clip(
+ struct afe_param_id_clip_bank_sel *param)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret;
+
+ if (!param) {
+ pr_err("%s: Invalid params", __func__);
+ return -EINVAL;
+ }
+ param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_CLIP_BANK_SEL_CFG;
+ param_hdr.param_size = sizeof(struct afe_param_id_clip_bank_sel);
+
+ ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr,
+ (u8 *) param);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_CLIP_BANK_SEL_CFG failed %d\n",
+ __func__, ret);
+ return ret;
+}
+int afe_send_aanc_version(
+ struct afe_param_id_cdc_aanc_version *version_cfg)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret;
+
+ pr_debug("%s: enter\n", __func__);
+ param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_CDC_AANC_VERSION;
+ param_hdr.param_size = sizeof(struct afe_param_id_cdc_aanc_version);
+
+ ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr,
+ (u8 *) version_cfg);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_CDC_AANC_VERSION failed %d\n",
+ __func__, ret);
+ return ret;
+}
+
+int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type)
+{
+ int i;
+
+ if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX ||
+ port_id == AFE_PORT_ID_INT3_MI2S_TX ||
+ port_id == AFE_PORT_ID_QUATERNARY_TDM_TX) {
+ mad_type = MAD_SW_AUDIO;
+ return 0;
+ }
+
+ i = port_id - SLIMBUS_0_RX;
+ if (i < 0 || i >= ARRAY_SIZE(afe_ports_mad_type)) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ }
+ atomic_set(&afe_ports_mad_type[i], mad_type);
+ return 0;
+}
+
+enum afe_mad_type afe_port_get_mad_type(u16 port_id)
+{
+ int i;
+
+ if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX ||
+ port_id == AFE_PORT_ID_INT3_MI2S_TX ||
+ port_id == AFE_PORT_ID_QUATERNARY_TDM_TX)
+ return MAD_SW_AUDIO;
+
+ i = port_id - SLIMBUS_0_RX;
+ if (i < 0 || i >= ARRAY_SIZE(afe_ports_mad_type)) {
+ pr_debug("%s: Non Slimbus port_id 0x%x\n", __func__, port_id);
+ return MAD_HW_NONE;
+ }
+ return (enum afe_mad_type) atomic_read(&afe_ports_mad_type[i]);
+}
+
+int afe_set_config(enum afe_config_type config_type, void *config_data, int arg)
+{
+ int ret;
+
+ pr_debug("%s: enter config_type %d\n", __func__, config_type);
+ ret = afe_q6_interface_prepare();
+ if (ret) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ switch (config_type) {
+ case AFE_SLIMBUS_SLAVE_CONFIG:
+ ret = afe_send_slimbus_slave_cfg(config_data);
+ if (!ret)
+ ret = afe_init_cdc_reg_config();
+ else
+ pr_err("%s: Sending slimbus slave config failed %d\n",
+ __func__, ret);
+ break;
+ case AFE_CDC_REGISTERS_CONFIG:
+ ret = afe_send_codec_reg_config(config_data);
+ break;
+ case AFE_SLIMBUS_SLAVE_PORT_CONFIG:
+ ret = afe_send_slimbus_slave_port_cfg(config_data, arg);
+ break;
+ case AFE_AANC_VERSION:
+ ret = afe_send_aanc_version(config_data);
+ break;
+ case AFE_CLIP_BANK_SEL:
+ ret = afe_send_bank_selection_clip(config_data);
+ break;
+ case AFE_CDC_CLIP_REGISTERS_CONFIG:
+ ret = afe_send_codec_reg_config(config_data);
+ break;
+ case AFE_CDC_REGISTER_PAGE_CONFIG:
+ ret = afe_send_codec_reg_page_config(config_data);
+ break;
+ default:
+ pr_err("%s: unknown configuration type %d",
+ __func__, config_type);
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ set_bit(config_type, &afe_configured_cmd);
+
+ return ret;
+}
+
+/*
+ * afe_clear_config - If SSR happens ADSP loses AFE configs, let AFE driver know
+ * about the state so client driver can wait until AFE is
+ * reconfigured.
+ */
+void afe_clear_config(enum afe_config_type config)
+{
+ clear_bit(config, &afe_configured_cmd);
+}
+
+bool afe_has_config(enum afe_config_type config)
+{
+ return !!test_bit(config, &afe_configured_cmd);
+}
+
+int afe_send_spdif_clk_cfg(struct afe_param_id_spdif_clk_cfg *cfg,
+ u16 port_id)
+{
+ struct afe_param_id_spdif_clk_cfg clk_cfg = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (!cfg) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+ }
+
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_SPDIF_CLK_CONFIG;
+ param_hdr.param_size = sizeof(struct afe_param_id_spdif_clk_cfg);
+
+ pr_debug("%s: Minor version = 0x%x clk val = %d clk root = 0x%x port id = 0x%x\n",
+ __func__, clk_cfg.clk_cfg_minor_version, clk_cfg.clk_value,
+ clk_cfg.clk_root, q6audio_get_port_id(port_id));
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &clk_cfg);
+ if (ret < 0)
+ pr_err("%s: AFE send clock config for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ return ret;
+}
+
+int afe_send_spdif_ch_status_cfg(struct afe_param_id_spdif_ch_status_cfg
+ *ch_status_cfg, u16 port_id)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (!ch_status_cfg)
+ pr_err("%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_SPDIF_CLK_CONFIG;
+ param_hdr.param_size = sizeof(struct afe_param_id_spdif_ch_status_cfg);
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) ch_status_cfg);
+ if (ret < 0)
+ pr_err("%s: AFE send channel status for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ return ret;
+}
+
+static int afe_send_cmd_port_start(u16 port_id)
+{
+ struct afe_port_cmd_device_start start;
+ int ret, index;
+
+ pr_debug("%s: enter\n", __func__);
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = index;
+ start.hdr.opcode = AFE_PORT_CMD_DEVICE_START;
+ start.port_id = q6audio_get_port_id(port_id);
+ pr_debug("%s: cmd device start opcode[0x%x] port id[0x%x]\n",
+ __func__, start.hdr.opcode, start.port_id);
+
+ ret = afe_apr_send_pkt(&start, &this_afe.wait[index]);
+ if (ret) {
+ pr_err("%s: AFE enable for port 0x%x failed %d\n", __func__,
+ port_id, ret);
+ } else if (this_afe.task != current) {
+ this_afe.task = current;
+ pr_debug("task_name = %s pid = %d\n",
+ this_afe.task->comm, this_afe.task->pid);
+ }
+
+ return ret;
+}
+
+static int afe_aanc_start(uint16_t tx_port_id, uint16_t rx_port_id)
+{
+ int ret;
+
+ pr_debug("%s: Tx port is 0x%x, Rx port is 0x%x\n",
+ __func__, tx_port_id, rx_port_id);
+ ret = afe_aanc_port_cfg(this_afe.apr, tx_port_id, rx_port_id);
+ if (ret) {
+ pr_err("%s: Send AANC Port Config failed %d\n",
+ __func__, ret);
+ goto fail_cmd;
+ }
+ send_afe_cal_type(AFE_AANC_CAL, tx_port_id);
+
+fail_cmd:
+ return ret;
+}
+
+int afe_spdif_port_start(u16 port_id, struct afe_spdif_port_config *spdif_port,
+ u32 rate)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ uint16_t port_index;
+ int ret = 0;
+
+ if (!spdif_port) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ afe_send_cal(port_id);
+ afe_send_hw_delay(port_id, rate);
+
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_SPDIF_CONFIG;
+ param_hdr.param_size = sizeof(struct afe_spdif_port_config);
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) spdif_port);
+ if (ret) {
+ pr_err("%s: AFE enable for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ goto fail_cmd;
+ }
+
+ port_index = afe_get_port_index(port_id);
+ if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) {
+ this_afe.afe_sample_rates[port_index] = rate;
+ } else {
+ pr_err("%s: Invalid port index %d\n", __func__, port_index);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = afe_send_spdif_ch_status_cfg(&spdif_port->ch_status, port_id);
+ if (ret < 0) {
+ pr_err("%s: afe send failed %d\n", __func__, ret);
+ goto fail_cmd;
+ }
+
+ return afe_send_cmd_port_start(port_id);
+
+fail_cmd:
+ return ret;
+}
+
+static int afe_send_slot_mapping_cfg_v2(
+ struct afe_param_id_slot_mapping_cfg_v2 *slot_mapping_cfg,
+ u16 port_id)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (!slot_mapping_cfg) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+ param_hdr.module_id = AFE_MODULE_TDM;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG;
+ param_hdr.param_size = sizeof(struct afe_param_id_slot_mapping_cfg_v2);
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr,
+ (u8 *) slot_mapping_cfg);
+ if (ret < 0)
+ pr_err("%s: AFE send slot mapping for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ return ret;
+}
+
+
+int afe_send_slot_mapping_cfg(
+ struct afe_param_id_slot_mapping_cfg *slot_mapping_cfg,
+ u16 port_id)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (!slot_mapping_cfg) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+ param_hdr.module_id = AFE_MODULE_TDM;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG;
+ param_hdr.param_size = sizeof(struct afe_param_id_slot_mapping_cfg);
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr,
+ (u8 *) slot_mapping_cfg);
+ if (ret < 0)
+ pr_err("%s: AFE send slot mapping for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ return ret;
+}
+
+int afe_send_custom_tdm_header_cfg(
+ struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header_cfg,
+ u16 port_id)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (!custom_tdm_header_cfg) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+ param_hdr.module_id = AFE_MODULE_TDM;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_CUSTOM_TDM_HEADER_CONFIG;
+ param_hdr.param_size =
+ sizeof(struct afe_param_id_custom_tdm_header_cfg);
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr,
+ (u8 *) custom_tdm_header_cfg);
+ if (ret < 0)
+ pr_err("%s: AFE send custom tdm header for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ return ret;
+}
+
+int afe_i2s_port_start(u16 port_id, struct afe_i2s_port_config *i2s_port,
+ u32 rate, u16 num_groups)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int index = 0;
+ uint16_t port_index = 0;
+ enum afe_mad_type mad_type = MAD_HW_NONE;
+ int ret = 0;
+
+ if (!i2s_port) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ if ((index >= 0) && (index < AFE_MAX_PORTS)) {
+ this_afe.afe_sample_rates[index] = rate;
+
+ if (this_afe.rt_cb)
+ this_afe.dev_acdb_id[index] = this_afe.rt_cb(port_id);
+ }
+
+ /* Also send the topology id here if multiple ports: */
+ port_index = afe_get_port_index(port_id);
+ if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE) &&
+ num_groups > 1) {
+ /* One time call: only for first time */
+ afe_send_custom_topology();
+ afe_send_port_topology_id(port_id);
+ afe_send_cal(port_id);
+ afe_send_hw_delay(port_id, rate);
+ }
+
+ /* Start SW MAD module */
+ mad_type = afe_port_get_mad_type(port_id);
+ pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+ mad_type);
+ if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) {
+ if (!afe_has_config(AFE_CDC_REGISTERS_CONFIG) ||
+ !afe_has_config(AFE_SLIMBUS_SLAVE_CONFIG)) {
+ pr_err("%s: AFE isn't configured yet for\n"
+ "HW MAD try Again\n", __func__);
+ ret = -EAGAIN;
+ goto fail_cmd;
+ }
+ ret = afe_turn_onoff_hw_mad(mad_type, true);
+ if (ret) {
+ pr_err("%s: afe_turn_onoff_hw_mad failed %d\n",
+ __func__, ret);
+ goto fail_cmd;
+ }
+ }
+
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_I2S_CONFIG;
+ param_hdr.param_size = sizeof(struct afe_param_id_i2s_cfg);
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr,
+ (u8 *) &i2s_port->i2s_cfg);
+ if (ret) {
+ pr_err("%s: AFE enable for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ goto fail_cmd;
+ }
+
+ port_index = afe_get_port_index(port_id);
+ if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) {
+ this_afe.afe_sample_rates[port_index] = rate;
+ } else {
+ pr_err("%s: Invalid port index %d\n", __func__, port_index);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* slot mapping is not need if there is only one group */
+ if (num_groups > 1) {
+ ret = afe_send_slot_mapping_cfg(
+ &i2s_port->slot_mapping,
+ port_id);
+ if (ret < 0) {
+ pr_err("%s: afe send failed %d\n", __func__, ret);
+ goto fail_cmd;
+ }
+ }
+
+ ret = afe_send_cmd_port_start(port_id);
+
+fail_cmd:
+ return ret;
+}
+
+int afe_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port,
+ u32 rate, u16 num_groups)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int index = 0;
+ uint16_t port_index = 0;
+ enum afe_mad_type mad_type = MAD_HW_NONE;
+ int ret = 0;
+
+ if (!tdm_port) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ if ((index >= 0) && (index < AFE_MAX_PORTS)) {
+ this_afe.afe_sample_rates[index] = rate;
+
+ if (this_afe.rt_cb)
+ this_afe.dev_acdb_id[index] = this_afe.rt_cb(port_id);
+ }
+
+ /* Also send the topology id here if multiple ports: */
+ port_index = afe_get_port_index(port_id);
+ if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE) &&
+ num_groups > 1) {
+ /* One time call: only for first time */
+ afe_send_custom_topology();
+ afe_send_port_topology_id(port_id);
+ afe_send_cal(port_id);
+ afe_send_hw_delay(port_id, rate);
+ }
+
+ /* Start SW MAD module */
+ mad_type = afe_port_get_mad_type(port_id);
+ pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+ mad_type);
+ if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) {
+ if (!afe_has_config(AFE_CDC_REGISTERS_CONFIG) ||
+ !afe_has_config(AFE_SLIMBUS_SLAVE_CONFIG)) {
+ pr_err("%s: AFE isn't configured yet for\n"
+ "HW MAD try Again\n", __func__);
+ ret = -EAGAIN;
+ goto fail_cmd;
+ }
+ ret = afe_turn_onoff_hw_mad(mad_type, true);
+ if (ret) {
+ pr_err("%s: afe_turn_onoff_hw_mad failed %d\n",
+ __func__, ret);
+ goto fail_cmd;
+ }
+ }
+
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_TDM_CONFIG;
+ param_hdr.param_size = sizeof(struct afe_param_id_tdm_cfg);
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr,
+ (u8 *) &tdm_port->tdm);
+ if (ret) {
+ pr_err("%s: AFE enable for port 0x%x failed ret = %d\n",
+ __func__, port_id, ret);
+ goto fail_cmd;
+ }
+
+ port_index = afe_get_port_index(port_id);
+ if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) {
+ this_afe.afe_sample_rates[port_index] = rate;
+ } else {
+ pr_err("%s: Invalid port index %d\n", __func__, port_index);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* slot mapping is not need if there is only one group */
+ if (num_groups > 1) {
+ if (afe_get_svc_version(APR_SVC_AFE) >=
+ ADSP_AFE_API_VERSION_V3)
+ ret = afe_send_slot_mapping_cfg_v2(
+ &tdm_port->slot_mapping_v2,
+ port_id);
+ else
+ ret = afe_send_slot_mapping_cfg(
+ &tdm_port->slot_mapping,
+ port_id);
+ if (ret < 0) {
+ pr_err("%s: afe send failed %d\n", __func__, ret);
+ goto fail_cmd;
+ }
+ }
+
+ if (tdm_port->custom_tdm_header.header_type) {
+ ret = afe_send_custom_tdm_header_cfg(
+ &tdm_port->custom_tdm_header, port_id);
+ if (ret < 0) {
+ pr_err("%s: afe send failed %d\n", __func__, ret);
+ goto fail_cmd;
+ }
+ }
+
+ ret = afe_send_cmd_port_start(port_id);
+
+fail_cmd:
+ return ret;
+}
+
+void afe_set_cal_mode(u16 port_id, enum afe_cal_mode afe_cal_mode)
+{
+ uint16_t port_index;
+
+ port_index = afe_get_port_index(port_id);
+ this_afe.afe_cal_mode[port_index] = afe_cal_mode;
+}
+
+void afe_set_routing_callback(routing_cb cb)
+{
+ this_afe.rt_cb = cb;
+}
+
+int afe_port_send_usb_dev_param(u16 port_id, union afe_port_config *afe_config)
+{
+ struct afe_param_id_usb_audio_dev_params usb_dev = {0};
+ struct afe_param_id_usb_audio_dev_lpcm_fmt lpcm_fmt = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (!afe_config) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+
+ param_hdr.param_id = AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS;
+ param_hdr.param_size = sizeof(usb_dev);
+ usb_dev.cfg_minor_version = AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG;
+ usb_dev.dev_token = afe_config->usb_audio.dev_token;
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &usb_dev);
+ if (ret) {
+ pr_err("%s: AFE device param cmd failed %d\n",
+ __func__, ret);
+ goto exit;
+ }
+
+ param_hdr.param_id = AFE_PARAM_ID_USB_AUDIO_DEV_LPCM_FMT;
+ param_hdr.param_size = sizeof(lpcm_fmt);
+ lpcm_fmt.cfg_minor_version = AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG;
+ lpcm_fmt.endian = afe_config->usb_audio.endian;
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &lpcm_fmt);
+ if (ret) {
+ pr_err("%s: AFE device param cmd LPCM_FMT failed %d\n",
+ __func__, ret);
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+static int q6afe_send_enc_config(u16 port_id,
+ union afe_enc_config_data *cfg, u32 format,
+ union afe_port_config afe_config,
+ u16 afe_in_channels, u16 afe_in_bit_width)
+{
+ u32 enc_fmt;
+ struct afe_enc_cfg_blk_param_t enc_blk_param = {0};
+ struct avs_enc_packetizer_id_param_t enc_pkt_id_param = {0};
+ struct afe_port_media_type_t media_type = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret;
+
+ pr_debug("%s:update DSP for enc format = %d\n", __func__, format);
+ if (format != ASM_MEDIA_FMT_SBC && format != ASM_MEDIA_FMT_AAC_V2 &&
+ format != ASM_MEDIA_FMT_APTX && format != ASM_MEDIA_FMT_APTX_HD) {
+ pr_err("%s:Unsuppported format Ignore AFE config\n", __func__);
+ return 0;
+ }
+
+ param_hdr.module_id = AFE_MODULE_ID_ENCODER;
+ param_hdr.instance_id = INSTANCE_ID_0;
+
+ param_hdr.param_id = AFE_ENCODER_PARAM_ID_ENC_FMT_ID;
+ param_hdr.param_size = sizeof(enc_fmt);
+ enc_fmt = format;
+ pr_debug("%s:sending AFE_ENCODER_PARAM_ID_ENC_FMT_ID payload\n",
+ __func__);
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &enc_fmt);
+ if (ret) {
+ pr_err("%s:unable to send AFE_ENCODER_PARAM_ID_ENC_FMT_ID",
+ __func__);
+ goto exit;
+ }
+
+ pr_debug("%s:send AFE_ENCODER_PARAM_ID_ENC_CFG_BLK to DSP payloadn",
+ __func__);
+ param_hdr.param_id = AFE_ENCODER_PARAM_ID_ENC_CFG_BLK;
+ param_hdr.param_size = sizeof(struct afe_enc_cfg_blk_param_t);
+ enc_blk_param.enc_cfg_blk_size = sizeof(union afe_enc_config_data);
+ enc_blk_param.enc_blk_config = *cfg;
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr,
+ (u8 *) &enc_blk_param);
+ if (ret) {
+ pr_err("%s: AFE_ENCODER_PARAM_ID_ENC_CFG_BLK for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ goto exit;
+ }
+
+ pr_debug("%s:sending AFE_ENCODER_PARAM_ID_PACKETIZER to DSP\n",
+ __func__);
+ param_hdr.param_id = AFE_ENCODER_PARAM_ID_PACKETIZER_ID;
+ param_hdr.param_size = sizeof(struct avs_enc_packetizer_id_param_t);
+ enc_pkt_id_param.enc_packetizer_id = AFE_MODULE_ID_PACKETIZER_COP;
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr,
+ (u8 *) &enc_pkt_id_param);
+ if (ret) {
+ pr_err("%s: AFE_ENCODER_PARAM_ID_PACKETIZER for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ goto exit;
+ }
+
+ pr_debug("%s:Sending AFE_API_VERSION_PORT_MEDIA_TYPE to DSP", __func__);
+ param_hdr.module_id = AFE_MODULE_PORT;
+ param_hdr.param_id = AFE_PARAM_ID_PORT_MEDIA_TYPE;
+ param_hdr.param_size = sizeof(struct afe_port_media_type_t);
+ media_type.minor_version = AFE_API_VERSION_PORT_MEDIA_TYPE;
+ media_type.sample_rate = afe_config.slim_sch.sample_rate;
+ if (afe_in_bit_width)
+ media_type.bit_width = afe_in_bit_width;
+ else
+ media_type.bit_width = afe_config.slim_sch.bit_width;
+
+ if (afe_in_channels)
+ media_type.num_channels = afe_in_channels;
+ else
+ media_type.num_channels = afe_config.slim_sch.num_channels;
+ media_type.data_format = AFE_PORT_DATA_FORMAT_PCM;
+ media_type.reserved = 0;
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &media_type);
+ if (ret) {
+ pr_err("%s: AFE_API_VERSION_PORT_MEDIA_TYPE for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+static int __afe_port_start(u16 port_id, union afe_port_config *afe_config,
+ u32 rate, u16 afe_in_channels, u16 afe_in_bit_width,
+ union afe_enc_config_data *cfg, u32 enc_format)
+{
+ union afe_port_config port_cfg;
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+ int cfg_type;
+ int index = 0;
+ enum afe_mad_type mad_type;
+ uint16_t port_index;
+
+ memset(&port_cfg, 0, sizeof(port_cfg));
+
+ if (!afe_config) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ if ((port_id == RT_PROXY_DAI_001_RX) ||
+ (port_id == RT_PROXY_DAI_002_TX)) {
+ pr_debug("%s: before incrementing pcm_afe_instance %d"\
+ " port_id 0x%x\n", __func__,
+ pcm_afe_instance[port_id & 0x1], port_id);
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ pcm_afe_instance[port_id & 0x1]++;
+ return 0;
+ }
+ if ((port_id == RT_PROXY_DAI_002_RX) ||
+ (port_id == RT_PROXY_DAI_001_TX)) {
+ pr_debug("%s: before incrementing proxy_afe_instance %d"\
+ " port_id 0x%x\n", __func__,
+ proxy_afe_instance[port_id & 0x1], port_id);
+
+ if (!afe_close_done[port_id & 0x1]) {
+ /*close pcm dai corresponding to the proxy dai*/
+ afe_close(port_id - 0x10);
+ pcm_afe_instance[port_id & 0x1]++;
+ pr_debug("%s: reconfigure afe port again\n", __func__);
+ }
+ proxy_afe_instance[port_id & 0x1]++;
+ afe_close_done[port_id & 0x1] = false;
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ }
+
+ pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ if ((index >= 0) && (index < AFE_MAX_PORTS)) {
+ this_afe.afe_sample_rates[index] = rate;
+
+ if (this_afe.rt_cb)
+ this_afe.dev_acdb_id[index] = this_afe.rt_cb(port_id);
+ }
+
+ mutex_lock(&this_afe.afe_cmd_lock);
+ /* Also send the topology id here: */
+ port_index = afe_get_port_index(port_id);
+ if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE)) {
+ /* One time call: only for first time */
+ afe_send_custom_topology();
+ afe_send_port_topology_id(port_id);
+ afe_send_cal(port_id);
+ afe_send_hw_delay(port_id, rate);
+ }
+
+ /* Start SW MAD module */
+ mad_type = afe_port_get_mad_type(port_id);
+ pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+ mad_type);
+ if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) {
+ if (!afe_has_config(AFE_CDC_REGISTERS_CONFIG) ||
+ !afe_has_config(AFE_SLIMBUS_SLAVE_CONFIG)) {
+ pr_err("%s: AFE isn't configured yet for\n"
+ "HW MAD try Again\n", __func__);
+ ret = -EAGAIN;
+ goto fail_cmd;
+ }
+ ret = afe_turn_onoff_hw_mad(mad_type, true);
+ if (ret) {
+ pr_err("%s: afe_turn_onoff_hw_mad failed %d\n",
+ __func__, ret);
+ goto fail_cmd;
+ }
+ }
+
+ if ((this_afe.aanc_info.aanc_active) &&
+ (this_afe.aanc_info.aanc_tx_port == port_id)) {
+ this_afe.aanc_info.aanc_tx_port_sample_rate = rate;
+ port_index =
+ afe_get_port_index(this_afe.aanc_info.aanc_rx_port);
+ if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) {
+ this_afe.aanc_info.aanc_rx_port_sample_rate =
+ this_afe.afe_sample_rates[port_index];
+ } else {
+ pr_err("%s: Invalid port index %d\n",
+ __func__, port_index);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ ret = afe_aanc_start(this_afe.aanc_info.aanc_tx_port,
+ this_afe.aanc_info.aanc_rx_port);
+ pr_debug("%s: afe_aanc_start ret %d\n", __func__, ret);
+ }
+
+ if ((port_id == AFE_PORT_ID_USB_RX) ||
+ (port_id == AFE_PORT_ID_USB_TX)) {
+ ret = afe_port_send_usb_dev_param(port_id, afe_config);
+ if (ret) {
+ pr_err("%s: AFE device param for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ }
+
+ switch (port_id) {
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ case AFE_PORT_ID_SECONDARY_PCM_RX:
+ case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ cfg_type = AFE_PARAM_ID_PCM_CONFIG;
+ break;
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case MI2S_RX:
+ case MI2S_TX:
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ case AFE_PORT_ID_QUINARY_MI2S_RX:
+ case AFE_PORT_ID_QUINARY_MI2S_TX:
+ case AFE_PORT_ID_SENARY_MI2S_TX:
+ case AFE_PORT_ID_INT0_MI2S_RX:
+ case AFE_PORT_ID_INT0_MI2S_TX:
+ case AFE_PORT_ID_INT1_MI2S_RX:
+ case AFE_PORT_ID_INT1_MI2S_TX:
+ case AFE_PORT_ID_INT2_MI2S_RX:
+ case AFE_PORT_ID_INT2_MI2S_TX:
+ case AFE_PORT_ID_INT3_MI2S_RX:
+ case AFE_PORT_ID_INT3_MI2S_TX:
+ case AFE_PORT_ID_INT4_MI2S_RX:
+ case AFE_PORT_ID_INT4_MI2S_TX:
+ case AFE_PORT_ID_INT5_MI2S_RX:
+ case AFE_PORT_ID_INT5_MI2S_TX:
+ case AFE_PORT_ID_INT6_MI2S_RX:
+ case AFE_PORT_ID_INT6_MI2S_TX:
+ cfg_type = AFE_PARAM_ID_I2S_CONFIG;
+ break;
+ case HDMI_RX:
+ case DISPLAY_PORT_RX:
+ cfg_type = AFE_PARAM_ID_HDMI_CONFIG;
+ break;
+ case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ cfg_type = AFE_PARAM_ID_PSEUDO_PORT_CONFIG;
+ break;
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_RX:
+ case SLIMBUS_3_TX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_4_TX:
+ case SLIMBUS_5_RX:
+ case SLIMBUS_5_TX:
+ case SLIMBUS_6_RX:
+ case SLIMBUS_6_TX:
+ case SLIMBUS_7_RX:
+ case SLIMBUS_7_TX:
+ case SLIMBUS_8_RX:
+ case SLIMBUS_8_TX:
+ cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG;
+ break;
+ case AFE_PORT_ID_USB_RX:
+ case AFE_PORT_ID_USB_TX:
+ cfg_type = AFE_PARAM_ID_USB_AUDIO_CONFIG;
+ break;
+ case RT_PROXY_PORT_001_RX:
+ case RT_PROXY_PORT_001_TX:
+ case RT_PROXY_PORT_002_RX:
+ case RT_PROXY_PORT_002_TX:
+ cfg_type = AFE_PARAM_ID_RT_PROXY_CONFIG;
+ break;
+ case INT_BT_SCO_RX:
+ case INT_BT_A2DP_RX:
+ case INT_BT_SCO_TX:
+ case INT_FM_RX:
+ case INT_FM_TX:
+ cfg_type = AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG;
+ break;
+ default:
+ pr_err("%s: Invalid port id 0x%x\n", __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = cfg_type;
+ param_hdr.param_size = sizeof(union afe_port_config);
+
+ port_cfg = *afe_config;
+ if ((enc_format != ASM_MEDIA_FMT_NONE) &&
+ (cfg_type == AFE_PARAM_ID_SLIMBUS_CONFIG)) {
+ port_cfg.slim_sch.data_format =
+ AFE_SB_DATA_FORMAT_GENERIC_COMPRESSED;
+ }
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &port_cfg);
+ if (ret) {
+ pr_err("%s: AFE enable for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ goto fail_cmd;
+ }
+
+ if ((enc_format != ASM_MEDIA_FMT_NONE) &&
+ (cfg_type == AFE_PARAM_ID_SLIMBUS_CONFIG)) {
+ pr_debug("%s: Found AFE encoder support for SLIMBUS enc_format = %d\n",
+ __func__, enc_format);
+ ret = q6afe_send_enc_config(port_id, cfg, enc_format,
+ *afe_config, afe_in_channels,
+ afe_in_bit_width);
+ if (ret) {
+ pr_err("%s: AFE encoder config for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ goto fail_cmd;
+ }
+ }
+
+ port_index = afe_get_port_index(port_id);
+ if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) {
+ /*
+ * If afe_port_start() for tx port called before
+ * rx port, then aanc rx sample rate is zero. So,
+ * AANC state machine in AFE will not get triggered.
+ * Make sure to check whether aanc is active during
+ * afe_port_start() for rx port and if aanc rx
+ * sample rate is zero, call afe_aanc_start to configure
+ * aanc with valid sample rates.
+ */
+ if (this_afe.aanc_info.aanc_active &&
+ !this_afe.aanc_info.aanc_rx_port_sample_rate) {
+ this_afe.aanc_info.aanc_rx_port_sample_rate =
+ this_afe.afe_sample_rates[port_index];
+ ret = afe_aanc_start(this_afe.aanc_info.aanc_tx_port,
+ this_afe.aanc_info.aanc_rx_port);
+ pr_debug("%s: afe_aanc_start ret %d\n", __func__, ret);
+ }
+ } else {
+ pr_err("%s: Invalid port index %d\n", __func__, port_index);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ ret = afe_send_cmd_port_start(port_id);
+
+fail_cmd:
+ mutex_unlock(&this_afe.afe_cmd_lock);
+ return ret;
+}
+
+/**
+ * afe_port_start - to configure AFE session with
+ * specified port configuration
+ *
+ * @port_id: AFE port id number
+ * @afe_config: port configutation
+ * @rate: sampling rate of port
+ *
+ * Returns 0 on success or error value on port start failure.
+ */
+int afe_port_start(u16 port_id, union afe_port_config *afe_config,
+ u32 rate)
+{
+ return __afe_port_start(port_id, afe_config, rate,
+ 0, 0, NULL, ASM_MEDIA_FMT_NONE);
+}
+EXPORT_SYMBOL(afe_port_start);
+
+/**
+ * afe_port_start_v2 - to configure AFE session with
+ * specified port configuration and encoder params
+ *
+ * @port_id: AFE port id number
+ * @afe_config: port configutation
+ * @rate: sampling rate of port
+ * @cfg: AFE encoder configuration information to setup encoder
+ * @afe_in_channels: AFE input channel configuration, this needs
+ * update only if input channel is differ from AFE output
+ *
+ * Returns 0 on success or error value on port start failure.
+ */
+int afe_port_start_v2(u16 port_id, union afe_port_config *afe_config,
+ u32 rate, u16 afe_in_channels, u16 afe_in_bit_width,
+ struct afe_enc_config *enc_cfg)
+{
+ return __afe_port_start(port_id, afe_config, rate,
+ afe_in_channels, afe_in_bit_width,
+ &enc_cfg->data, enc_cfg->format);
+}
+EXPORT_SYMBOL(afe_port_start_v2);
+
+int afe_get_port_index(u16 port_id)
+{
+ switch (port_id) {
+ case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX;
+ case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX;
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ return IDX_AFE_PORT_ID_PRIMARY_PCM_RX;
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ return IDX_AFE_PORT_ID_PRIMARY_PCM_TX;
+ case AFE_PORT_ID_SECONDARY_PCM_RX:
+ return IDX_AFE_PORT_ID_SECONDARY_PCM_RX;
+ case AFE_PORT_ID_SECONDARY_PCM_TX:
+ return IDX_AFE_PORT_ID_SECONDARY_PCM_TX;
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_PCM_RX;
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_PCM_TX;
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX;
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX;
+ case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX;
+ case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX;
+ case MI2S_RX: return IDX_MI2S_RX;
+ case MI2S_TX: return IDX_MI2S_TX;
+ case HDMI_RX: return IDX_HDMI_RX;
+ case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX;
+ case AFE_PORT_ID_SPDIF_RX: return IDX_SPDIF_RX;
+ case RSVD_2: return IDX_RSVD_2;
+ case RSVD_3: return IDX_RSVD_3;
+ case DIGI_MIC_TX: return IDX_DIGI_MIC_TX;
+ case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX;
+ case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX;
+ case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX;
+ case VOICE2_PLAYBACK_TX: return IDX_VOICE2_PLAYBACK_TX;
+ case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX;
+ case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX;
+ case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX;
+ case SLIMBUS_1_TX: return IDX_SLIMBUS_1_TX;
+ case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX;
+ case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX;
+ case SLIMBUS_3_RX: return IDX_SLIMBUS_3_RX;
+ case SLIMBUS_3_TX: return IDX_SLIMBUS_3_TX;
+ case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX;
+ case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX;
+ case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX;
+ case INT_FM_RX: return IDX_INT_FM_RX;
+ case INT_FM_TX: return IDX_INT_FM_TX;
+ case RT_PROXY_PORT_001_RX: return IDX_RT_PROXY_PORT_001_RX;
+ case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX;
+ case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX;
+ case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX;
+ case SLIMBUS_5_RX: return IDX_SLIMBUS_5_RX;
+ case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX;
+ case SLIMBUS_6_RX: return IDX_SLIMBUS_6_RX;
+ case SLIMBUS_6_TX: return IDX_SLIMBUS_6_TX;
+ case SLIMBUS_7_RX: return IDX_SLIMBUS_7_RX;
+ case SLIMBUS_7_TX: return IDX_SLIMBUS_7_TX;
+ case SLIMBUS_8_RX: return IDX_SLIMBUS_8_RX;
+ case SLIMBUS_8_TX: return IDX_SLIMBUS_8_TX;
+ case AFE_PORT_ID_USB_RX: return IDX_AFE_PORT_ID_USB_RX;
+ case AFE_PORT_ID_USB_TX: return IDX_AFE_PORT_ID_USB_TX;
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_PRIMARY_MI2S_RX;
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_PRIMARY_MI2S_TX;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_SD1;
+ case AFE_PORT_ID_QUINARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_QUINARY_MI2S_RX;
+ case AFE_PORT_ID_QUINARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_QUINARY_MI2S_TX;
+ case AFE_PORT_ID_SENARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_SENARY_MI2S_TX;
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_0;
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_0;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_1;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_1;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_2;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_2;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_3;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_3;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_4;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_4;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_5;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_5;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_6;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_6;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_7;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_7;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_0;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_0;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_1;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_1;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_2;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_2;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_3;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_3;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_4;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_4;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_5;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_5;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_6;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_6;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_7;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_7;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_0;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_0;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_1;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_1;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_2;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_2;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_3;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_3;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_4;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_4;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_5;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_5;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_6;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_6;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_7;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_7;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_0;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_0;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_1;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_1;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_2;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_2;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_3;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_3;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_4;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_4;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_5;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_5;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_6;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7;
+ case AFE_PORT_ID_INT0_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT0_MI2S_RX;
+ case AFE_PORT_ID_INT0_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT0_MI2S_TX;
+ case AFE_PORT_ID_INT1_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT1_MI2S_RX;
+ case AFE_PORT_ID_INT1_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT1_MI2S_TX;
+ case AFE_PORT_ID_INT2_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT2_MI2S_RX;
+ case AFE_PORT_ID_INT2_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT2_MI2S_TX;
+ case AFE_PORT_ID_INT3_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT3_MI2S_RX;
+ case AFE_PORT_ID_INT3_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT3_MI2S_TX;
+ case AFE_PORT_ID_INT4_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT4_MI2S_RX;
+ case AFE_PORT_ID_INT4_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT4_MI2S_TX;
+ case AFE_PORT_ID_INT5_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT5_MI2S_RX;
+ case AFE_PORT_ID_INT5_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT5_MI2S_TX;
+ case AFE_PORT_ID_INT6_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT6_MI2S_RX;
+ case AFE_PORT_ID_INT6_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT6_MI2S_TX;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX_1;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX_2;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX_3;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX_4;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX_1;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX_2;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX_3;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX_4;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX_1;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX_2;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX_3;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX_4;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_1;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_2;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_3;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_4;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX_1;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX_2;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX_3;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX_4;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX_1;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX_2;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX_3;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX_4;
+ case RT_PROXY_PORT_002_RX:
+ return IDX_RT_PROXY_PORT_002_RX;
+ case RT_PROXY_PORT_002_TX:
+ return IDX_RT_PROXY_PORT_002_TX;
+ default:
+ pr_err("%s: port 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ }
+}
+
+int afe_open(u16 port_id,
+ union afe_port_config *afe_config, int rate)
+{
+ struct afe_port_cmd_device_start start;
+ union afe_port_config port_cfg;
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+ int cfg_type;
+ int index = 0;
+
+ memset(&start, 0, sizeof(start));
+ memset(&port_cfg, 0, sizeof(port_cfg));
+
+ if (!afe_config) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ pr_err("%s: port_id 0x%x rate %d\n", __func__, port_id, rate);
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Invalid port 0x%x ret %d", __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ if ((port_id == RT_PROXY_DAI_001_RX) ||
+ (port_id == RT_PROXY_DAI_002_TX)) {
+ pr_err("%s: wrong port 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ }
+ if ((port_id == RT_PROXY_DAI_002_RX) ||
+ (port_id == RT_PROXY_DAI_001_TX))
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ if ((index >= 0) && (index < AFE_MAX_PORTS)) {
+ this_afe.afe_sample_rates[index] = rate;
+
+ if (this_afe.rt_cb)
+ this_afe.dev_acdb_id[index] = this_afe.rt_cb(port_id);
+ }
+
+ /* Also send the topology id here: */
+ afe_send_custom_topology(); /* One time call: only for first time */
+ afe_send_port_topology_id(port_id);
+
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Failed : Invalid Port id = 0x%x ret %d\n",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+ mutex_lock(&this_afe.afe_cmd_lock);
+
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ cfg_type = AFE_PARAM_ID_I2S_CONFIG;
+ break;
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ case AFE_PORT_ID_SECONDARY_PCM_RX:
+ case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ cfg_type = AFE_PARAM_ID_PCM_CONFIG;
+ break;
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ case MI2S_RX:
+ case MI2S_TX:
+ case AFE_PORT_ID_QUINARY_MI2S_RX:
+ case AFE_PORT_ID_QUINARY_MI2S_TX:
+ case AFE_PORT_ID_SENARY_MI2S_TX:
+ cfg_type = AFE_PARAM_ID_I2S_CONFIG;
+ break;
+ case HDMI_RX:
+ case DISPLAY_PORT_RX:
+ cfg_type = AFE_PARAM_ID_HDMI_CONFIG;
+ break;
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_RX:
+ case SLIMBUS_3_TX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_4_TX:
+ case SLIMBUS_5_RX:
+ case SLIMBUS_6_RX:
+ case SLIMBUS_6_TX:
+ case SLIMBUS_7_RX:
+ case SLIMBUS_7_TX:
+ case SLIMBUS_8_RX:
+ case SLIMBUS_8_TX:
+ cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG;
+ break;
+ case AFE_PORT_ID_USB_RX:
+ case AFE_PORT_ID_USB_TX:
+ cfg_type = AFE_PARAM_ID_USB_AUDIO_CONFIG;
+ break;
+ default:
+ pr_err("%s: Invalid port id 0x%x\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = cfg_type;
+ param_hdr.param_size = sizeof(union afe_port_config);
+ port_cfg = *afe_config;
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &port_cfg);
+ if (ret) {
+ pr_err("%s: AFE enable for port 0x%x opcode[0x%x]failed %d\n",
+ __func__, port_id, cfg_type, ret);
+ goto fail_cmd;
+ }
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = index;
+ start.hdr.opcode = AFE_PORT_CMD_DEVICE_START;
+ start.port_id = q6audio_get_port_id(port_id);
+ pr_debug("%s: cmd device start opcode[0x%x] port id[0x%x]\n",
+ __func__, start.hdr.opcode, start.port_id);
+
+ ret = afe_apr_send_pkt(&start, &this_afe.wait[index]);
+ if (ret) {
+ pr_err("%s: AFE enable for port 0x%x failed %d\n", __func__,
+ port_id, ret);
+ goto fail_cmd;
+ }
+
+fail_cmd:
+ mutex_unlock(&this_afe.afe_cmd_lock);
+ return ret;
+}
+
+int afe_loopback(u16 enable, u16 rx_port, u16 tx_port)
+{
+ struct afe_loopback_cfg_v1 lb_param = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (rx_port == MI2S_RX)
+ rx_port = AFE_PORT_ID_PRIMARY_MI2S_RX;
+ if (tx_port == MI2S_TX)
+ tx_port = AFE_PORT_ID_PRIMARY_MI2S_TX;
+
+ param_hdr.module_id = AFE_MODULE_LOOPBACK;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_LOOPBACK_CONFIG;
+ param_hdr.param_size = sizeof(struct afe_loopback_cfg_v1);
+
+ lb_param.dst_port_id = rx_port;
+ lb_param.routing_mode = LB_MODE_DEFAULT;
+ lb_param.enable = (enable ? 1 : 0);
+ lb_param.loopback_cfg_minor_version = AFE_API_VERSION_LOOPBACK_CONFIG;
+
+ ret = q6afe_pack_and_set_param_in_band(tx_port,
+ q6audio_get_port_index(tx_port),
+ param_hdr, (u8 *) &lb_param);
+ if (ret)
+ pr_err("%s: AFE loopback failed %d\n", __func__, ret);
+ return ret;
+}
+
+int afe_loopback_gain(u16 port_id, u16 volume)
+{
+ struct afe_loopback_gain_per_path_param set_param = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_afe_handle(this_afe.apr);
+ }
+
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Failed : Invalid Port id = 0x%x ret %d\n",
+ __func__, port_id, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ /* RX ports numbers are even .TX ports numbers are odd. */
+ if (port_id % 2 == 0) {
+ pr_err("%s: Failed : afe loopback gain only for TX ports. port_id %d\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ pr_debug("%s: port 0x%x volume %d\n", __func__, port_id, volume);
+
+ param_hdr.module_id = AFE_MODULE_LOOPBACK;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH;
+ param_hdr.param_size = sizeof(struct afe_loopback_gain_per_path_param);
+ set_param.rx_port_id = port_id;
+ set_param.gain = volume;
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &set_param);
+ if (ret)
+ pr_err("%s: AFE param set failed for port 0x%x ret %d\n",
+ __func__, port_id, ret);
+
+fail_cmd:
+ return ret;
+}
+
+int afe_pseudo_port_start_nowait(u16 port_id)
+{
+ struct afe_pseudoport_start_command start;
+ int ret = 0;
+
+ pr_debug("%s: port_id=0x%x\n", __func__, port_id);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE APR is not registered\n", __func__);
+ return -ENODEV;
+ }
+
+
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = 0;
+ start.hdr.opcode = AFE_PSEUDOPORT_CMD_START;
+ start.port_id = port_id;
+ start.timing = 1;
+
+ ret = afe_apr_send_pkt(&start, NULL);
+ if (ret) {
+ pr_err("%s: AFE enable for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ return ret;
+ }
+ return 0;
+}
+
+int afe_start_pseudo_port(u16 port_id)
+{
+ int ret = 0;
+ struct afe_pseudoport_start_command start;
+ int index = 0;
+
+ pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Invalid port 0x%x ret %d",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = 0;
+ start.hdr.opcode = AFE_PSEUDOPORT_CMD_START;
+ start.port_id = port_id;
+ start.timing = 1;
+ start.hdr.token = index;
+
+ ret = afe_apr_send_pkt(&start, &this_afe.wait[index]);
+ if (ret)
+ pr_err("%s: AFE enable for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ return ret;
+}
+
+int afe_pseudo_port_stop_nowait(u16 port_id)
+{
+ int ret = 0;
+ struct afe_pseudoport_stop_command stop;
+ int index = 0;
+
+ pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE is already closed\n", __func__);
+ return -EINVAL;
+ }
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Invalid port 0x%x ret %d",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+ stop.hdr.token = index;
+
+ ret = afe_apr_send_pkt(&stop, NULL);
+ if (ret)
+ pr_err("%s: AFE close failed %d\n", __func__, ret);
+
+ return ret;
+}
+
+int afe_port_group_set_param(u16 group_id,
+ union afe_port_group_config *afe_group_config)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int cfg_type;
+ int ret;
+
+ if (!afe_group_config) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: group id: 0x%x\n", __func__, group_id);
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ switch (group_id) {
+ case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX:
+ case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX:
+ case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX:
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX:
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX:
+ cfg_type = AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG;
+ break;
+ default:
+ pr_err("%s: Invalid group id 0x%x\n", __func__, group_id);
+ return -EINVAL;
+ }
+
+ param_hdr.module_id = AFE_MODULE_GROUP_DEVICE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = cfg_type;
+ param_hdr.param_size = sizeof(union afe_port_group_config);
+
+ ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr,
+ (u8 *) afe_group_config);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_GROUP_DEVICE_CFG failed %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int afe_port_group_mi2s_set_param(u16 group_id,
+ struct afe_param_id_group_device_i2s_cfg_v1 *afe_group_config)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int cfg_type;
+ int ret;
+
+ if (!afe_group_config) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: group id: 0x%x\n", __func__, group_id);
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ switch (group_id) {
+ case AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_RX:
+ case AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_TX:
+ case AFE_GROUP_DEVICE_ID_TERTIARY_MI2S_RX:
+ case AFE_GROUP_DEVICE_ID_TERTIARY_MI2S_TX:
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_MI2S_RX:
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_MI2S_TX:
+ cfg_type = AFE_PARAM_ID_GROUP_DEVICE_I2S_CONFIG;
+ break;
+ default:
+ pr_err("%s: Invalid group id 0x%x\n", __func__, group_id);
+ return -EINVAL;
+ }
+
+ param_hdr.module_id = AFE_MODULE_GROUP_DEVICE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = cfg_type;
+ param_hdr.param_size =
+ sizeof(struct afe_param_id_group_device_i2s_cfg_v1);
+
+ ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr,
+ (u8 *) afe_group_config);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_GROUP_DEVICE_CFG failed %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static atomic_t mi2s_gp_en_ref[IDX_GROUP_MI2S_MAX];
+static int afe_get_mi2s_group_idx(u16 group_id)
+{
+ int gp_idx = -1;
+
+ switch (group_id) {
+ case AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_RX:
+ gp_idx = IDX_GROUP_SECONDARY_MI2S_RX;
+ break;
+ case AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_TX:
+ gp_idx = IDX_GROUP_SECONDARY_MI2S_TX;
+ break;
+ case AFE_GROUP_DEVICE_ID_TERTIARY_MI2S_RX:
+ gp_idx = IDX_GROUP_TERTIARY_MI2S_RX;
+ break;
+ case AFE_GROUP_DEVICE_ID_TERTIARY_MI2S_TX:
+ gp_idx = IDX_GROUP_TERTIARY_MI2S_TX;
+ break;
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_MI2S_RX:
+ gp_idx = IDX_GROUP_QUATERNARY_MI2S_RX;
+ break;
+ case AFE_GROUP_DEVICE_ID_QUATERNARY_MI2S_TX:
+ gp_idx = IDX_GROUP_QUATERNARY_MI2S_TX;
+ break;
+ default:
+ break;
+ }
+
+ return gp_idx;
+}
+
+int afe_port_group_mi2s_enable(u16 group_id,
+ union afe_port_group_mi2s_config *afe_group_config,
+ u16 enable)
+{
+ struct afe_param_id_group_device_enable group_enable = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+ int gp_idx;
+
+ pr_debug("%s: group id: 0x%x enable: %d\n", __func__,
+ group_id, enable);
+
+ gp_idx = afe_get_mi2s_group_idx(group_id);
+
+ if ((gp_idx >= 0) && (gp_idx < IDX_GROUP_MI2S_MAX)) {
+
+ atomic_t *gp_ref = &mi2s_gp_en_ref[gp_idx];
+
+ if (enable)
+ atomic_inc(gp_ref);
+ else
+ atomic_dec(gp_ref);
+
+ if ((enable) && (atomic_read(gp_ref) > 1)) {
+ pr_err("%s: this TDM group is enabled already %d refs_cnt %d\n",
+ __func__, group_id, atomic_read(gp_ref));
+ goto rtn;
+ }
+
+ if ((!enable) && (atomic_read(gp_ref) > 0)) {
+ pr_err("%s: this TDM group will be disabled in last call %d refs_cnt %d\n",
+ __func__, group_id, atomic_read(gp_ref));
+ goto rtn;
+ }
+ }
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ if (enable) {
+ ret = afe_port_group_mi2s_set_param(
+ group_id, &afe_group_config->i2s_cfg);
+ if (ret < 0) {
+ pr_err("%s: afe send failed %d\n", __func__, ret);
+ return ret;
+ }
+ }
+
+ param_hdr.module_id = AFE_MODULE_GROUP_DEVICE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_GROUP_DEVICE_ENABLE;
+ param_hdr.param_size = sizeof(struct afe_group_device_enable);
+ group_enable.group_id = group_id;
+ group_enable.enable = enable;
+
+ ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr,
+ (u8 *) &group_enable);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_GROUP_DEVICE_ENABLE failed %d\n",
+ __func__, ret);
+
+rtn:
+ return ret;
+}
+
+int afe_port_group_enable(u16 group_id,
+ union afe_port_group_config *afe_group_config,
+ u16 enable)
+{
+ struct afe_group_device_enable group_enable = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+ int gp_idx;
+
+ pr_debug("%s: group id: 0x%x enable: %d\n", __func__,
+ group_id, enable);
+
+ gp_idx = afe_get_tdm_group_idx(group_id);
+
+ if ((gp_idx >= 0) && (gp_idx < IDX_GROUP_TDM_MAX)) {
+
+ atomic_t *gp_ref = &tdm_gp_en_ref[gp_idx];
+
+ if (enable)
+ atomic_inc(gp_ref);
+ else
+ atomic_dec(gp_ref);
+
+ if ((enable) && (atomic_read(gp_ref) > 1)) {
+ pr_err("%s: this TDM group is enabled already %d refs_cnt %d\n",
+ __func__, group_id, atomic_read(gp_ref));
+ goto rtn;
+ }
+
+ if ((!enable) && (atomic_read(gp_ref) > 0)) {
+ pr_err("%s: this TDM group will be disabled in last call %d refs_cnt %d\n",
+ __func__, group_id, atomic_read(gp_ref));
+ goto rtn;
+ }
+ }
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ if (enable) {
+ ret = afe_port_group_set_param(group_id, afe_group_config);
+ if (ret < 0) {
+ pr_err("%s: afe send failed %d\n", __func__, ret);
+ return ret;
+ }
+ }
+
+ param_hdr.module_id = AFE_MODULE_GROUP_DEVICE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_GROUP_DEVICE_ENABLE;
+ param_hdr.param_size = sizeof(struct afe_group_device_enable);
+ group_enable.group_id = group_id;
+ group_enable.enable = enable;
+
+ ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr,
+ (u8 *) &group_enable);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_GROUP_DEVICE_ENABLE failed %d\n",
+ __func__, ret);
+
+rtn:
+
+ return ret;
+}
+
+int afe_stop_pseudo_port(u16 port_id)
+{
+ int ret = 0;
+ struct afe_pseudoport_stop_command stop;
+ int index = 0;
+
+ pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE is already closed\n", __func__);
+ return -EINVAL;
+ }
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Invalid port 0x%x ret %d\n",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+ stop.hdr.token = index;
+
+ ret = afe_apr_send_pkt(&stop, &this_afe.wait[index]);
+ if (ret)
+ pr_err("%s: AFE close failed %d\n", __func__, ret);
+
+ return ret;
+}
+
+uint32_t afe_req_mmap_handle(struct afe_audio_client *ac)
+{
+ return ac->mem_map_handle;
+}
+
+struct afe_audio_client *q6afe_audio_client_alloc(void *priv)
+{
+ struct afe_audio_client *ac;
+ int lcnt = 0;
+
+ ac = kzalloc(sizeof(struct afe_audio_client), GFP_KERNEL);
+ if (!ac) {
+ pr_err("%s: cannot allocate audio client for afe\n", __func__);
+ return NULL;
+ }
+ ac->priv = priv;
+
+ init_waitqueue_head(&ac->cmd_wait);
+ INIT_LIST_HEAD(&ac->port[0].mem_map_handle);
+ INIT_LIST_HEAD(&ac->port[1].mem_map_handle);
+ pr_debug("%s: mem_map_handle list init'ed\n", __func__);
+ mutex_init(&ac->cmd_lock);
+ for (lcnt = 0; lcnt <= OUT; lcnt++) {
+ mutex_init(&ac->port[lcnt].lock);
+ spin_lock_init(&ac->port[lcnt].dsp_lock);
+ }
+ atomic_set(&ac->cmd_state, 0);
+
+ return ac;
+}
+
+int q6afe_audio_client_buf_alloc_contiguous(unsigned int dir,
+ struct afe_audio_client *ac,
+ unsigned int bufsz,
+ unsigned int bufcnt)
+{
+ int cnt = 0;
+ int rc = 0;
+ struct afe_audio_buffer *buf;
+ size_t len;
+
+ if (!(ac) || ((dir != IN) && (dir != OUT))) {
+ pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: bufsz[%d]bufcnt[%d]\n",
+ __func__,
+ bufsz, bufcnt);
+
+ if (ac->port[dir].buf) {
+ pr_debug("%s: buffer already allocated\n", __func__);
+ return 0;
+ }
+ mutex_lock(&ac->cmd_lock);
+ buf = kzalloc(((sizeof(struct afe_audio_buffer))*bufcnt),
+ GFP_KERNEL);
+
+ if (!buf) {
+ pr_err("%s: null buf\n", __func__);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+
+ ac->port[dir].buf = buf;
+
+ rc = msm_audio_ion_alloc("afe_client", &buf[0].client,
+ &buf[0].handle, bufsz*bufcnt,
+ &buf[0].phys, &len,
+ &buf[0].data);
+ if (rc) {
+ pr_err("%s: audio ION alloc failed, rc = %d\n",
+ __func__, rc);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+
+ buf[0].used = dir ^ 1;
+ buf[0].size = bufsz;
+ buf[0].actual_size = bufsz;
+ cnt = 1;
+ while (cnt < bufcnt) {
+ if (bufsz > 0) {
+ buf[cnt].data = buf[0].data + (cnt * bufsz);
+ buf[cnt].phys = buf[0].phys + (cnt * bufsz);
+ if (!buf[cnt].data) {
+ pr_err("%s: Buf alloc failed\n",
+ __func__);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+ buf[cnt].used = dir ^ 1;
+ buf[cnt].size = bufsz;
+ buf[cnt].actual_size = bufsz;
+ pr_debug("%s: data[%pK]phys[%pK][%pK]\n", __func__,
+ buf[cnt].data,
+ &buf[cnt].phys,
+ &buf[cnt].phys);
+ }
+ cnt++;
+ }
+ ac->port[dir].max_buf_cnt = cnt;
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+fail:
+ pr_err("%s: jump fail\n", __func__);
+ q6afe_audio_client_buf_free_contiguous(dir, ac);
+ return -EINVAL;
+}
+
+int afe_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz,
+ struct afe_audio_client *ac)
+{
+ int ret = 0;
+
+ mutex_lock(&this_afe.afe_cmd_lock);
+ ac->mem_map_handle = 0;
+ ret = afe_cmd_memory_map(dma_addr_p, dma_buf_sz);
+ if (ret < 0) {
+ pr_err("%s: afe_cmd_memory_map failed %d\n",
+ __func__, ret);
+
+ mutex_unlock(&this_afe.afe_cmd_lock);
+ return ret;
+ }
+ ac->mem_map_handle = this_afe.mmap_handle;
+ mutex_unlock(&this_afe.afe_cmd_lock);
+
+ return ret;
+}
+
+int afe_cmd_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz)
+{
+ int ret = 0;
+ int cmd_size = 0;
+ void *payload = NULL;
+ void *mmap_region_cmd = NULL;
+ struct afe_service_cmd_shared_mem_map_regions *mregion = NULL;
+ struct afe_service_shared_map_region_payload *mregion_pl = NULL;
+ int index = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_afe_handle(this_afe.apr);
+ }
+ if (dma_buf_sz % SZ_4K != 0) {
+ /*
+ * The memory allocated by msm_audio_ion_alloc is always 4kB
+ * aligned, ADSP expects the size to be 4kB aligned as well
+ * so re-adjusts the buffer size before passing to ADSP.
+ */
+ dma_buf_sz = PAGE_ALIGN(dma_buf_sz);
+ }
+
+ cmd_size = sizeof(struct afe_service_cmd_shared_mem_map_regions) \
+ + sizeof(struct afe_service_shared_map_region_payload);
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (!mmap_region_cmd) {
+ pr_err("%s: allocate mmap_region_cmd failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ mregion = (struct afe_service_cmd_shared_mem_map_regions *)
+ mmap_region_cmd;
+ mregion->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mregion->hdr.pkt_size = cmd_size;
+ mregion->hdr.src_port = 0;
+ mregion->hdr.dest_port = 0;
+ mregion->hdr.token = 0;
+ mregion->hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS;
+ mregion->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+ mregion->num_regions = 1;
+ mregion->property_flag = 0x00;
+ /* Todo */
+ index = mregion->hdr.token = IDX_RSVD_2;
+
+ payload = ((u8 *) mmap_region_cmd +
+ sizeof(struct afe_service_cmd_shared_mem_map_regions));
+
+ mregion_pl = (struct afe_service_shared_map_region_payload *)payload;
+
+ mregion_pl->shm_addr_lsw = lower_32_bits(dma_addr_p);
+ mregion_pl->shm_addr_msw = msm_audio_populate_upper_32_bits(dma_addr_p);
+ mregion_pl->mem_size_bytes = dma_buf_sz;
+
+ pr_debug("%s: dma_addr_p 0x%pK , size %d\n", __func__,
+ &dma_addr_p, dma_buf_sz);
+ atomic_set(&this_afe.state, 1);
+ atomic_set(&this_afe.status, 0);
+ this_afe.mmap_handle = 0;
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) mmap_region_cmd);
+ if (ret < 0) {
+ pr_err("%s: AFE memory map cmd failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait[index],
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ if (atomic_read(&this_afe.status) > 0) {
+ pr_err("%s: config cmd failed [%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&this_afe.status)));
+ ret = adsp_err_get_lnx_err_code(
+ atomic_read(&this_afe.status));
+ goto fail_cmd;
+ }
+
+ kfree(mmap_region_cmd);
+ return 0;
+fail_cmd:
+ kfree(mmap_region_cmd);
+ pr_err("%s: fail_cmd\n", __func__);
+ return ret;
+}
+
+int afe_cmd_memory_map_nowait(int port_id, phys_addr_t dma_addr_p,
+ u32 dma_buf_sz)
+{
+ int ret = 0;
+ int cmd_size = 0;
+ void *payload = NULL;
+ void *mmap_region_cmd = NULL;
+ struct afe_service_cmd_shared_mem_map_regions *mregion = NULL;
+ struct afe_service_shared_map_region_payload *mregion_pl = NULL;
+ int index = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_afe_handle(this_afe.apr);
+ }
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Invalid port 0x%x ret %d",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ cmd_size = sizeof(struct afe_service_cmd_shared_mem_map_regions)
+ + sizeof(struct afe_service_shared_map_region_payload);
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (!mmap_region_cmd) {
+ pr_err("%s: allocate mmap_region_cmd failed\n", __func__);
+ return -ENOMEM;
+ }
+ mregion = (struct afe_service_cmd_shared_mem_map_regions *)
+ mmap_region_cmd;
+ mregion->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mregion->hdr.pkt_size = sizeof(mregion);
+ mregion->hdr.src_port = 0;
+ mregion->hdr.dest_port = 0;
+ mregion->hdr.token = 0;
+ mregion->hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS;
+ mregion->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+ mregion->num_regions = 1;
+ mregion->property_flag = 0x00;
+
+ payload = ((u8 *) mmap_region_cmd +
+ sizeof(struct afe_service_cmd_shared_mem_map_regions));
+ mregion_pl = (struct afe_service_shared_map_region_payload *)payload;
+
+ mregion_pl->shm_addr_lsw = lower_32_bits(dma_addr_p);
+ mregion_pl->shm_addr_msw = msm_audio_populate_upper_32_bits(dma_addr_p);
+ mregion_pl->mem_size_bytes = dma_buf_sz;
+
+ ret = afe_apr_send_pkt(mmap_region_cmd, NULL);
+ if (ret)
+ pr_err("%s: AFE memory map cmd failed %d\n",
+ __func__, ret);
+ kfree(mmap_region_cmd);
+ return ret;
+}
+int q6afe_audio_client_buf_free_contiguous(unsigned int dir,
+ struct afe_audio_client *ac)
+{
+ struct afe_audio_port_data *port;
+ int cnt = 0;
+ mutex_lock(&ac->cmd_lock);
+ port = &ac->port[dir];
+ if (!port->buf) {
+ pr_err("%s: buf is null\n", __func__);
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+ }
+ cnt = port->max_buf_cnt - 1;
+
+ if (port->buf[0].data) {
+ pr_debug("%s: data[%pK]phys[%pK][%pK] , client[%pK] handle[%pK]\n",
+ __func__,
+ port->buf[0].data,
+ &port->buf[0].phys,
+ &port->buf[0].phys,
+ port->buf[0].client,
+ port->buf[0].handle);
+ msm_audio_ion_free(port->buf[0].client, port->buf[0].handle);
+ port->buf[0].client = NULL;
+ port->buf[0].handle = NULL;
+ }
+
+ while (cnt >= 0) {
+ port->buf[cnt].data = NULL;
+ port->buf[cnt].phys = 0;
+ cnt--;
+ }
+ port->max_buf_cnt = 0;
+ kfree(port->buf);
+ port->buf = NULL;
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+}
+
+void q6afe_audio_client_free(struct afe_audio_client *ac)
+{
+ int loopcnt;
+ struct afe_audio_port_data *port;
+ if (!ac) {
+ pr_err("%s: audio client is NULL\n", __func__);
+ return;
+ }
+ for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
+ port = &ac->port[loopcnt];
+ if (!port->buf)
+ continue;
+ pr_debug("%s: loopcnt = %d\n", __func__, loopcnt);
+ q6afe_audio_client_buf_free_contiguous(loopcnt, ac);
+ }
+ kfree(ac);
+ return;
+}
+
+int afe_cmd_memory_unmap(u32 mem_map_handle)
+{
+ int ret = 0;
+ struct afe_service_cmd_shared_mem_unmap_regions mregion;
+ int index = 0;
+
+ pr_debug("%s: handle 0x%x\n", __func__, mem_map_handle);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_afe_handle(this_afe.apr);
+ }
+
+ mregion.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mregion.hdr.pkt_size = sizeof(mregion);
+ mregion.hdr.src_port = 0;
+ mregion.hdr.dest_port = 0;
+ mregion.hdr.token = 0;
+ mregion.hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS;
+ mregion.mem_map_handle = mem_map_handle;
+
+ /* Todo */
+ index = mregion.hdr.token = IDX_RSVD_2;
+
+ atomic_set(&this_afe.status, 0);
+ ret = afe_apr_send_pkt(&mregion, &this_afe.wait[index]);
+ if (ret)
+ pr_err("%s: AFE memory unmap cmd failed %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+int afe_cmd_memory_unmap_nowait(u32 mem_map_handle)
+{
+ int ret = 0;
+ struct afe_service_cmd_shared_mem_unmap_regions mregion;
+
+ pr_debug("%s: handle 0x%x\n", __func__, mem_map_handle);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_afe_handle(this_afe.apr);
+ }
+
+ mregion.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mregion.hdr.pkt_size = sizeof(mregion);
+ mregion.hdr.src_port = 0;
+ mregion.hdr.dest_port = 0;
+ mregion.hdr.token = 0;
+ mregion.hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS;
+ mregion.mem_map_handle = mem_map_handle;
+
+ ret = afe_apr_send_pkt(&mregion, NULL);
+ if (ret)
+ pr_err("%s: AFE memory unmap cmd failed %d\n",
+ __func__, ret);
+ return ret;
+}
+
+int afe_register_get_events(u16 port_id,
+ void (*cb) (uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv),
+ void *private_data)
+{
+ int ret = 0;
+ struct afe_service_cmd_register_rt_port_driver rtproxy;
+
+ pr_debug("%s: port_id: 0x%x\n", __func__, port_id);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_afe_handle(this_afe.apr);
+ }
+ if ((port_id == RT_PROXY_DAI_002_RX) ||
+ (port_id == RT_PROXY_DAI_001_TX)) {
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ } else {
+ pr_err("%s: wrong port id 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ }
+
+ if (port_id == RT_PROXY_PORT_001_TX) {
+ this_afe.tx_cb = cb;
+ this_afe.tx_private_data = private_data;
+ } else if (port_id == RT_PROXY_PORT_001_RX) {
+ this_afe.rx_cb = cb;
+ this_afe.rx_private_data = private_data;
+ }
+
+ rtproxy.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ rtproxy.hdr.pkt_size = sizeof(rtproxy);
+ rtproxy.hdr.src_port = 1;
+ rtproxy.hdr.dest_port = 1;
+ rtproxy.hdr.opcode = AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER;
+ rtproxy.port_id = port_id;
+ rtproxy.reserved = 0;
+
+ ret = afe_apr_send_pkt(&rtproxy, NULL);
+ if (ret)
+ pr_err("%s: AFE reg. rtproxy_event failed %d\n",
+ __func__, ret);
+ return ret;
+}
+
+int afe_unregister_get_events(u16 port_id)
+{
+ int ret = 0;
+ struct afe_service_cmd_unregister_rt_port_driver rtproxy;
+ int index = 0;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_afe_handle(this_afe.apr);
+ }
+
+ if ((port_id == RT_PROXY_DAI_002_RX) ||
+ (port_id == RT_PROXY_DAI_001_TX)) {
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ } else {
+ pr_err("%s: wrong port id 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ }
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_err("%s: Invalid port 0x%x ret %d", __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ rtproxy.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ rtproxy.hdr.pkt_size = sizeof(rtproxy);
+ rtproxy.hdr.src_port = 0;
+ rtproxy.hdr.dest_port = 0;
+ rtproxy.hdr.token = 0;
+ rtproxy.hdr.opcode = AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER;
+ rtproxy.port_id = port_id;
+ rtproxy.reserved = 0;
+
+ rtproxy.hdr.token = index;
+
+ if (port_id == RT_PROXY_PORT_001_TX) {
+ this_afe.tx_cb = NULL;
+ this_afe.tx_private_data = NULL;
+ } else if (port_id == RT_PROXY_PORT_001_RX) {
+ this_afe.rx_cb = NULL;
+ this_afe.rx_private_data = NULL;
+ }
+
+ ret = afe_apr_send_pkt(&rtproxy, &this_afe.wait[index]);
+ if (ret)
+ pr_err("%s: AFE enable Unreg. rtproxy_event failed %d\n",
+ __func__, ret);
+ return ret;
+}
+
+int afe_rt_proxy_port_write(phys_addr_t buf_addr_p,
+ u32 mem_map_handle, int bytes)
+{
+ int ret = 0;
+ struct afe_port_data_cmd_rt_proxy_port_write_v2 afecmd_wr;
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: register to AFE is not done\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ pr_debug("%s: buf_addr_p = 0x%pK bytes = %d\n", __func__,
+ &buf_addr_p, bytes);
+
+ afecmd_wr.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ afecmd_wr.hdr.pkt_size = sizeof(afecmd_wr);
+ afecmd_wr.hdr.src_port = 0;
+ afecmd_wr.hdr.dest_port = 0;
+ afecmd_wr.hdr.token = 0;
+ afecmd_wr.hdr.opcode = AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2;
+ afecmd_wr.port_id = RT_PROXY_PORT_001_TX;
+ afecmd_wr.buffer_address_lsw = lower_32_bits(buf_addr_p);
+ afecmd_wr.buffer_address_msw =
+ msm_audio_populate_upper_32_bits(buf_addr_p);
+ afecmd_wr.mem_map_handle = mem_map_handle;
+ afecmd_wr.available_bytes = bytes;
+ afecmd_wr.reserved = 0;
+
+ ret = afe_apr_send_pkt(&afecmd_wr, NULL);
+ if (ret)
+ pr_err("%s: AFE rtproxy write to port 0x%x failed %d\n",
+ __func__, afecmd_wr.port_id, ret);
+ return ret;
+
+}
+
+int afe_rt_proxy_port_read(phys_addr_t buf_addr_p,
+ u32 mem_map_handle, int bytes)
+{
+ int ret = 0;
+ struct afe_port_data_cmd_rt_proxy_port_read_v2 afecmd_rd;
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: register to AFE is not done\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ pr_debug("%s: buf_addr_p = 0x%pK bytes = %d\n", __func__,
+ &buf_addr_p, bytes);
+
+ afecmd_rd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ afecmd_rd.hdr.pkt_size = sizeof(afecmd_rd);
+ afecmd_rd.hdr.src_port = 0;
+ afecmd_rd.hdr.dest_port = 0;
+ afecmd_rd.hdr.token = 0;
+ afecmd_rd.hdr.opcode = AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2;
+ afecmd_rd.port_id = RT_PROXY_PORT_001_RX;
+ afecmd_rd.buffer_address_lsw = lower_32_bits(buf_addr_p);
+ afecmd_rd.buffer_address_msw =
+ msm_audio_populate_upper_32_bits(buf_addr_p);
+ afecmd_rd.available_bytes = bytes;
+ afecmd_rd.mem_map_handle = mem_map_handle;
+
+ ret = afe_apr_send_pkt(&afecmd_rd, NULL);
+ if (ret)
+ pr_err("%s: AFE rtproxy read cmd to port 0x%x failed %d\n",
+ __func__, afecmd_rd.port_id, ret);
+ return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_afelb;
+static struct dentry *debugfs_afelb_gain;
+
+static int afe_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ pr_info("%s: debug intf %s\n", __func__, (char *) file->private_data);
+ return 0;
+}
+
+static int afe_get_parameters(char *buf, long int *param1, int num_of_par)
+{
+ char *token;
+ int base, cnt;
+
+ token = strsep(&buf, " ");
+
+ for (cnt = 0; cnt < num_of_par; cnt++) {
+ if (token != NULL) {
+ if ((token[1] == 'x') || (token[1] == 'X'))
+ base = 16;
+ else
+ base = 10;
+
+ if (kstrtoul(token, base, &param1[cnt]) != 0) {
+ pr_err("%s: kstrtoul failed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ token = strsep(&buf, " ");
+ } else {
+ pr_err("%s: token NULL\n", __func__);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+#define AFE_LOOPBACK_ON (1)
+#define AFE_LOOPBACK_OFF (0)
+static ssize_t afe_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *lb_str = filp->private_data;
+ char lbuf[32];
+ int rc;
+ unsigned long param[5];
+
+ if (cnt > sizeof(lbuf) - 1) {
+ pr_err("%s: cnt %zd size %zd\n", __func__, cnt, sizeof(lbuf)-1);
+ return -EINVAL;
+ }
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc) {
+ pr_err("%s: copy from user failed %d\n", __func__, rc);
+ return -EFAULT;
+ }
+
+ lbuf[cnt] = '\0';
+
+ if (!strcmp(lb_str, "afe_loopback")) {
+ rc = afe_get_parameters(lbuf, param, 3);
+ if (!rc) {
+ pr_info("%s: %lu %lu %lu\n", lb_str, param[0], param[1],
+ param[2]);
+
+ if ((param[0] != AFE_LOOPBACK_ON) && (param[0] !=
+ AFE_LOOPBACK_OFF)) {
+ pr_err("%s: Error, parameter 0 incorrect\n",
+ __func__);
+ rc = -EINVAL;
+ goto afe_error;
+ }
+ if ((q6audio_validate_port(param[1]) < 0) ||
+ (q6audio_validate_port(param[2])) < 0) {
+ pr_err("%s: Error, invalid afe port\n",
+ __func__);
+ }
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Error, AFE not opened\n", __func__);
+ rc = -EINVAL;
+ } else {
+ rc = afe_loopback(param[0], param[1], param[2]);
+ }
+ } else {
+ pr_err("%s: Error, invalid parameters\n", __func__);
+ rc = -EINVAL;
+ }
+
+ } else if (!strcmp(lb_str, "afe_loopback_gain")) {
+ rc = afe_get_parameters(lbuf, param, 2);
+ if (!rc) {
+ pr_info("%s: %s %lu %lu\n",
+ __func__, lb_str, param[0], param[1]);
+
+ rc = q6audio_validate_port(param[0]);
+ if (rc < 0) {
+ pr_err("%s: Error, invalid afe port %d %lu\n",
+ __func__, rc, param[0]);
+ rc = -EINVAL;
+ goto afe_error;
+ }
+
+ if (param[1] > 100) {
+ pr_err("%s: Error, volume shoud be 0 to 100 percentage param = %lu\n",
+ __func__, param[1]);
+ rc = -EINVAL;
+ goto afe_error;
+ }
+
+ param[1] = (Q6AFE_MAX_VOLUME * param[1]) / 100;
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Error, AFE not opened\n", __func__);
+ rc = -EINVAL;
+ } else {
+ rc = afe_loopback_gain(param[0], param[1]);
+ }
+ } else {
+ pr_err("%s: Error, invalid parameters\n", __func__);
+ rc = -EINVAL;
+ }
+ }
+
+afe_error:
+ if (rc == 0)
+ rc = cnt;
+ else
+ pr_err("%s: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+static const struct file_operations afe_debug_fops = {
+ .open = afe_debug_open,
+ .write = afe_debug_write
+};
+
+static void config_debug_fs_init(void)
+{
+ debugfs_afelb = debugfs_create_file("afe_loopback",
+ S_IRUGO | S_IWUSR | S_IWGRP, NULL, (void *) "afe_loopback",
+ &afe_debug_fops);
+
+ debugfs_afelb_gain = debugfs_create_file("afe_loopback_gain",
+ S_IRUGO | S_IWUSR | S_IWGRP, NULL, (void *) "afe_loopback_gain",
+ &afe_debug_fops);
+}
+static void config_debug_fs_exit(void)
+{
+ if (debugfs_afelb)
+ debugfs_remove(debugfs_afelb);
+ if (debugfs_afelb_gain)
+ debugfs_remove(debugfs_afelb_gain);
+}
+#else
+static void config_debug_fs_init(void)
+{
+ return;
+}
+static void config_debug_fs_exit(void)
+{
+ return;
+}
+#endif
+
+void afe_set_dtmf_gen_rx_portid(u16 port_id, int set)
+{
+ if (set)
+ this_afe.dtmf_gen_rx_portid = port_id;
+ else if (this_afe.dtmf_gen_rx_portid == port_id)
+ this_afe.dtmf_gen_rx_portid = -1;
+}
+
+int afe_dtmf_generate_rx(int64_t duration_in_ms,
+ uint16_t high_freq,
+ uint16_t low_freq, uint16_t gain)
+{
+ int ret = 0;
+ int index = 0;
+ struct afe_dtmf_generation_command cmd_dtmf;
+
+ pr_debug("%s: DTMF AFE Gen\n", __func__);
+
+ if (afe_validate_port(this_afe.dtmf_gen_rx_portid) < 0) {
+ pr_err("%s: Failed : Invalid Port id = 0x%x\n",
+ __func__, this_afe.dtmf_gen_rx_portid);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_afe_handle(this_afe.apr);
+ }
+
+ pr_debug("%s: dur=%lld: hfreq=%d lfreq=%d gain=%d portid=0x%x\n",
+ __func__,
+ duration_in_ms, high_freq, low_freq, gain,
+ this_afe.dtmf_gen_rx_portid);
+
+ cmd_dtmf.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cmd_dtmf.hdr.pkt_size = sizeof(cmd_dtmf);
+ cmd_dtmf.hdr.src_port = 0;
+ cmd_dtmf.hdr.dest_port = 0;
+ cmd_dtmf.hdr.token = 0;
+ cmd_dtmf.hdr.opcode = AFE_PORTS_CMD_DTMF_CTL;
+ cmd_dtmf.duration_in_ms = duration_in_ms;
+ cmd_dtmf.high_freq = high_freq;
+ cmd_dtmf.low_freq = low_freq;
+ cmd_dtmf.gain = gain;
+ cmd_dtmf.num_ports = 1;
+ cmd_dtmf.port_ids = q6audio_get_port_id(this_afe.dtmf_gen_rx_portid);
+
+ atomic_set(&this_afe.state, 1);
+ atomic_set(&this_afe.status, 0);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &cmd_dtmf);
+ if (ret < 0) {
+ pr_err("%s: AFE DTMF failed for num_ports:%d ids:0x%x\n",
+ __func__, cmd_dtmf.num_ports, cmd_dtmf.port_ids);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ index = q6audio_get_port_index(this_afe.dtmf_gen_rx_portid);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ ret = wait_event_timeout(this_afe.wait[index],
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ if (atomic_read(&this_afe.status) > 0) {
+ pr_err("%s: config cmd failed [%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&this_afe.status)));
+ ret = adsp_err_get_lnx_err_code(
+ atomic_read(&this_afe.status));
+ goto fail_cmd;
+ }
+ return 0;
+
+fail_cmd:
+ pr_err("%s: failed %d\n", __func__, ret);
+ return ret;
+}
+
+static int afe_sidetone_iir(u16 tx_port_id)
+{
+ int ret;
+ uint16_t size = 0;
+ int cal_index = AFE_SIDETONE_IIR_CAL;
+ int iir_pregain = 0;
+ int iir_num_biquad_stages = 0;
+ int iir_enable;
+ struct cal_block_data *cal_block;
+ int mid;
+ struct afe_mod_enable_param enable = {0};
+ struct afe_sidetone_iir_filter_config_params filter_data = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ u8 *packed_param_data = NULL;
+ u32 packed_param_size = 0;
+ u32 single_param_size = 0;
+ struct audio_cal_info_sidetone_iir *st_iir_cal_info = NULL;
+
+ if (this_afe.cal_data[cal_index] == NULL) {
+ pr_err("%s: cal data is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ mutex_lock(&this_afe.cal_data[cal_index]->lock);
+ cal_block = cal_utils_get_only_cal_block(this_afe.cal_data[cal_index]);
+ if (cal_block == NULL) {
+ pr_err("%s: cal_block not found\n ", __func__);
+ mutex_unlock(&this_afe.cal_data[cal_index]->lock);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Cache data from cal block while inside lock to reduce locked time */
+ st_iir_cal_info =
+ (struct audio_cal_info_sidetone_iir *) cal_block->cal_info;
+ iir_pregain = st_iir_cal_info->pregain;
+ iir_enable = st_iir_cal_info->iir_enable;
+ iir_num_biquad_stages = st_iir_cal_info->num_biquad_stages;
+ mid = st_iir_cal_info->mid;
+
+ /*
+ * calculate the actual size of payload based on no of stages
+ * enabled in calibration
+ */
+ size = (MAX_SIDETONE_IIR_DATA_SIZE / MAX_NO_IIR_FILTER_STAGE) *
+ iir_num_biquad_stages;
+ /*
+ * For an odd number of stages, 2 bytes of padding are
+ * required at the end of the payload.
+ */
+ if (iir_num_biquad_stages % 2) {
+ pr_debug("%s: adding 2 to size:%d\n", __func__, size);
+ size = size + 2;
+ }
+ memcpy(&filter_data.iir_config, &st_iir_cal_info->iir_config, size);
+ mutex_unlock(&this_afe.cal_data[cal_index]->lock);
+
+ packed_param_size =
+ sizeof(param_hdr) * 2 + sizeof(enable) + sizeof(filter_data);
+ packed_param_data = kzalloc(packed_param_size, GFP_KERNEL);
+ if (!packed_param_data)
+ return -ENOMEM;
+ packed_param_size = 0;
+
+ /*
+ * Set IIR enable params
+ */
+ param_hdr.module_id = mid;
+ param_hdr.param_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_ENABLE;
+ param_hdr.param_size = sizeof(enable);
+ enable.enable = iir_enable;
+ ret = q6common_pack_pp_params(packed_param_data, &param_hdr,
+ (u8 *) &enable, &single_param_size);
+ if (ret) {
+ pr_err("%s: Failed to pack param data, error %d\n", __func__,
+ ret);
+ goto done;
+ }
+ packed_param_size += single_param_size;
+
+ /*
+ * Set IIR filter config params
+ */
+ param_hdr.module_id = mid;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_SIDETONE_IIR_FILTER_CONFIG;
+ param_hdr.param_size = sizeof(filter_data.num_biquad_stages) +
+ sizeof(filter_data.pregain) + size;
+ filter_data.num_biquad_stages = iir_num_biquad_stages;
+ filter_data.pregain = iir_pregain;
+ ret = q6common_pack_pp_params(packed_param_data + packed_param_size,
+ &param_hdr, (u8 *) &filter_data,
+ &single_param_size);
+ if (ret) {
+ pr_err("%s: Failed to pack param data, error %d\n", __func__,
+ ret);
+ goto done;
+ }
+ packed_param_size += single_param_size;
+
+ pr_debug("%s: tx(0x%x)mid(0x%x)iir_en(%d)stg(%d)gain(0x%x)size(%d)\n",
+ __func__, tx_port_id, mid, enable.enable,
+ filter_data.num_biquad_stages, filter_data.pregain,
+ param_hdr.param_size);
+
+ ret = q6afe_set_params(tx_port_id, q6audio_get_port_index(tx_port_id),
+ NULL, packed_param_data, packed_param_size);
+ if (ret)
+ pr_err("%s: AFE sidetone failed for tx_port(0x%x)\n",
+ __func__, tx_port_id);
+
+done:
+ kfree(packed_param_data);
+ return ret;
+}
+
+static int afe_sidetone(u16 tx_port_id, u16 rx_port_id, bool enable)
+{
+ int ret;
+ int cal_index = AFE_SIDETONE_CAL;
+ int sidetone_gain;
+ int sidetone_enable;
+ struct cal_block_data *cal_block;
+ int mid = 0;
+ struct afe_loopback_sidetone_gain gain_data = {0};
+ struct loopback_cfg_data cfg_data = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ u8 *packed_param_data = NULL;
+ u32 packed_param_size = 0;
+ u32 single_param_size = 0;
+ struct audio_cal_info_sidetone *st_cal_info = NULL;
+
+ if (this_afe.cal_data[cal_index] == NULL) {
+ pr_err("%s: cal data is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ mutex_lock(&this_afe.cal_data[cal_index]->lock);
+ cal_block = cal_utils_get_only_cal_block(this_afe.cal_data[cal_index]);
+ if (cal_block == NULL) {
+ pr_err("%s: cal_block not found\n", __func__);
+ mutex_unlock(&this_afe.cal_data[cal_index]->lock);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Cache data from cal block while inside lock to reduce locked time */
+ st_cal_info = (struct audio_cal_info_sidetone *) cal_block->cal_info;
+ sidetone_gain = st_cal_info->gain;
+ sidetone_enable = st_cal_info->enable;
+ mid = st_cal_info->mid;
+ mutex_unlock(&this_afe.cal_data[cal_index]->lock);
+
+ packed_param_size =
+ sizeof(param_hdr) * 2 + sizeof(gain_data) + sizeof(cfg_data);
+ packed_param_data = kzalloc(packed_param_size, GFP_KERNEL);
+ if (!packed_param_data)
+ return -ENOMEM;
+ packed_param_size = 0;
+
+ /* Set gain data. */
+ param_hdr.module_id = AFE_MODULE_LOOPBACK;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH;
+ param_hdr.param_size = sizeof(struct afe_loopback_sidetone_gain);
+ gain_data.rx_port_id = rx_port_id;
+ gain_data.gain = sidetone_gain;
+ ret = q6common_pack_pp_params(packed_param_data, &param_hdr,
+ (u8 *) &gain_data, &single_param_size);
+ if (ret) {
+ pr_err("%s: Failed to pack param data, error %d\n", __func__,
+ ret);
+ goto done;
+ }
+ packed_param_size += single_param_size;
+
+ /* Set configuration data. */
+ param_hdr.module_id = AFE_MODULE_LOOPBACK;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_LOOPBACK_CONFIG;
+ param_hdr.param_size = sizeof(struct loopback_cfg_data);
+ cfg_data.loopback_cfg_minor_version = AFE_API_VERSION_LOOPBACK_CONFIG;
+ cfg_data.dst_port_id = rx_port_id;
+ cfg_data.routing_mode = LB_MODE_SIDETONE;
+ cfg_data.enable = enable;
+ ret = q6common_pack_pp_params(packed_param_data + packed_param_size,
+ &param_hdr, (u8 *) &cfg_data,
+ &single_param_size);
+ if (ret) {
+ pr_err("%s: Failed to pack param data, error %d\n", __func__,
+ ret);
+ goto done;
+ }
+ packed_param_size += single_param_size;
+
+ pr_debug("%s rx(0x%x) tx(0x%x) enable(%d) mid(0x%x) gain(%d) sidetone_enable(%d)\n",
+ __func__, rx_port_id, tx_port_id,
+ enable, mid, sidetone_gain, sidetone_enable);
+
+ ret = q6afe_set_params(tx_port_id, q6audio_get_port_index(tx_port_id),
+ NULL, packed_param_data, packed_param_size);
+ if (ret)
+ pr_err("%s: AFE sidetone send failed for tx_port:%d rx_port:%d ret:%d\n",
+ __func__, tx_port_id, rx_port_id, ret);
+
+done:
+ kfree(packed_param_data);
+ return ret;
+}
+
+int afe_sidetone_enable(u16 tx_port_id, u16 rx_port_id, bool enable)
+{
+ int ret;
+ int index;
+
+ index = q6audio_get_port_index(rx_port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (q6audio_validate_port(rx_port_id) < 0) {
+ pr_err("%s: Invalid port 0x%x\n",
+ __func__, rx_port_id);
+ ret = -EINVAL;
+ goto done;
+ }
+ index = q6audio_get_port_index(tx_port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (q6audio_validate_port(tx_port_id) < 0) {
+ pr_err("%s: Invalid port 0x%x\n",
+ __func__, tx_port_id);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (enable) {
+ ret = afe_sidetone_iir(tx_port_id);
+ if (ret)
+ goto done;
+ }
+
+ ret = afe_sidetone(tx_port_id, rx_port_id, enable);
+
+done:
+ return ret;
+}
+
+int afe_validate_port(u16 port_id)
+{
+ int ret;
+
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ case AFE_PORT_ID_SECONDARY_PCM_RX:
+ case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case MI2S_RX:
+ case MI2S_TX:
+ case HDMI_RX:
+ case DISPLAY_PORT_RX:
+ case AFE_PORT_ID_SPDIF_RX:
+ case RSVD_2:
+ case RSVD_3:
+ case DIGI_MIC_TX:
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_RX:
+ case INT_BT_SCO_RX:
+ case INT_BT_SCO_TX:
+ case INT_BT_A2DP_RX:
+ case INT_FM_RX:
+ case INT_FM_TX:
+ case RT_PROXY_PORT_001_RX:
+ case RT_PROXY_PORT_001_TX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_4_TX:
+ case SLIMBUS_5_RX:
+ case SLIMBUS_6_RX:
+ case SLIMBUS_6_TX:
+ case SLIMBUS_7_RX:
+ case SLIMBUS_7_TX:
+ case SLIMBUS_8_RX:
+ case SLIMBUS_8_TX:
+ case AFE_PORT_ID_USB_RX:
+ case AFE_PORT_ID_USB_TX:
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ case AFE_PORT_ID_QUINARY_MI2S_RX:
+ case AFE_PORT_ID_QUINARY_MI2S_TX:
+ case AFE_PORT_ID_SENARY_MI2S_TX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ case AFE_PORT_ID_INT0_MI2S_RX:
+ case AFE_PORT_ID_INT1_MI2S_RX:
+ case AFE_PORT_ID_INT2_MI2S_RX:
+ case AFE_PORT_ID_INT3_MI2S_RX:
+ case AFE_PORT_ID_INT4_MI2S_RX:
+ case AFE_PORT_ID_INT5_MI2S_RX:
+ case AFE_PORT_ID_INT6_MI2S_RX:
+ case AFE_PORT_ID_INT0_MI2S_TX:
+ case AFE_PORT_ID_INT1_MI2S_TX:
+ case AFE_PORT_ID_INT2_MI2S_TX:
+ case AFE_PORT_ID_INT3_MI2S_TX:
+ case AFE_PORT_ID_INT4_MI2S_TX:
+ case AFE_PORT_ID_INT5_MI2S_TX:
+ case AFE_PORT_ID_INT6_MI2S_TX:
+ case RT_PROXY_PORT_002_RX:
+ case RT_PROXY_PORT_002_TX:
+ {
+ ret = 0;
+ break;
+ }
+
+ default:
+ pr_err("%s: default ret 0x%x\n", __func__, port_id);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int afe_convert_virtual_to_portid(u16 port_id)
+{
+ int ret;
+
+ /*
+ * if port_id is virtual, convert to physical..
+ * if port_id is already physical, return physical
+ */
+ if (afe_validate_port(port_id) < 0) {
+ if (port_id == RT_PROXY_DAI_001_RX ||
+ port_id == RT_PROXY_DAI_001_TX ||
+ port_id == RT_PROXY_DAI_002_RX ||
+ port_id == RT_PROXY_DAI_002_TX) {
+ ret = VIRTUAL_ID_TO_PORTID(port_id);
+ } else {
+ pr_err("%s: wrong port 0x%x\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ }
+ } else
+ ret = port_id;
+
+ return ret;
+}
+int afe_port_stop_nowait(int port_id)
+{
+ struct afe_port_cmd_device_stop stop;
+ int ret = 0;
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE is already closed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+ port_id = q6audio_convert_virtual_to_portid(port_id);
+
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PORT_CMD_DEVICE_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+
+ ret = afe_apr_send_pkt(&stop, NULL);
+ if (ret)
+ pr_err("%s: AFE close failed %d\n", __func__, ret);
+
+fail_cmd:
+ return ret;
+
+}
+
+int afe_close(int port_id)
+{
+ struct afe_port_cmd_device_stop stop;
+ enum afe_mad_type mad_type;
+ int ret = 0;
+ int index = 0;
+ uint16_t port_index;
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE is already closed\n", __func__);
+ if ((port_id == RT_PROXY_DAI_001_RX) ||
+ (port_id == RT_PROXY_DAI_002_TX))
+ pcm_afe_instance[port_id & 0x1] = 0;
+ if ((port_id == RT_PROXY_DAI_002_RX) ||
+ (port_id == RT_PROXY_DAI_001_TX))
+ proxy_afe_instance[port_id & 0x1] = 0;
+ afe_close_done[port_id & 0x1] = true;
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+ if ((port_id == RT_PROXY_DAI_001_RX) ||
+ (port_id == RT_PROXY_DAI_002_TX)) {
+ pr_debug("%s: before decrementing pcm_afe_instance %d\n",
+ __func__, pcm_afe_instance[port_id & 0x1]);
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ pcm_afe_instance[port_id & 0x1]--;
+ if ((!(pcm_afe_instance[port_id & 0x1] == 0 &&
+ proxy_afe_instance[port_id & 0x1] == 0)) ||
+ afe_close_done[port_id & 0x1] == true)
+ return 0;
+ else
+ afe_close_done[port_id & 0x1] = true;
+ }
+
+ if ((port_id == RT_PROXY_DAI_002_RX) ||
+ (port_id == RT_PROXY_DAI_001_TX)) {
+ pr_debug("%s: before decrementing proxy_afe_instance %d\n",
+ __func__, proxy_afe_instance[port_id & 0x1]);
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ proxy_afe_instance[port_id & 0x1]--;
+ if ((!(pcm_afe_instance[port_id & 0x1] == 0 &&
+ proxy_afe_instance[port_id & 0x1] == 0)) ||
+ afe_close_done[port_id & 0x1] == true)
+ return 0;
+ else
+ afe_close_done[port_id & 0x1] = true;
+ }
+
+ port_id = q6audio_convert_virtual_to_portid(port_id);
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_validate_port(port_id);
+ if (ret < 0) {
+ pr_warn("%s: Not a valid port id 0x%x ret %d\n",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ mad_type = afe_port_get_mad_type(port_id);
+ pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+ mad_type);
+ if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) {
+ pr_debug("%s: Turn off MAD\n", __func__);
+ ret = afe_turn_onoff_hw_mad(mad_type, false);
+ if (ret) {
+ pr_err("%s: afe_turn_onoff_hw_mad failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ } else {
+ pr_debug("%s: Not a MAD port\n", __func__);
+ }
+
+ port_index = afe_get_port_index(port_id);
+ if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) {
+ this_afe.afe_sample_rates[port_index] = 0;
+ this_afe.topology[port_index] = 0;
+ this_afe.dev_acdb_id[port_index] = 0;
+ } else {
+ pr_err("%s: port %d\n", __func__, port_index);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if ((port_id == this_afe.aanc_info.aanc_tx_port) &&
+ (this_afe.aanc_info.aanc_active)) {
+ memset(&this_afe.aanc_info, 0x00, sizeof(this_afe.aanc_info));
+ ret = afe_aanc_mod_enable(this_afe.apr, port_id, 0);
+ if (ret)
+ pr_err("%s: AFE mod disable failed %d\n",
+ __func__, ret);
+ }
+
+ /*
+ * even if ramp down configuration failed it is not serious enough to
+ * warrant bailaing out.
+ */
+ if (afe_spk_ramp_dn_cfg(port_id) < 0)
+ pr_err("%s: ramp down configuration failed\n", __func__);
+
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = index;
+ stop.hdr.opcode = AFE_PORT_CMD_DEVICE_STOP;
+ stop.port_id = q6audio_get_port_id(port_id);
+ stop.reserved = 0;
+
+ ret = afe_apr_send_pkt(&stop, &this_afe.wait[index]);
+ if (ret)
+ pr_err("%s: AFE close failed %d\n", __func__, ret);
+
+fail_cmd:
+ return ret;
+}
+
+int afe_set_digital_codec_core_clock(u16 port_id,
+ struct afe_digital_clk_cfg *cfg)
+{
+ struct afe_digital_clk_cfg clk_cfg = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (!cfg) {
+ pr_err("%s: clock cfg is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ /*default rx port is taken to enable the codec digital clock*/
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG;
+ param_hdr.param_size = sizeof(struct afe_digital_clk_cfg);
+ clk_cfg = *cfg;
+
+ pr_debug("%s: Minor version =0x%x clk val = %d\n"
+ "clk root = 0x%x resrv = 0x%x\n",
+ __func__, cfg->i2s_cfg_minor_version, cfg->clk_val,
+ cfg->clk_root, cfg->reserved);
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &clk_cfg);
+ if (ret < 0)
+ pr_err("%s: AFE enable for port 0x%x ret %d\n", __func__,
+ port_id, ret);
+ return ret;
+}
+
+int afe_set_lpass_clock(u16 port_id, struct afe_clk_cfg *cfg)
+{
+ struct afe_clk_cfg clk_cfg = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (!cfg) {
+ pr_err("%s: clock cfg is NULL\n", __func__);
+ return -EINVAL;
+ }
+ ret = q6audio_is_digital_pcm_interface(port_id);
+ if (ret < 0) {
+ pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+
+ mutex_lock(&this_afe.afe_cmd_lock);
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_LPAIF_CLK_CONFIG;
+ param_hdr.param_size = sizeof(clk_cfg);
+ clk_cfg = *cfg;
+
+ pr_debug("%s: Minor version =0x%x clk val1 = %d\n"
+ "clk val2 = %d, clk src = 0x%x\n"
+ "clk root = 0x%x clk mode = 0x%x resrv = 0x%x\n"
+ "port id = 0x%x\n",
+ __func__, cfg->i2s_cfg_minor_version,
+ cfg->clk_val1, cfg->clk_val2, cfg->clk_src,
+ cfg->clk_root, cfg->clk_set_mode,
+ cfg->reserved, q6audio_get_port_id(port_id));
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &clk_cfg);
+ if (ret < 0)
+ pr_err("%s: AFE enable for port 0x%x ret %d\n",
+ __func__, port_id, ret);
+
+ mutex_unlock(&this_afe.afe_cmd_lock);
+ return ret;
+}
+
+int afe_set_lpass_clk_cfg(int index, struct afe_clk_set *cfg)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (!cfg) {
+ pr_err("%s: clock cfg is NULL\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: index[%d] invalid!\n", __func__, index);
+ return -EINVAL;
+ }
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ mutex_lock(&this_afe.afe_cmd_lock);
+ param_hdr.module_id = AFE_MODULE_CLOCK_SET;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_CLOCK_SET;
+ param_hdr.param_size = sizeof(struct afe_clk_set);
+
+ pr_debug("%s: Minor version =0x%x clk id = %d\n"
+ "clk freq (Hz) = %d, clk attri = 0x%x\n"
+ "clk root = 0x%x clk enable = 0x%x\n",
+ __func__, cfg->clk_set_minor_version,
+ cfg->clk_id, cfg->clk_freq_in_hz, cfg->clk_attri,
+ cfg->clk_root, cfg->enable);
+
+ ret = q6afe_svc_pack_and_set_param_in_band(index, param_hdr,
+ (u8 *) cfg);
+ if (ret < 0)
+ pr_err("%s: AFE clk cfg failed with ret %d\n",
+ __func__, ret);
+
+ mutex_unlock(&this_afe.afe_cmd_lock);
+ return ret;
+}
+
+int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg)
+{
+ int index = 0;
+ int ret = 0;
+
+ index = q6audio_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: AFE port index[%d] invalid!\n",
+ __func__, index);
+ return -EINVAL;
+ }
+ ret = q6audio_is_digital_pcm_interface(port_id);
+ if (ret < 0) {
+ pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+
+ ret = afe_set_lpass_clk_cfg(index, cfg);
+ if (ret)
+ pr_err("%s: afe_set_lpass_clk_cfg_v2 failed %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+int afe_set_lpass_internal_digital_codec_clock(u16 port_id,
+ struct afe_digital_clk_cfg *cfg)
+{
+ struct afe_digital_clk_cfg clk_cfg = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (!cfg) {
+ pr_err("%s: clock cfg is NULL\n", __func__);
+ return -EINVAL;
+ }
+ ret = q6audio_is_digital_pcm_interface(port_id);
+ if (ret < 0) {
+ pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG;
+ param_hdr.param_size = sizeof(clk_cfg);
+ clk_cfg = *cfg;
+
+ pr_debug("%s: Minor version =0x%x clk val = %d\n"
+ "clk root = 0x%x resrv = 0x%x port id = 0x%x\n",
+ __func__, cfg->i2s_cfg_minor_version,
+ cfg->clk_val, cfg->clk_root, cfg->reserved,
+ q6audio_get_port_id(port_id));
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &clk_cfg);
+ if (ret < 0)
+ pr_err("%s: AFE enable for port 0x0x%x ret %d\n",
+ __func__, port_id, ret);
+
+ return ret;
+}
+
+int afe_enable_lpass_core_shared_clock(u16 port_id, u32 enable)
+{
+ struct afe_param_id_lpass_core_shared_clk_cfg clk_cfg = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ ret = q6audio_is_digital_pcm_interface(port_id);
+ if (ret < 0) {
+ pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+
+ mutex_lock(&this_afe.afe_cmd_lock);
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_LPASS_CORE_SHARED_CLOCK_CONFIG;
+ param_hdr.param_size = sizeof(clk_cfg);
+ clk_cfg.lpass_core_shared_clk_cfg_minor_version =
+ AFE_API_VERSION_LPASS_CORE_SHARED_CLK_CONFIG;
+ clk_cfg.enable = enable;
+
+ pr_debug("%s: port id = %d, enable = %d\n",
+ __func__, q6audio_get_port_id(port_id), enable);
+
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr, (u8 *) &clk_cfg);
+ if (ret < 0)
+ pr_err("%s: AFE enable for port 0x%x ret %d\n",
+ __func__, port_id, ret);
+
+ mutex_unlock(&this_afe.afe_cmd_lock);
+ return ret;
+}
+
+int q6afe_check_osr_clk_freq(u32 freq)
+{
+ int ret = 0;
+ switch (freq) {
+ case Q6AFE_LPASS_OSR_CLK_12_P288_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_8_P192_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_6_P144_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_4_P096_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_3_P072_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_2_P048_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_1_P536_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_1_P024_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_768_kHZ:
+ case Q6AFE_LPASS_OSR_CLK_512_kHZ:
+ break;
+ default:
+ pr_err("%s: deafult freq 0x%x\n",
+ __func__, freq);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+int afe_get_sp_th_vi_ftm_data(struct afe_sp_th_vi_get_param *th_vi)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int port = SLIMBUS_4_TX;
+ int ret = -EINVAL;
+
+ if (!th_vi) {
+ pr_err("%s: Invalid params\n", __func__);
+ goto done;
+ }
+ if (this_afe.vi_tx_port != -1)
+ port = this_afe.vi_tx_port;
+
+ param_hdr.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS;
+ param_hdr.param_size = sizeof(struct afe_sp_th_vi_ftm_params);
+
+ ret = q6afe_get_params(port, NULL, &param_hdr);
+ if (ret) {
+ pr_err("%s: Failed to get TH VI FTM data\n", __func__);
+ goto done;
+ }
+
+ th_vi->pdata = param_hdr;
+ memcpy(&th_vi->param , &this_afe.th_vi_resp.param,
+ sizeof(this_afe.th_vi_resp.param));
+ pr_debug("%s: DC resistance %d %d temp %d %d status %d %d\n",
+ __func__, th_vi->param.dc_res_q24[SP_V2_SPKR_1],
+ th_vi->param.dc_res_q24[SP_V2_SPKR_2],
+ th_vi->param.temp_q22[SP_V2_SPKR_1],
+ th_vi->param.temp_q22[SP_V2_SPKR_2],
+ th_vi->param.status[SP_V2_SPKR_1],
+ th_vi->param.status[SP_V2_SPKR_2]);
+ ret = 0;
+done:
+ return ret;
+}
+
+int afe_get_sp_ex_vi_ftm_data(struct afe_sp_ex_vi_get_param *ex_vi)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int port = SLIMBUS_4_TX;
+ int ret = -EINVAL;
+
+ if (!ex_vi) {
+ pr_err("%s: Invalid params\n", __func__);
+ goto done;
+ }
+ if (this_afe.vi_tx_port != -1)
+ port = this_afe.vi_tx_port;
+
+ param_hdr.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS;
+ param_hdr.param_size = sizeof(struct afe_sp_ex_vi_ftm_params);
+
+ ret = q6afe_get_params(port, NULL, &param_hdr);
+ if (ret < 0) {
+ pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n",
+ __func__, port, param_hdr.param_id, ret);
+ goto done;
+ }
+
+ ex_vi->pdata = param_hdr;
+ memcpy(&ex_vi->param , &this_afe.ex_vi_resp.param,
+ sizeof(this_afe.ex_vi_resp.param));
+ pr_debug("%s: freq %d %d resistance %d %d qfactor %d %d state %d %d\n",
+ __func__, ex_vi->param.freq_q20[SP_V2_SPKR_1],
+ ex_vi->param.freq_q20[SP_V2_SPKR_2],
+ ex_vi->param.resis_q24[SP_V2_SPKR_1],
+ ex_vi->param.resis_q24[SP_V2_SPKR_2],
+ ex_vi->param.qmct_q24[SP_V2_SPKR_1],
+ ex_vi->param.qmct_q24[SP_V2_SPKR_2],
+ ex_vi->param.status[SP_V2_SPKR_1],
+ ex_vi->param.status[SP_V2_SPKR_2]);
+ ret = 0;
+done:
+ return ret;
+}
+
+int afe_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats,
+ u16 port)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = -EINVAL;
+
+ if (!timing_stats) {
+ pr_err("%s: Invalid params\n", __func__);
+ goto exit;
+ }
+
+ param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_DEV_TIMING_STATS;
+ param_hdr.param_size = sizeof(struct afe_param_id_dev_timing_stats);
+
+ ret = q6afe_get_params(port, NULL, &param_hdr);
+ if (ret < 0) {
+ pr_err("%s: get param port 0x%x param id[0x%x] failed %d\n",
+ __func__, port, param_hdr.param_id, ret);
+ goto exit;
+ }
+
+ memcpy(timing_stats, &this_afe.av_dev_drift_resp.timing_stats,
+ param_hdr.param_size);
+ ret = 0;
+exit:
+ return ret;
+}
+
+int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib_resp)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int port = SLIMBUS_4_TX;
+ int ret = -EINVAL;
+
+ if (!calib_resp) {
+ pr_err("%s: Invalid params\n", __func__);
+ goto fail_cmd;
+ }
+ if (this_afe.vi_tx_port != -1)
+ port = this_afe.vi_tx_port;
+
+ param_hdr.module_id = AFE_MODULE_FB_SPKR_PROT_VI_PROC_V2;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AFE_PARAM_ID_CALIB_RES_CFG_V2;
+ param_hdr.param_size = sizeof(struct afe_spkr_prot_get_vi_calib);
+
+ ret = q6afe_get_params(port, NULL, &param_hdr);
+ if (ret < 0) {
+ pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n",
+ __func__, port, param_hdr.param_id, ret);
+ goto fail_cmd;
+ }
+ memcpy(&calib_resp->res_cfg , &this_afe.calib_data.res_cfg,
+ sizeof(this_afe.calib_data.res_cfg));
+ pr_info("%s: state %s resistance %d %d\n", __func__,
+ fbsp_state[calib_resp->res_cfg.th_vi_ca_state],
+ calib_resp->res_cfg.r0_cali_q24[SP_V2_SPKR_1],
+ calib_resp->res_cfg.r0_cali_q24[SP_V2_SPKR_2]);
+ ret = 0;
+fail_cmd:
+ return ret;
+}
+
+int afe_spk_prot_feed_back_cfg(int src_port, int dst_port,
+ int l_ch, int r_ch, u32 enable)
+{
+ int ret = -EINVAL;
+ union afe_spkr_prot_config prot_config;
+ int index = 0;
+
+ if (!enable) {
+ pr_debug("%s: Disable Feedback tx path", __func__);
+ this_afe.vi_tx_port = -1;
+ this_afe.vi_rx_port = -1;
+ return 0;
+ }
+
+ if ((q6audio_validate_port(src_port) < 0) ||
+ (q6audio_validate_port(dst_port) < 0)) {
+ pr_err("%s: invalid ports src 0x%x dst 0x%x",
+ __func__, src_port, dst_port);
+ goto fail_cmd;
+ }
+ if (!l_ch && !r_ch) {
+ pr_err("%s: error ch values zero\n", __func__);
+ goto fail_cmd;
+ }
+ pr_debug("%s: src_port 0x%x dst_port 0x%x l_ch %d r_ch %d\n",
+ __func__, src_port, dst_port, l_ch, r_ch);
+ memset(&prot_config, 0, sizeof(prot_config));
+ prot_config.feedback_path_cfg.dst_portid =
+ q6audio_get_port_id(dst_port);
+ if (l_ch) {
+ prot_config.feedback_path_cfg.chan_info[index++] = 1;
+ prot_config.feedback_path_cfg.chan_info[index++] = 2;
+ }
+ if (r_ch) {
+ prot_config.feedback_path_cfg.chan_info[index++] = 3;
+ prot_config.feedback_path_cfg.chan_info[index++] = 4;
+ }
+ prot_config.feedback_path_cfg.num_channels = index;
+ pr_debug("%s no of channels: %d\n", __func__, index);
+ prot_config.feedback_path_cfg.minor_version = 1;
+ ret = afe_spk_prot_prepare(src_port, dst_port,
+ AFE_PARAM_ID_FEEDBACK_PATH_CFG, &prot_config);
+fail_cmd:
+ return ret;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+ int ret = -EINVAL;
+
+ switch (cal_type) {
+ case AFE_COMMON_RX_CAL_TYPE:
+ ret = AFE_COMMON_RX_CAL;
+ break;
+ case AFE_COMMON_TX_CAL_TYPE:
+ ret = AFE_COMMON_TX_CAL;
+ break;
+ case AFE_AANC_CAL_TYPE:
+ ret = AFE_AANC_CAL;
+ break;
+ case AFE_HW_DELAY_CAL_TYPE:
+ ret = AFE_HW_DELAY_CAL;
+ break;
+ case AFE_FB_SPKR_PROT_CAL_TYPE:
+ ret = AFE_FB_SPKR_PROT_CAL;
+ break;
+ case AFE_SIDETONE_CAL_TYPE:
+ ret = AFE_SIDETONE_CAL;
+ break;
+ case AFE_SIDETONE_IIR_CAL_TYPE:
+ ret = AFE_SIDETONE_IIR_CAL;
+ break;
+ case AFE_TOPOLOGY_CAL_TYPE:
+ ret = AFE_TOPOLOGY_CAL;
+ break;
+ case AFE_CUST_TOPOLOGY_CAL_TYPE:
+ ret = AFE_CUST_TOPOLOGY_CAL;
+ break;
+ default:
+ pr_err("%s: invalid cal type %d!\n", __func__, cal_type);
+ }
+ return ret;
+}
+
+int afe_alloc_cal(int32_t cal_type, size_t data_size,
+ void *data)
+{
+ int ret = 0;
+ int cal_index;
+
+ cal_index = get_cal_type_index(cal_type);
+ pr_debug("%s: cal_type = %d cal_index = %d\n",
+ __func__, cal_type, cal_index);
+
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_alloc_cal(data_size, data,
+ this_afe.cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int afe_dealloc_cal(int32_t cal_type, size_t data_size,
+ void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_dealloc_cal(data_size, data,
+ this_afe.cal_data[cal_index]);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int afe_set_cal(int32_t cal_type, size_t data_size,
+ void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_set_cal(data_size, data,
+ this_afe.cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (cal_index == AFE_CUST_TOPOLOGY_CAL) {
+ mutex_lock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock);
+ this_afe.set_custom_topology = 1;
+ pr_debug("%s:[AFE_CUSTOM_TOPOLOGY] ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ mutex_unlock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock);
+ }
+
+done:
+ return ret;
+}
+
+static struct cal_block_data *afe_find_hw_delay_by_path(
+ struct cal_type_data *cal_type, int path)
+{
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block = NULL;
+ pr_debug("%s:\n", __func__);
+
+ list_for_each_safe(ptr, next,
+ &cal_type->cal_blocks) {
+
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+
+ if (((struct audio_cal_info_hw_delay *)cal_block->cal_info)
+ ->path == path) {
+ return cal_block;
+ }
+ }
+ return NULL;
+}
+
+static int afe_get_cal_hw_delay(int32_t path,
+ struct audio_cal_hw_delay_entry *entry)
+{
+ int ret = 0;
+ int i;
+ struct cal_block_data *cal_block = NULL;
+ struct audio_cal_hw_delay_data *hw_delay_info = NULL;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.cal_data[AFE_HW_DELAY_CAL] == NULL) {
+ pr_err("%s: AFE_HW_DELAY_CAL not initialized\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (entry == NULL) {
+ pr_err("%s: entry is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ if ((path >= MAX_PATH_TYPE) || (path < 0)) {
+ pr_err("%s: bad path: %d\n",
+ __func__, path);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&this_afe.cal_data[AFE_HW_DELAY_CAL]->lock);
+ cal_block = afe_find_hw_delay_by_path(
+ this_afe.cal_data[AFE_HW_DELAY_CAL], path);
+ if (cal_block == NULL)
+ goto unlock;
+
+ hw_delay_info = &((struct audio_cal_info_hw_delay *)
+ cal_block->cal_info)->data;
+ if (hw_delay_info->num_entries > MAX_HW_DELAY_ENTRIES) {
+ pr_err("%s: invalid num entries: %d\n",
+ __func__, hw_delay_info->num_entries);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ for (i = 0; i < hw_delay_info->num_entries; i++) {
+ if (hw_delay_info->entry[i].sample_rate ==
+ entry->sample_rate) {
+ entry->delay_usec = hw_delay_info->entry[i].delay_usec;
+ break;
+ }
+ }
+ if (i == hw_delay_info->num_entries) {
+ pr_err("%s: Unable to find delay for sample rate %d\n",
+ __func__, entry->sample_rate);
+ ret = -EFAULT;
+ goto unlock;
+ }
+ pr_debug("%s: Path = %d samplerate = %u usec = %u status %d\n",
+ __func__, path, entry->sample_rate, entry->delay_usec, ret);
+unlock:
+ mutex_unlock(&this_afe.cal_data[AFE_HW_DELAY_CAL]->lock);
+done:
+ return ret;
+}
+
+static int afe_set_cal_sp_th_vi_ftm_cfg(int32_t cal_type, size_t data_size,
+ void *data)
+{
+ int ret = 0;
+ struct audio_cal_type_sp_th_vi_ftm_cfg *cal_data = data;
+
+ if (this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL ||
+ cal_data == NULL ||
+ data_size != sizeof(*cal_data))
+ goto done;
+
+ pr_debug("%s: cal_type = %d\n", __func__, cal_type);
+ mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+ memcpy(&this_afe.th_ftm_cfg, &cal_data->cal_info,
+ sizeof(this_afe.th_ftm_cfg));
+ mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+done:
+ return ret;
+}
+
+static int afe_set_cal_sp_ex_vi_ftm_cfg(int32_t cal_type, size_t data_size,
+ void *data)
+{
+ int ret = 0;
+ struct audio_cal_type_sp_ex_vi_ftm_cfg *cal_data = data;
+
+ if (this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL ||
+ cal_data == NULL ||
+ data_size != sizeof(*cal_data))
+ goto done;
+
+ pr_debug("%s: cal_type = %d\n", __func__, cal_type);
+ mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+ memcpy(&this_afe.ex_ftm_cfg, &cal_data->cal_info,
+ sizeof(this_afe.ex_ftm_cfg));
+ mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+done:
+ return ret;
+}
+
+static int afe_set_cal_fb_spkr_prot(int32_t cal_type, size_t data_size,
+ void *data)
+{
+ int ret = 0;
+ struct audio_cal_type_fb_spk_prot_cfg *cal_data = data;
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL)
+ goto done;
+ if (cal_data == NULL)
+ goto done;
+ if (data_size != sizeof(*cal_data))
+ goto done;
+
+ if (cal_data->cal_info.mode == MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS)
+ __pm_wakeup_event(&wl.ws, jiffies_to_msecs(WAKELOCK_TIMEOUT));
+ mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+ memcpy(&this_afe.prot_cfg, &cal_data->cal_info,
+ sizeof(this_afe.prot_cfg));
+ mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+done:
+ return ret;
+}
+
+static int afe_get_cal_sp_th_vi_ftm_param(int32_t cal_type, size_t data_size,
+ void *data)
+{
+ int i, ret = 0;
+ struct audio_cal_type_sp_th_vi_param *cal_data = data;
+ struct afe_sp_th_vi_get_param th_vi;
+
+ pr_debug("%s: cal_type = %d\n", __func__, cal_type);
+ if (this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL ||
+ cal_data == NULL ||
+ data_size != sizeof(*cal_data))
+ goto done;
+
+ mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+ for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+ cal_data->cal_info.status[i] = -EINVAL;
+ cal_data->cal_info.r_dc_q24[i] = -1;
+ cal_data->cal_info.temp_q22[i] = -1;
+ }
+ if (!afe_get_sp_th_vi_ftm_data(&th_vi)) {
+ for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+ pr_debug("%s: ftm param status = %d\n",
+ __func__, th_vi.param.status[i]);
+ if (th_vi.param.status[i] == FBSP_IN_PROGRESS) {
+ cal_data->cal_info.status[i] = -EAGAIN;
+ } else if (th_vi.param.status[i] == FBSP_SUCCESS) {
+ cal_data->cal_info.status[i] = 0;
+ cal_data->cal_info.r_dc_q24[i] =
+ th_vi.param.dc_res_q24[i];
+ cal_data->cal_info.temp_q22[i] =
+ th_vi.param.temp_q22[i];
+ }
+ }
+ }
+ mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+done:
+ return ret;
+}
+
+static int afe_get_cal_sp_ex_vi_ftm_param(int32_t cal_type, size_t data_size,
+ void *data)
+{
+ int i, ret = 0;
+ struct audio_cal_type_sp_ex_vi_param *cal_data = data;
+ struct afe_sp_ex_vi_get_param ex_vi;
+
+ pr_debug("%s: cal_type = %d\n", __func__, cal_type);
+ if (this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL ||
+ cal_data == NULL ||
+ data_size != sizeof(*cal_data))
+ goto done;
+
+ mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+ for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+ cal_data->cal_info.status[i] = -EINVAL;
+ cal_data->cal_info.freq_q20[i] = -1;
+ cal_data->cal_info.resis_q24[i] = -1;
+ cal_data->cal_info.qmct_q24[i] = -1;
+ }
+ if (!afe_get_sp_ex_vi_ftm_data(&ex_vi)) {
+ for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+ pr_debug("%s: ftm param status = %d\n",
+ __func__, ex_vi.param.status[i]);
+ if (ex_vi.param.status[i] == FBSP_IN_PROGRESS) {
+ cal_data->cal_info.status[i] = -EAGAIN;
+ } else if (ex_vi.param.status[i] == FBSP_SUCCESS) {
+ cal_data->cal_info.status[i] = 0;
+ cal_data->cal_info.freq_q20[i] =
+ ex_vi.param.freq_q20[i];
+ cal_data->cal_info.resis_q24[i] =
+ ex_vi.param.resis_q24[i];
+ cal_data->cal_info.qmct_q24[i] =
+ ex_vi.param.qmct_q24[i];
+ }
+ }
+ }
+ mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+done:
+ return ret;
+}
+
+static int afe_get_cal_fb_spkr_prot(int32_t cal_type, size_t data_size,
+ void *data)
+{
+ int ret = 0;
+ struct audio_cal_type_fb_spk_prot_status *cal_data = data;
+ struct afe_spkr_prot_get_vi_calib calib_resp;
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL)
+ goto done;
+ if (cal_data == NULL)
+ goto done;
+ if (data_size != sizeof(*cal_data))
+ goto done;
+
+ mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+ if (this_afe.prot_cfg.mode == MSM_SPKR_PROT_CALIBRATED) {
+ cal_data->cal_info.r0[SP_V2_SPKR_1] =
+ this_afe.prot_cfg.r0[SP_V2_SPKR_1];
+ cal_data->cal_info.r0[SP_V2_SPKR_2] =
+ this_afe.prot_cfg.r0[SP_V2_SPKR_2];
+ cal_data->cal_info.status = 0;
+ } else if (this_afe.prot_cfg.mode ==
+ MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) {
+ /*Call AFE to query the status*/
+ cal_data->cal_info.status = -EINVAL;
+ cal_data->cal_info.r0[SP_V2_SPKR_1] = -1;
+ cal_data->cal_info.r0[SP_V2_SPKR_2] = -1;
+ if (!afe_spk_prot_get_calib_data(&calib_resp)) {
+ if (calib_resp.res_cfg.th_vi_ca_state ==
+ FBSP_IN_PROGRESS)
+ cal_data->cal_info.status = -EAGAIN;
+ else if (calib_resp.res_cfg.th_vi_ca_state ==
+ FBSP_SUCCESS) {
+ cal_data->cal_info.status = 0;
+ cal_data->cal_info.r0[SP_V2_SPKR_1] =
+ calib_resp.res_cfg.r0_cali_q24[SP_V2_SPKR_1];
+ cal_data->cal_info.r0[SP_V2_SPKR_2] =
+ calib_resp.res_cfg.r0_cali_q24[SP_V2_SPKR_2];
+ }
+ }
+ if (!cal_data->cal_info.status) {
+ this_afe.prot_cfg.mode =
+ MSM_SPKR_PROT_CALIBRATED;
+ this_afe.prot_cfg.r0[SP_V2_SPKR_1] =
+ cal_data->cal_info.r0[SP_V2_SPKR_1];
+ this_afe.prot_cfg.r0[SP_V2_SPKR_2] =
+ cal_data->cal_info.r0[SP_V2_SPKR_2];
+ }
+ } else {
+ /*Indicates calibration data is invalid*/
+ cal_data->cal_info.status = -EINVAL;
+ cal_data->cal_info.r0[SP_V2_SPKR_1] = -1;
+ cal_data->cal_info.r0[SP_V2_SPKR_2] = -1;
+ }
+ mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+ __pm_relax(&wl.ws);
+done:
+ return ret;
+}
+
+static int afe_map_cal_data(int32_t cal_type,
+ struct cal_block_data *cal_block)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+
+ mutex_lock(&this_afe.afe_cmd_lock);
+ atomic_set(&this_afe.mem_map_cal_index, cal_index);
+ ret = afe_cmd_memory_map(cal_block->cal_data.paddr,
+ cal_block->map_data.map_size);
+ atomic_set(&this_afe.mem_map_cal_index, -1);
+ if (ret < 0) {
+ pr_err("%s: mmap did not work! size = %zd ret %d\n",
+ __func__,
+ cal_block->map_data.map_size, ret);
+ pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n",
+ __func__,
+ &cal_block->cal_data.paddr,
+ cal_block->map_data.map_size);
+ mutex_unlock(&this_afe.afe_cmd_lock);
+ goto done;
+ }
+ cal_block->map_data.q6map_handle = atomic_read(&this_afe.
+ mem_map_cal_handles[cal_index]);
+ mutex_unlock(&this_afe.afe_cmd_lock);
+done:
+ return ret;
+}
+
+static int afe_unmap_cal_data(int32_t cal_type,
+ struct cal_block_data *cal_block)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block == NULL) {
+ pr_err("%s: Cal block is NULL!\n",
+ __func__);
+ goto done;
+ }
+
+ if (cal_block->map_data.q6map_handle == 0) {
+ pr_err("%s: Map handle is NULL, nothing to unmap\n",
+ __func__);
+ goto done;
+ }
+
+ atomic_set(&this_afe.mem_map_cal_handles[cal_index],
+ cal_block->map_data.q6map_handle);
+ atomic_set(&this_afe.mem_map_cal_index, cal_index);
+ ret = afe_cmd_memory_unmap_nowait(
+ cal_block->map_data.q6map_handle);
+ atomic_set(&this_afe.mem_map_cal_index, -1);
+ if (ret < 0) {
+ pr_err("%s: unmap did not work! cal_type %i ret %d\n",
+ __func__, cal_index, ret);
+ }
+ cal_block->map_data.q6map_handle = 0;
+done:
+ return ret;
+}
+
+static void afe_delete_cal_data(void)
+{
+ pr_debug("%s:\n", __func__);
+
+ cal_utils_destroy_cal_types(MAX_AFE_CAL_TYPES, this_afe.cal_data);
+
+ return;
+}
+
+static int afe_init_cal_data(void)
+{
+ int ret = 0;
+ struct cal_type_info cal_type_info[] = {
+ {{AFE_COMMON_RX_CAL_TYPE,
+ {afe_alloc_cal, afe_dealloc_cal, NULL,
+ afe_set_cal, NULL, NULL} },
+ {afe_map_cal_data, afe_unmap_cal_data,
+ cal_utils_match_buf_num} },
+
+ {{AFE_COMMON_TX_CAL_TYPE,
+ {afe_alloc_cal, afe_dealloc_cal, NULL,
+ afe_set_cal, NULL, NULL} },
+ {afe_map_cal_data, afe_unmap_cal_data,
+ cal_utils_match_buf_num} },
+
+ {{AFE_AANC_CAL_TYPE,
+ {afe_alloc_cal, afe_dealloc_cal, NULL,
+ afe_set_cal, NULL, NULL} },
+ {afe_map_cal_data, afe_unmap_cal_data,
+ cal_utils_match_buf_num} },
+
+ {{AFE_FB_SPKR_PROT_CAL_TYPE,
+ {NULL, NULL, NULL, afe_set_cal_fb_spkr_prot,
+ afe_get_cal_fb_spkr_prot, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{AFE_HW_DELAY_CAL_TYPE,
+ {NULL, NULL, NULL,
+ afe_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{AFE_SIDETONE_CAL_TYPE,
+ {NULL, NULL, NULL,
+ afe_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{AFE_SIDETONE_IIR_CAL_TYPE,
+ {NULL, NULL, NULL,
+ afe_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{AFE_TOPOLOGY_CAL_TYPE,
+ {NULL, NULL, NULL,
+ afe_set_cal, NULL, NULL} },
+ {NULL, NULL,
+ cal_utils_match_buf_num} },
+
+ {{AFE_CUST_TOPOLOGY_CAL_TYPE,
+ {afe_alloc_cal, afe_dealloc_cal, NULL,
+ afe_set_cal, NULL, NULL} },
+ {afe_map_cal_data, afe_unmap_cal_data,
+ cal_utils_match_buf_num} },
+
+ {{AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE,
+ {NULL, NULL, NULL, afe_set_cal_sp_th_vi_ftm_cfg,
+ afe_get_cal_sp_th_vi_ftm_param, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE,
+ {NULL, NULL, NULL, afe_set_cal_sp_ex_vi_ftm_cfg,
+ afe_get_cal_sp_ex_vi_ftm_param, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+ };
+ pr_debug("%s:\n", __func__);
+
+ ret = cal_utils_create_cal_types(MAX_AFE_CAL_TYPES, this_afe.cal_data,
+ cal_type_info);
+ if (ret < 0) {
+ pr_err("%s: could not create cal type! %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return ret;
+err:
+ afe_delete_cal_data();
+ return ret;
+}
+
+int afe_map_rtac_block(struct rtac_cal_block_data *cal_block)
+{
+ int result = 0;
+ pr_debug("%s:\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("%s: cal_block is NULL!\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block->cal_data.paddr == 0) {
+ pr_debug("%s: No address to map!\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block->map_data.map_size == 0) {
+ pr_debug("%s: map size is 0!\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ result = afe_cmd_memory_map(cal_block->cal_data.paddr,
+ cal_block->map_data.map_size);
+ if (result < 0) {
+ pr_err("%s: afe_cmd_memory_map failed for addr = 0x%pK, size = %d, err %d\n",
+ __func__, &cal_block->cal_data.paddr,
+ cal_block->map_data.map_size, result);
+ return result;
+ }
+ cal_block->map_data.map_handle = this_afe.mmap_handle;
+
+done:
+ return result;
+}
+
+int afe_unmap_rtac_block(uint32_t *mem_map_handle)
+{
+ int result = 0;
+ pr_debug("%s:\n", __func__);
+
+ if (mem_map_handle == NULL) {
+ pr_err("%s: Map handle is NULL, nothing to unmap\n",
+ __func__);
+ goto done;
+ }
+
+ if (*mem_map_handle == 0) {
+ pr_debug("%s: Map handle is 0, nothing to unmap\n",
+ __func__);
+ goto done;
+ }
+
+ result = afe_cmd_memory_unmap(*mem_map_handle);
+ if (result) {
+ pr_err("%s: AFE memory unmap failed %d, handle 0x%x\n",
+ __func__, result, *mem_map_handle);
+ goto done;
+ } else {
+ *mem_map_handle = 0;
+ }
+
+done:
+ return result;
+}
+
+int afe_request_dma_resources(uint8_t dma_type, uint8_t num_read_dma_channels,
+ uint8_t num_write_dma_channels)
+{
+ int result = 0;
+ struct afe_request_lpass_dma_resources_command config;
+
+ pr_debug("%s:\n", __func__);
+
+ if (dma_type != AFE_LPAIF_DEFAULT_DMA_TYPE) {
+ pr_err("%s: DMA type %d is invalid\n",
+ __func__,
+ dma_type);
+ goto done;
+ }
+
+ if ((num_read_dma_channels == 0) &&
+ (num_write_dma_channels == 0)) {
+ pr_err("%s: DMA channels to allocate are 0\n",
+ __func__);
+ goto done;
+ }
+
+ if (num_read_dma_channels > AFE_MAX_RDDMA) {
+ pr_err("%s: Read DMA channels %d to allocate are > %d\n",
+ __func__,
+ num_read_dma_channels,
+ AFE_MAX_RDDMA);
+ goto done;
+ }
+
+ if (num_write_dma_channels > AFE_MAX_WRDMA) {
+ pr_err("%s: Write DMA channels %d to allocate are > %d\n",
+ __func__,
+ num_write_dma_channels,
+ AFE_MAX_WRDMA);
+ goto done;
+ }
+
+ result = afe_q6_interface_prepare();
+ if (result != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n",
+ __func__, result);
+ goto done;
+ }
+
+ memset(&config, 0, sizeof(config));
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = IDX_GLOBAL_CFG;
+ config.hdr.opcode = AFE_CMD_REQUEST_LPASS_RESOURCES;
+ config.resources.resource_id = AFE_LPAIF_DMA_RESOURCE_ID;
+ /* Only AFE_LPAIF_DEFAULT_DMA_TYPE dma type is supported */
+ config.dma_resources.dma_type = dma_type;
+ config.dma_resources.num_read_dma_channels = num_read_dma_channels;
+ config.dma_resources.num_write_dma_channels = num_write_dma_channels;
+
+ result = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+ if (result)
+ pr_err("%s: AFE_CMD_REQUEST_LPASS_RESOURCES failed %d\n",
+ __func__, result);
+
+done:
+ return result;
+}
+EXPORT_SYMBOL(afe_request_dma_resources);
+
+int afe_get_dma_idx(bool **ret_rddma_idx,
+ bool **ret_wrdma_idx)
+{
+ int ret = 0;
+
+ if (!ret_rddma_idx || !ret_wrdma_idx) {
+ pr_err("%s: invalid return pointers.", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ *ret_rddma_idx = &this_afe.alloced_rddma[0];
+ *ret_wrdma_idx = &this_afe.alloced_wrdma[0];
+
+done:
+ return ret;
+}
+EXPORT_SYMBOL(afe_get_dma_idx);
+
+int afe_release_all_dma_resources(void)
+{
+ int result = 0;
+ int i, total_size;
+ struct afe_release_lpass_dma_resources_command *config;
+ uint8_t *payload;
+
+ pr_debug("%s:\n", __func__);
+
+ if ((this_afe.num_alloced_rddma == 0) &&
+ (this_afe.num_alloced_wrdma == 0)) {
+ pr_err("%s: DMA channels to release is 0",
+ __func__);
+ goto done;
+ }
+
+ result = afe_q6_interface_prepare();
+ if (result != 0) {
+ pr_err("%s: Q6 interface prepare failed %d\n",
+ __func__, result);
+ goto done;
+ }
+
+ total_size = sizeof(struct afe_release_lpass_dma_resources_command) +
+ sizeof(uint8_t) *
+ (this_afe.num_alloced_rddma + this_afe.num_alloced_wrdma);
+
+ config = kzalloc(total_size, GFP_KERNEL);
+ if (!config) {
+ result = -ENOMEM;
+ goto done;
+ }
+
+ memset(config, 0, total_size);
+ payload = (uint8_t *) config +
+ sizeof(struct afe_release_lpass_dma_resources_command);
+
+ config->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ config->hdr.pkt_size = total_size;
+ config->hdr.src_port = 0;
+ config->hdr.dest_port = 0;
+ config->hdr.token = IDX_GLOBAL_CFG;
+ config->hdr.opcode = AFE_CMD_RELEASE_LPASS_RESOURCES;
+ config->resources.resource_id = AFE_LPAIF_DMA_RESOURCE_ID;
+ /* Only AFE_LPAIF_DEFAULT_DMA_TYPE dma type is supported */
+ config->dma_resources.dma_type = AFE_LPAIF_DEFAULT_DMA_TYPE;
+ config->dma_resources.num_read_dma_channels =
+ this_afe.num_alloced_rddma;
+ config->dma_resources.num_write_dma_channels =
+ this_afe.num_alloced_wrdma;
+
+ for (i = 0; i < AFE_MAX_RDDMA; i++) {
+ if (this_afe.alloced_rddma[i]) {
+ *payload = i;
+ payload++;
+ }
+ }
+
+ for (i = 0; i < AFE_MAX_WRDMA; i++) {
+ if (this_afe.alloced_wrdma[i]) {
+ *payload = i;
+ payload++;
+ }
+ }
+
+ result = afe_apr_send_pkt(config, &this_afe.wait[IDX_GLOBAL_CFG]);
+ if (result)
+ pr_err("%s: AFE_CMD_RELEASE_LPASS_RESOURCES failed %d\n",
+ __func__, result);
+
+ kfree(config);
+done:
+ return result;
+}
+EXPORT_SYMBOL(afe_release_all_dma_resources);
+
+static int __init afe_init(void)
+{
+ int i = 0, ret;
+
+ atomic_set(&this_afe.state, 0);
+ atomic_set(&this_afe.status, 0);
+ atomic_set(&this_afe.mem_map_cal_index, -1);
+ this_afe.apr = NULL;
+ this_afe.dtmf_gen_rx_portid = -1;
+ this_afe.mmap_handle = 0;
+ this_afe.vi_tx_port = -1;
+ this_afe.vi_rx_port = -1;
+ this_afe.prot_cfg.mode = MSM_SPKR_PROT_DISABLED;
+ this_afe.th_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
+ this_afe.ex_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
+ mutex_init(&this_afe.afe_cmd_lock);
+ for (i = 0; i < AFE_MAX_PORTS; i++) {
+ this_afe.afe_cal_mode[i] = AFE_CAL_MODE_DEFAULT;
+ this_afe.afe_sample_rates[i] = 0;
+ this_afe.dev_acdb_id[i] = 0;
+ init_waitqueue_head(&this_afe.wait[i]);
+ }
+ wakeup_source_init(&wl.ws, "spkr-prot");
+ ret = afe_init_cal_data();
+ if (ret)
+ pr_err("%s: could not init cal data! %d\n", __func__, ret);
+
+ config_debug_fs_init();
+
+ for (i = 0; i < IDX_GROUP_TDM_MAX; i++)
+ atomic_set(&tdm_gp_en_ref[i], 0);
+
+ return 0;
+}
+
+static void __exit afe_exit(void)
+{
+ afe_delete_cal_data();
+
+ config_debug_fs_exit();
+ mutex_destroy(&this_afe.afe_cmd_lock);
+ wakeup_source_trash(&wl.ws);
+}
+
+device_initcall(afe_init);
+__exitcall(afe_exit);
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
new file mode 100644
index 000000000000..eb3b42f47974
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -0,0 +1,10113 @@
+/*
+ * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+
+#include <linux/debugfs.h>
+#include <linux/time.h>
+#include <linux/atomic.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/mm.h>
+
+#include <asm/ioctls.h>
+
+#include <linux/memory.h>
+
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6core.h>
+#include <sound/q6audio-v2.h>
+#include <sound/audio_cal_utils.h>
+#include <sound/adsp_err.h>
+#include <sound/compress_params.h>
+#include <sound/q6common.h>
+
+#define TRUE 0x01
+#define FALSE 0x00
+#define SESSION_MAX 9
+#define ASM_MAX_CHANNELS 8
+
+enum {
+ ASM_TOPOLOGY_CAL = 0,
+ ASM_CUSTOM_TOP_CAL,
+ ASM_AUDSTRM_CAL,
+ ASM_RTAC_APR_CAL,
+ ASM_MAX_CAL_TYPES
+};
+
+union asm_token_struct {
+ struct {
+ u8 stream_id;
+ u8 session_id;
+ u8 buf_index;
+ u8 flags;
+ } _token;
+ u32 token;
+} __packed;
+
+
+enum {
+ ASM_DIRECTION_OFFSET,
+ ASM_CMD_NO_WAIT_OFFSET,
+ /*
+ * Offset is limited to 7 because flags is stored in u8
+ * field in asm_token_structure defined above. The offset
+ * starts from 0.
+ */
+ ASM_MAX_OFFSET = 7,
+};
+
+enum {
+ WAIT_CMD,
+ NO_WAIT_CMD
+};
+
+#define ASM_SET_BIT(n, x) (n |= 1 << x)
+#define ASM_TEST_BIT(n, x) ((n >> x) & 1)
+
+/* TODO, combine them together */
+static DEFINE_MUTEX(session_lock);
+struct asm_mmap {
+ atomic_t ref_cnt;
+ void *apr;
+};
+
+static struct asm_mmap this_mmap;
+
+struct audio_session {
+ struct audio_client *ac;
+ spinlock_t session_lock;
+ bool ignore;
+};
+/* session id: 0 reserved */
+static struct audio_session session[ASM_ACTIVE_STREAMS_ALLOWED + 1];
+
+struct asm_buffer_node {
+ struct list_head list;
+ phys_addr_t buf_phys_addr;
+ uint32_t mmap_hdl;
+};
+static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv);
+static int32_t q6asm_callback(struct apr_client_data *data, void *priv);
+static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg);
+static void q6asm_add_hdr_custom_topology(struct audio_client *ac,
+ struct apr_hdr *hdr,
+ uint32_t pkt_size);
+static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg);
+static int q6asm_memory_map_regions(struct audio_client *ac, int dir,
+ uint32_t bufsz, uint32_t bufcnt,
+ bool is_contiguous);
+static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir);
+static void q6asm_reset_buf_state(struct audio_client *ac);
+
+static int q6asm_map_channels(u8 *channel_mapping, uint32_t channels,
+ bool use_back_flavor);
+void *q6asm_mmap_apr_reg(void);
+
+static int q6asm_is_valid_session(struct apr_client_data *data, void *priv);
+static int q6asm_get_asm_topology_cal(void);
+static int q6asm_get_asm_app_type_cal(void);
+
+/* for ASM custom topology */
+static struct cal_type_data *cal_data[ASM_MAX_CAL_TYPES];
+static struct audio_buffer common_buf[2];
+static struct audio_client common_client;
+static int set_custom_topology;
+static int topology_map_handle;
+
+struct generic_get_data_ {
+ int valid;
+ int is_inband;
+ int size_in_ints;
+ int ints[];
+};
+static struct generic_get_data_ *generic_get_data;
+
+#ifdef CONFIG_DEBUG_FS
+#define OUT_BUFFER_SIZE 56
+#define IN_BUFFER_SIZE 24
+
+static struct timeval out_cold_tv;
+static struct timeval out_warm_tv;
+static struct timeval out_cont_tv;
+static struct timeval in_cont_tv;
+static long out_enable_flag;
+static long in_enable_flag;
+static struct dentry *out_dentry;
+static struct dentry *in_dentry;
+static int in_cont_index;
+/*This var is used to keep track of first write done for cold output latency */
+static int out_cold_index;
+static char *out_buffer;
+static char *in_buffer;
+
+static uint32_t adsp_reg_event_opcode[] = {
+ ASM_STREAM_CMD_REGISTER_PP_EVENTS,
+ ASM_STREAM_CMD_REGISTER_ENCDEC_EVENTS,
+ ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE };
+
+static uint32_t adsp_raise_event_opcode[] = {
+ ASM_STREAM_PP_EVENT,
+ ASM_STREAM_CMD_ENCDEC_EVENTS,
+ ASM_IEC_61937_MEDIA_FMT_EVENT };
+
+static int is_adsp_reg_event(uint32_t cmd)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(adsp_reg_event_opcode); i++) {
+ if (cmd == adsp_reg_event_opcode[i])
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int is_adsp_raise_event(uint32_t cmd)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(adsp_raise_event_opcode); i++) {
+ if (cmd == adsp_raise_event_opcode[i])
+ return i;
+ }
+ return -EINVAL;
+}
+
+int q6asm_get_svc_version(uint32_t service_id)
+{
+ int ret = 0;
+ static int asm_cached_version;
+ size_t ver_size;
+ struct avcs_fwk_ver_info *ver_info = NULL;
+
+ if (service_id == AVCS_SERVICE_ID_ALL) {
+ pr_err("%s: Invalid service id: %d", __func__,
+ AVCS_SERVICE_ID_ALL);
+ return -EINVAL;
+ }
+
+ if (asm_cached_version != 0)
+ return asm_cached_version;
+
+ ver_size = sizeof(struct avcs_get_fwk_version) +
+ sizeof(struct avs_svc_api_info);
+ ver_info = kzalloc(ver_size, GFP_KERNEL);
+ if (ver_info == NULL)
+ return -ENOMEM;
+
+ ret = q6core_get_service_version(service_id, ver_info, ver_size);
+ if (ret < 0)
+ goto done;
+
+ ret = ver_info->services[0].api_version;
+ asm_cached_version = ret;
+done:
+ kfree(ver_info);
+ return ret;
+}
+
+static inline void q6asm_set_flag_in_token(union asm_token_struct *asm_token,
+ int flag, int flag_offset)
+{
+ if (flag)
+ ASM_SET_BIT(asm_token->_token.flags, flag_offset);
+}
+
+static inline int q6asm_get_flag_from_token(union asm_token_struct *asm_token,
+ int flag_offset)
+{
+ return ASM_TEST_BIT(asm_token->_token.flags, flag_offset);
+}
+
+static inline void q6asm_update_token(u32 *token, u8 session_id, u8 stream_id,
+ u8 buf_index, u8 dir, u8 nowait_flag)
+{
+ union asm_token_struct asm_token;
+
+ asm_token.token = 0;
+ asm_token._token.session_id = session_id;
+ asm_token._token.stream_id = stream_id;
+ asm_token._token.buf_index = buf_index;
+ q6asm_set_flag_in_token(&asm_token, dir, ASM_DIRECTION_OFFSET);
+ q6asm_set_flag_in_token(&asm_token, nowait_flag,
+ ASM_CMD_NO_WAIT_OFFSET);
+ *token = asm_token.token;
+}
+
+static inline uint32_t q6asm_get_pcm_format_id(uint32_t media_format_block_ver)
+{
+ uint32_t pcm_format_id;
+
+ switch (media_format_block_ver) {
+ case PCM_MEDIA_FORMAT_V5:
+ pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V5;
+ break;
+ case PCM_MEDIA_FORMAT_V4:
+ pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4;
+ break;
+ case PCM_MEDIA_FORMAT_V3:
+ pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3;
+ break;
+ case PCM_MEDIA_FORMAT_V2:
+ default:
+ pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+ break;
+ }
+ return pcm_format_id;
+}
+
+/*
+ * q6asm_get_buf_index_from_token:
+ * Retrieve buffer index from token.
+ *
+ * @token: token value sent to ASM service on q6.
+ * Returns buffer index in the read/write commands.
+ */
+uint8_t q6asm_get_buf_index_from_token(uint32_t token)
+{
+ union asm_token_struct asm_token;
+
+ asm_token.token = token;
+ return asm_token._token.buf_index;
+}
+EXPORT_SYMBOL(q6asm_get_buf_index_from_token);
+
+/*
+ * q6asm_get_stream_id_from_token:
+ * Retrieve stream id from token.
+ *
+ * @token: token value sent to ASM service on q6.
+ * Returns stream id.
+ */
+uint8_t q6asm_get_stream_id_from_token(uint32_t token)
+{
+ union asm_token_struct asm_token;
+
+ asm_token.token = token;
+ return asm_token._token.stream_id;
+}
+EXPORT_SYMBOL(q6asm_get_stream_id_from_token);
+
+static int audio_output_latency_dbgfs_open(struct inode *inode,
+ struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+static ssize_t audio_output_latency_dbgfs_read(struct file *file,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ if (out_buffer == NULL) {
+ pr_err("%s: out_buffer is null\n", __func__);
+ return 0;
+ }
+ if (count < OUT_BUFFER_SIZE) {
+ pr_err("%s: read size %d exceeds buf size %zd\n", __func__,
+ OUT_BUFFER_SIZE, count);
+ return 0;
+ }
+ snprintf(out_buffer, OUT_BUFFER_SIZE, "%ld,%ld,%ld,%ld,%ld,%ld,",\
+ out_cold_tv.tv_sec, out_cold_tv.tv_usec, out_warm_tv.tv_sec,\
+ out_warm_tv.tv_usec, out_cont_tv.tv_sec, out_cont_tv.tv_usec);
+ return simple_read_from_buffer(buf, OUT_BUFFER_SIZE, ppos,
+ out_buffer, OUT_BUFFER_SIZE);
+}
+static ssize_t audio_output_latency_dbgfs_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ char *temp;
+
+ if (count != 2*sizeof(char)) {
+ pr_err("%s: err count is more %zd\n", __func__, count);
+ return -EINVAL;
+ } else {
+ temp = kmalloc(2*sizeof(char), GFP_KERNEL);
+ }
+
+ out_cold_index = 0;
+
+ if (temp) {
+ if (copy_from_user(temp, buf, 2*sizeof(char))) {
+ pr_err("%s: copy from user failed for size %zd\n",
+ __func__, 2*sizeof(char));
+ kfree(temp);
+ return -EFAULT;
+ }
+ if (!kstrtol(temp, 10, &out_enable_flag)) {
+ kfree(temp);
+ return count;
+ }
+ kfree(temp);
+ }
+ return -EINVAL;
+}
+static const struct file_operations audio_output_latency_debug_fops = {
+ .open = audio_output_latency_dbgfs_open,
+ .read = audio_output_latency_dbgfs_read,
+ .write = audio_output_latency_dbgfs_write
+};
+static int audio_input_latency_dbgfs_open(struct inode *inode,
+ struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+static ssize_t audio_input_latency_dbgfs_read(struct file *file,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ if (in_buffer == NULL) {
+ pr_err("%s: in_buffer is null\n", __func__);
+ return 0;
+ }
+ if (count < IN_BUFFER_SIZE) {
+ pr_err("%s: read size %d exceeds buf size %zd\n", __func__,
+ IN_BUFFER_SIZE, count);
+ return 0;
+ }
+ snprintf(in_buffer, IN_BUFFER_SIZE, "%ld,%ld,",\
+ in_cont_tv.tv_sec, in_cont_tv.tv_usec);
+ return simple_read_from_buffer(buf, IN_BUFFER_SIZE, ppos,
+ in_buffer, IN_BUFFER_SIZE);
+}
+static ssize_t audio_input_latency_dbgfs_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ char *temp;
+
+ if (count != 2*sizeof(char)) {
+ pr_err("%s: err count is more %zd\n", __func__, count);
+ return -EINVAL;
+ } else {
+ temp = kmalloc(2*sizeof(char), GFP_KERNEL);
+ }
+ if (temp) {
+ if (copy_from_user(temp, buf, 2*sizeof(char))) {
+ pr_err("%s: copy from user failed for size %zd\n",
+ __func__, 2*sizeof(char));
+ kfree(temp);
+ return -EFAULT;
+ }
+ if (!kstrtol(temp, 10, &in_enable_flag)) {
+ kfree(temp);
+ return count;
+ }
+ kfree(temp);
+ }
+ return -EINVAL;
+}
+static const struct file_operations audio_input_latency_debug_fops = {
+ .open = audio_input_latency_dbgfs_open,
+ .read = audio_input_latency_dbgfs_read,
+ .write = audio_input_latency_dbgfs_write
+};
+
+static void config_debug_fs_write_cb(void)
+{
+ if (out_enable_flag) {
+ /* For first Write done log the time and reset
+ out_cold_index*/
+ if (out_cold_index != 1) {
+ do_gettimeofday(&out_cold_tv);
+ pr_debug("COLD: apr_send_pkt at %ld sec %ld microsec\n",
+ out_cold_tv.tv_sec,\
+ out_cold_tv.tv_usec);
+ out_cold_index = 1;
+ }
+ pr_debug("%s: out_enable_flag %ld\n",
+ __func__, out_enable_flag);
+ }
+}
+static void config_debug_fs_read_cb(void)
+{
+ if (in_enable_flag) {
+ /* when in_cont_index == 7, DSP would be
+ * writing into the 8th 512 byte buffer and this
+ * timestamp is tapped here.Once done it then writes
+ * to 9th 512 byte buffer.These two buffers(8th, 9th)
+ * reach the test application in 5th iteration and that
+ * timestamp is tapped at user level. The difference
+ * of these two timestamps gives us the time between
+ * the time at which dsp started filling the sample
+ * required and when it reached the test application.
+ * Hence continuous input latency
+ */
+ if (in_cont_index == 7) {
+ do_gettimeofday(&in_cont_tv);
+ pr_info("%s: read buffer at %ld sec %ld microsec\n",
+ __func__,
+ in_cont_tv.tv_sec, in_cont_tv.tv_usec);
+ }
+ in_cont_index++;
+ }
+}
+
+static void config_debug_fs_reset_index(void)
+{
+ in_cont_index = 0;
+}
+
+static void config_debug_fs_run(void)
+{
+ if (out_enable_flag) {
+ do_gettimeofday(&out_cold_tv);
+ pr_debug("%s: COLD apr_send_pkt at %ld sec %ld microsec\n",
+ __func__, out_cold_tv.tv_sec, out_cold_tv.tv_usec);
+ }
+}
+
+static void config_debug_fs_write(struct audio_buffer *ab, int offset)
+{
+ if (out_enable_flag) {
+ char zero_pattern[2] = {0x00, 0x00};
+ char *data;
+
+ if ((offset < 0) || (offset > ab->size)) {
+ pr_err("Invalid offset %d", offset);
+ return;
+ }
+
+ data = (char *)ab->data + offset;
+ /* If First two byte is non zero and last two byte
+ is zero then it is warm output pattern */
+ if ((strncmp(data, zero_pattern, 2)) &&
+ (!strncmp((data + 2), zero_pattern, 2))) {
+ do_gettimeofday(&out_warm_tv);
+ pr_debug("%s: WARM:apr_send_pkt at %ld sec %ld microsec\n",
+ __func__,
+ out_warm_tv.tv_sec,\
+ out_warm_tv.tv_usec);
+ pr_debug("%s: Warm Pattern Matched\n", __func__);
+ }
+ /* If First two byte is zero and last two byte is
+ non zero then it is cont ouput pattern */
+ else if ((!strncmp(data, zero_pattern, 2))
+ && (strncmp((data + 2), zero_pattern, 2))) {
+ do_gettimeofday(&out_cont_tv);
+ pr_debug("%s: CONT:apr_send_pkt at %ld sec %ld microsec\n",
+ __func__,
+ out_cont_tv.tv_sec,
+ out_cont_tv.tv_usec);
+ pr_debug("%s: Cont Pattern Matched\n", __func__);
+ }
+ }
+}
+static void config_debug_fs_init(void)
+{
+ out_buffer = kzalloc(OUT_BUFFER_SIZE, GFP_KERNEL);
+ if (out_buffer == NULL) {
+ pr_err("%s: kmalloc() for out_buffer failed\n", __func__);
+ goto outbuf_fail;
+ }
+ in_buffer = kzalloc(IN_BUFFER_SIZE, GFP_KERNEL);
+ if (in_buffer == NULL) {
+ pr_err("%s: kmalloc() for in_buffer failed\n", __func__);
+ goto inbuf_fail;
+ }
+ out_dentry = debugfs_create_file("audio_out_latency_measurement_node",\
+ S_IRUGO | S_IWUSR | S_IWGRP,\
+ NULL, NULL, &audio_output_latency_debug_fops);
+ if (IS_ERR(out_dentry)) {
+ pr_err("%s: debugfs_create_file failed\n", __func__);
+ goto file_fail;
+ }
+ in_dentry = debugfs_create_file("audio_in_latency_measurement_node",\
+ S_IRUGO | S_IWUSR | S_IWGRP,\
+ NULL, NULL, &audio_input_latency_debug_fops);
+ if (IS_ERR(in_dentry)) {
+ pr_err("%s: debugfs_create_file failed\n", __func__);
+ goto file_fail;
+ }
+ return;
+file_fail:
+ kfree(in_buffer);
+inbuf_fail:
+ kfree(out_buffer);
+outbuf_fail:
+ in_buffer = NULL;
+ out_buffer = NULL;
+ return;
+}
+#else
+static void config_debug_fs_write(struct audio_buffer *ab, int offset)
+{
+ return;
+}
+static void config_debug_fs_run(void)
+{
+ return;
+}
+static void config_debug_fs_reset_index(void)
+{
+ return;
+}
+static void config_debug_fs_read_cb(void)
+{
+ return;
+}
+static void config_debug_fs_write_cb(void)
+{
+ return;
+}
+static void config_debug_fs_init(void)
+{
+ return;
+}
+#endif
+
+int q6asm_mmap_apr_dereg(void)
+{
+ int c;
+
+ c = atomic_sub_return(1, &this_mmap.ref_cnt);
+ if (c == 0) {
+ apr_deregister(this_mmap.apr);
+ common_client.mmap_apr = NULL;
+ pr_debug("%s: APR De-Register common port\n", __func__);
+ } else if (c < 0) {
+ pr_err("%s: APR Common Port Already Closed %d\n",
+ __func__, c);
+ atomic_set(&this_mmap.ref_cnt, 0);
+ }
+
+ return 0;
+}
+
+static int q6asm_session_alloc(struct audio_client *ac)
+{
+ int n;
+
+ for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) {
+ if (!(session[n].ac)) {
+ session[n].ac = ac;
+ return n;
+ }
+ }
+ pr_err("%s: session not available\n", __func__);
+ return -ENOMEM;
+}
+
+static void q6asm_session_set_ignore(int id)
+{
+ session[id].ignore = true;
+}
+
+static void q6asm_session_clean_ignore(void)
+{
+ int i;
+
+ for (i = 1; i <= ASM_ACTIVE_STREAMS_ALLOWED; i++)
+ session[i].ignore = false;
+}
+
+static void q6asm_session_deregister(struct audio_client *ac)
+{
+ rtac_set_asm_handle(ac->session, NULL);
+ apr_deregister(ac->apr2);
+ apr_deregister(ac->apr);
+ q6asm_mmap_apr_dereg();
+ ac->apr2 = NULL;
+ ac->apr = NULL;
+ ac->mmap_apr = NULL;
+}
+
+static int q6asm_session_register(struct audio_client *ac)
+{
+ ac->apr = apr_register("ADSP", "ASM",
+ (apr_fn)q6asm_callback,
+ ((ac->session) << 8 | 0x0001),
+ ac);
+ if (ac->apr == NULL) {
+ pr_err("%s: Registration with APR failed\n", __func__);
+ goto fail_apr1;
+ }
+
+ ac->apr2 = apr_register("ADSP", "ASM",
+ (apr_fn)q6asm_callback,
+ ((ac->session) << 8 | 0x0002),
+ ac);
+ if (ac->apr2 == NULL) {
+ pr_err("%s: Registration with APR-2 failed\n", __func__);
+ goto fail_apr2;
+ }
+
+ rtac_set_asm_handle(ac->session, ac->apr);
+
+ pr_debug("%s: Registering the common port with APR\n", __func__);
+ ac->mmap_apr = q6asm_mmap_apr_reg();
+ if (ac->mmap_apr == NULL)
+ goto fail_mmap;
+
+ return 0;
+
+fail_mmap:
+ apr_deregister(ac->apr2);
+fail_apr2:
+ apr_deregister(ac->apr);
+fail_apr1:
+ return -EINVAL;
+}
+
+static int q6asm_session_try_next(struct audio_client *ac)
+{
+ int n;
+ int s = ac->session;
+
+ for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) {
+ if (++s > ASM_ACTIVE_STREAMS_ALLOWED)
+ s -= ASM_ACTIVE_STREAMS_ALLOWED;
+ if (!session[s].ignore && !session[s].ac) {
+ q6asm_session_deregister(ac);
+ session[ac->session].ac = NULL;
+ session[s].ac = ac;
+ ac->session = s;
+ return q6asm_session_register(ac);
+ }
+ }
+ pr_debug("%s: session not available\n", __func__);
+ return -EINVAL;
+}
+
+static int q6asm_get_session_id_from_audio_client(struct audio_client *ac)
+{
+ int n;
+ for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) {
+ if (session[n].ac == ac)
+ return n;
+ }
+ return 0;
+}
+
+static bool q6asm_is_valid_audio_client(struct audio_client *ac)
+{
+ return q6asm_get_session_id_from_audio_client(ac) ? 1 : 0;
+}
+
+static void q6asm_session_free(struct audio_client *ac)
+{
+ int session_id;
+ unsigned long flags;
+
+ pr_debug("%s: sessionid[%d]\n", __func__, ac->session);
+ session_id = ac->session;
+ rtac_remove_popp_from_adm_devices(ac->session);
+ spin_lock_irqsave(&(session[session_id].session_lock), flags);
+ session[ac->session].ac = NULL;
+ ac->session = 0;
+ ac->perf_mode = LEGACY_PCM_MODE;
+ ac->fptr_cache_ops = NULL;
+ ac->cb = NULL;
+ ac->priv = NULL;
+ kfree(ac);
+ ac = NULL;
+ spin_unlock_irqrestore(&(session[session_id].session_lock), flags);
+
+ return;
+}
+
+static uint32_t q6asm_get_next_buf(struct audio_client *ac,
+ uint32_t curr_buf, uint32_t max_buf_cnt)
+{
+ dev_vdbg(ac->dev, "%s: curr_buf = %d, max_buf_cnt = %d\n",
+ __func__, curr_buf, max_buf_cnt);
+ curr_buf += 1;
+ return (curr_buf >= max_buf_cnt) ? 0 : curr_buf;
+}
+
+static int q6asm_map_cal_memory(int32_t cal_type,
+ struct cal_block_data *cal_block)
+{
+ int result = 0;
+ struct asm_buffer_node *buf_node = NULL;
+ struct list_head *ptr, *next;
+
+ if (cal_block == NULL) {
+ pr_err("%s: cal_block is NULL!\n",
+ __func__);
+ goto done;
+ }
+
+ if (cal_block->cal_data.paddr == 0) {
+ pr_debug("%s: No address to map!\n",
+ __func__);
+ goto done;
+ }
+
+ common_client.mmap_apr = q6asm_mmap_apr_reg();
+ if (common_client.mmap_apr == NULL) {
+ pr_err("%s: q6asm_mmap_apr_reg failed\n",
+ __func__);
+ result = -EPERM;
+ goto done;
+ }
+ common_client.apr = common_client.mmap_apr;
+ if (cal_block->map_data.map_size == 0) {
+ pr_debug("%s: map size is 0!\n",
+ __func__);
+ goto done;
+ }
+
+ /* Use second asm buf to map memory */
+ if (common_client.port[IN].buf == NULL) {
+ pr_err("%s: common buf is NULL\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ common_client.port[IN].buf->phys = cal_block->cal_data.paddr;
+
+ result = q6asm_memory_map_regions(&common_client,
+ IN, cal_block->map_data.map_size, 1, 1);
+ if (result < 0) {
+ pr_err("%s: mmap did not work! size = %zd result %d\n",
+ __func__,
+ cal_block->map_data.map_size, result);
+ pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n",
+ __func__,
+ &cal_block->cal_data.paddr,
+ cal_block->map_data.map_size);
+ goto done;
+ }
+
+ list_for_each_safe(ptr, next,
+ &common_client.port[IN].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_phys_addr == cal_block->cal_data.paddr) {
+ cal_block->map_data.q6map_handle = buf_node->mmap_hdl;
+ break;
+ }
+ }
+done:
+ return result;
+}
+
+static int remap_cal_data(int32_t cal_type, struct cal_block_data *cal_block)
+{
+ int ret = 0;
+
+ if (cal_block->map_data.ion_client == NULL) {
+ pr_err("%s: No ION allocation for cal type %d!\n",
+ __func__, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((cal_block->map_data.map_size > 0) &&
+ (cal_block->map_data.q6map_handle == 0)) {
+
+ ret = q6asm_map_cal_memory(cal_type, cal_block);
+ if (ret < 0) {
+ pr_err("%s: mmap did not work! size = %zd ret %d\n",
+ __func__, cal_block->map_data.map_size, ret);
+ goto done;
+ }
+ }
+done:
+ return ret;
+}
+
+static int q6asm_unmap_cal_memory(int32_t cal_type,
+ struct cal_block_data *cal_block)
+{
+ int result = 0;
+ int result2 = 0;
+
+ if (cal_block == NULL) {
+ pr_err("%s: cal_block is NULL!\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block->map_data.q6map_handle == 0) {
+ pr_debug("%s: No address to unmap!\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (common_client.mmap_apr == NULL) {
+ common_client.mmap_apr = q6asm_mmap_apr_reg();
+ if (common_client.mmap_apr == NULL) {
+ pr_err("%s: q6asm_mmap_apr_reg failed\n",
+ __func__);
+ result = -EPERM;
+ goto done;
+ }
+ }
+
+ result2 = q6asm_memory_unmap_regions(&common_client, IN);
+ if (result2 < 0) {
+ pr_err("%s: unmap failed, err %d\n",
+ __func__, result2);
+ result = result2;
+ }
+
+ cal_block->map_data.q6map_handle = 0;
+done:
+ return result;
+}
+
+int q6asm_unmap_cal_data(int cal_type, struct cal_block_data *cal_block)
+{
+ int ret = 0;
+
+ if ((cal_block->map_data.map_size > 0) &&
+ (cal_block->map_data.q6map_handle != 0)) {
+
+ ret = q6asm_unmap_cal_memory(cal_type, cal_block);
+ if (ret < 0) {
+ pr_err("%s: unmap did not work! size = %zd ret %d\n",
+ __func__, cal_block->map_data.map_size, ret);
+ goto done;
+ }
+ }
+done:
+ return ret;
+}
+
+int send_asm_custom_topology(struct audio_client *ac)
+{
+ struct cal_block_data *cal_block = NULL;
+ struct cmd_set_topologies asm_top;
+ int result = 0;
+ int result1 = 0;
+
+ if (cal_data[ASM_CUSTOM_TOP_CAL] == NULL)
+ goto done;
+
+ mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+ if (!set_custom_topology)
+ goto unlock;
+ set_custom_topology = 0;
+
+ cal_block = cal_utils_get_only_cal_block(cal_data[ASM_CUSTOM_TOP_CAL]);
+ if (cal_block == NULL)
+ goto unlock;
+
+ if (cal_block->cal_data.size == 0) {
+ pr_debug("%s: No cal to send!\n", __func__);
+ goto unlock;
+ }
+
+ pr_debug("%s: Sending cal_index %d\n", __func__, ASM_CUSTOM_TOP_CAL);
+
+ result = remap_cal_data(ASM_CUST_TOPOLOGY_CAL_TYPE, cal_block);
+ if (result) {
+ pr_err("%s: Remap_cal_data failed for cal %d!\n",
+ __func__, ASM_CUSTOM_TOP_CAL);
+ goto unlock;
+ }
+
+ q6asm_add_hdr_custom_topology(ac, &asm_top.hdr, sizeof(asm_top));
+ atomic_set(&ac->mem_state, -1);
+ asm_top.hdr.opcode = ASM_CMD_ADD_TOPOLOGIES;
+ asm_top.payload_addr_lsw = lower_32_bits(cal_block->cal_data.paddr);
+ asm_top.payload_addr_msw = msm_audio_populate_upper_32_bits(
+ cal_block->cal_data.paddr);
+ asm_top.mem_map_handle = cal_block->map_data.q6map_handle;
+ asm_top.payload_size = cal_block->cal_data.size;
+
+ pr_debug("%s: Sending ASM_CMD_ADD_TOPOLOGIES payload = %pK, size = %d, map handle = 0x%x\n",
+ __func__, &cal_block->cal_data.paddr,
+ asm_top.payload_size, asm_top.mem_map_handle);
+
+ result = apr_send_pkt(ac->apr, (uint32_t *) &asm_top);
+ if (result < 0) {
+ pr_err("%s: Set topologies failed result %d\n",
+ __func__, result);
+ pr_debug("%s: Set topologies failed payload = 0x%pK\n",
+ __func__, &cal_block->cal_data.paddr);
+ goto unmap;
+
+ }
+
+ result = wait_event_timeout(ac->mem_wait,
+ (atomic_read(&ac->mem_state) >= 0), 5*HZ);
+ if (!result) {
+ pr_err("%s: Set topologies failed timeout\n", __func__);
+ pr_debug("%s: Set topologies failed after timedout payload = 0x%pK\n",
+ __func__, &cal_block->cal_data.paddr);
+ result = -ETIMEDOUT;
+ goto unmap;
+ }
+ if (atomic_read(&ac->mem_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->mem_state)));
+ result = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->mem_state));
+ goto unmap;
+ }
+
+unmap:
+ result1 = q6asm_unmap_cal_memory(ASM_CUST_TOPOLOGY_CAL_TYPE,
+ cal_block);
+ if (result1 < 0) {
+ result = result1;
+ pr_debug("%s: unmap cal failed! %d\n", __func__, result);
+ }
+unlock:
+ mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+done:
+ return result;
+}
+
+int q6asm_map_rtac_block(struct rtac_cal_block_data *cal_block)
+{
+ int result = 0;
+ struct asm_buffer_node *buf_node = NULL;
+ struct list_head *ptr, *next;
+ pr_debug("%s:\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("%s: cal_block is NULL!\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block->cal_data.paddr == 0) {
+ pr_debug("%s: No address to map!\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (common_client.mmap_apr == NULL) {
+ common_client.mmap_apr = q6asm_mmap_apr_reg();
+ if (common_client.mmap_apr == NULL) {
+ pr_err("%s: q6asm_mmap_apr_reg failed\n",
+ __func__);
+ result = -EPERM;
+ goto done;
+ }
+ }
+
+ if (cal_block->map_data.map_size == 0) {
+ pr_debug("%s: map size is 0!\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ /* Use second asm buf to map memory */
+ if (common_client.port[OUT].buf == NULL) {
+ pr_err("%s: common buf is NULL\n",
+ __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ common_client.port[OUT].buf->phys = cal_block->cal_data.paddr;
+
+ result = q6asm_memory_map_regions(&common_client,
+ OUT, cal_block->map_data.map_size, 1, 1);
+ if (result < 0) {
+ pr_err("%s: mmap did not work! size = %d result %d\n",
+ __func__,
+ cal_block->map_data.map_size, result);
+ pr_debug("%s: mmap did not work! addr = 0x%pK, size = %d\n",
+ __func__,
+ &cal_block->cal_data.paddr,
+ cal_block->map_data.map_size);
+ goto done;
+ }
+
+ list_for_each_safe(ptr, next,
+ &common_client.port[OUT].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_phys_addr == cal_block->cal_data.paddr) {
+ cal_block->map_data.map_handle = buf_node->mmap_hdl;
+ break;
+ }
+ }
+done:
+ return result;
+}
+
+int q6asm_unmap_rtac_block(uint32_t *mem_map_handle)
+{
+ int result = 0;
+ int result2 = 0;
+ pr_debug("%s:\n", __func__);
+
+ if (mem_map_handle == NULL) {
+ pr_debug("%s: Map handle is NULL, nothing to unmap\n",
+ __func__);
+ goto done;
+ }
+
+ if (*mem_map_handle == 0) {
+ pr_debug("%s: Map handle is 0, nothing to unmap\n",
+ __func__);
+ goto done;
+ }
+
+ if (common_client.mmap_apr == NULL) {
+ common_client.mmap_apr = q6asm_mmap_apr_reg();
+ if (common_client.mmap_apr == NULL) {
+ pr_err("%s: q6asm_mmap_apr_reg failed\n",
+ __func__);
+ result = -EPERM;
+ goto done;
+ }
+ }
+
+
+ result2 = q6asm_memory_unmap_regions(&common_client, OUT);
+ if (result2 < 0) {
+ pr_err("%s: unmap failed, err %d\n",
+ __func__, result2);
+ result = result2;
+ } else {
+ *mem_map_handle = 0;
+ }
+
+ result2 = q6asm_mmap_apr_dereg();
+ if (result2 < 0) {
+ pr_err("%s: q6asm_mmap_apr_dereg failed, err %d\n",
+ __func__, result2);
+ result = result2;
+ }
+done:
+ return result;
+}
+
+int q6asm_audio_client_buf_free(unsigned int dir,
+ struct audio_client *ac)
+{
+ struct audio_port_data *port;
+ int cnt = 0;
+ int rc = 0;
+ pr_debug("%s: Session id %d\n", __func__, ac->session);
+ mutex_lock(&ac->cmd_lock);
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[dir];
+ if (!port->buf) {
+ pr_err("%s: buf NULL\n", __func__);
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+ }
+ cnt = port->max_buf_cnt - 1;
+
+ if (cnt >= 0) {
+ rc = q6asm_memory_unmap_regions(ac, dir);
+ if (rc < 0)
+ pr_err("%s: Memory_unmap_regions failed %d\n",
+ __func__, rc);
+ }
+
+ while (cnt >= 0) {
+ if (port->buf[cnt].data) {
+ if (!rc || atomic_read(&ac->reset))
+ msm_audio_ion_free(
+ port->buf[cnt].client,
+ port->buf[cnt].handle);
+
+ port->buf[cnt].client = NULL;
+ port->buf[cnt].handle = NULL;
+ port->buf[cnt].data = NULL;
+ port->buf[cnt].phys = 0;
+ --(port->max_buf_cnt);
+ }
+ --cnt;
+ }
+ kfree(port->buf);
+ port->buf = NULL;
+ }
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+}
+
+int q6asm_audio_client_buf_free_contiguous(unsigned int dir,
+ struct audio_client *ac)
+{
+ struct audio_port_data *port;
+ int cnt = 0;
+ int rc = 0;
+ pr_debug("%s: Session id %d\n", __func__, ac->session);
+ mutex_lock(&ac->cmd_lock);
+ port = &ac->port[dir];
+ if (!port->buf) {
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+ }
+ cnt = port->max_buf_cnt - 1;
+
+ if (cnt >= 0) {
+ rc = q6asm_memory_unmap(ac, port->buf[0].phys, dir);
+ if (rc < 0)
+ pr_err("%s: Memory_unmap_regions failed %d\n",
+ __func__, rc);
+ }
+
+ if (port->buf[0].data) {
+ pr_debug("%s: data[%pK]phys[%pK][%pK] , client[%pK] handle[%pK]\n",
+ __func__,
+ port->buf[0].data,
+ &port->buf[0].phys,
+ &port->buf[0].phys,
+ port->buf[0].client,
+ port->buf[0].handle);
+ if (!rc || atomic_read(&ac->reset))
+ msm_audio_ion_free(port->buf[0].client,
+ port->buf[0].handle);
+ port->buf[0].client = NULL;
+ port->buf[0].handle = NULL;
+ }
+
+ while (cnt >= 0) {
+ port->buf[cnt].data = NULL;
+ port->buf[cnt].phys = 0;
+ cnt--;
+ }
+ port->max_buf_cnt = 0;
+ kfree(port->buf);
+ port->buf = NULL;
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+}
+
+void q6asm_audio_client_free(struct audio_client *ac)
+{
+ int loopcnt;
+ struct audio_port_data *port;
+
+ if (!ac) {
+ pr_err("%s: ac %pK\n", __func__, ac);
+ return;
+ }
+ if (!ac->session) {
+ pr_err("%s: ac session invalid\n", __func__);
+ return;
+ }
+
+ mutex_lock(&session_lock);
+
+ pr_debug("%s: Session id %d\n", __func__, ac->session);
+ if (ac->io_mode & SYNC_IO_MODE) {
+ for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
+ port = &ac->port[loopcnt];
+ if (!port->buf)
+ continue;
+ pr_debug("%s: loopcnt = %d\n",
+ __func__, loopcnt);
+ q6asm_audio_client_buf_free(loopcnt, ac);
+ }
+ }
+
+ q6asm_session_deregister(ac);
+ q6asm_session_free(ac);
+
+ pr_debug("%s: APR De-Register\n", __func__);
+
+/*done:*/
+ mutex_unlock(&session_lock);
+
+ return;
+}
+
+int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode1)
+{
+ uint32_t mode;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ac->io_mode &= 0xFF00;
+ mode = (mode1 & 0xF);
+
+ pr_debug("%s: ac->mode after anding with FF00:0x%x,\n",
+ __func__, ac->io_mode);
+
+ if ((mode == ASYNC_IO_MODE) || (mode == SYNC_IO_MODE)) {
+ ac->io_mode |= mode1;
+ pr_debug("%s: Set Mode to 0x%x\n", __func__, ac->io_mode);
+ return 0;
+ } else {
+ pr_err("%s: Not an valid IO Mode:%d\n", __func__, ac->io_mode);
+ return -EINVAL;
+ }
+}
+
+void *q6asm_mmap_apr_reg(void)
+{
+ if ((atomic_read(&this_mmap.ref_cnt) == 0) ||
+ (this_mmap.apr == NULL)) {
+ this_mmap.apr = apr_register("ADSP", "ASM", \
+ (apr_fn)q6asm_srvc_callback,\
+ 0x0FFFFFFFF, &this_mmap);
+ if (this_mmap.apr == NULL) {
+ pr_debug("%s: Unable to register APR ASM common port\n",
+ __func__);
+ goto fail;
+ }
+ }
+ atomic_inc(&this_mmap.ref_cnt);
+
+ return this_mmap.apr;
+fail:
+ return NULL;
+}
+
+int q6asm_send_stream_cmd(struct audio_client *ac,
+ struct msm_adsp_event_data *data)
+{
+ char *asm_params = NULL;
+ struct apr_hdr hdr;
+ int rc;
+ uint32_t sz = 0;
+ uint64_t actual_sz = 0;
+
+ if (!data || !ac) {
+ pr_err("%s: %s is NULL\n", __func__,
+ (!data) ? "data" : "ac");
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (data->event_type >= ARRAY_SIZE(adsp_reg_event_opcode)) {
+ pr_err("%s: event %u out of boundary of array size of (%lu)\n",
+ __func__, data->event_type,
+ (long)ARRAY_SIZE(adsp_reg_event_opcode));
+ rc = -EINVAL;
+ goto done;
+ }
+
+ actual_sz = sizeof(struct apr_hdr) + data->payload_len;
+ if (actual_sz > U32_MAX) {
+ pr_err("%s: payload size 0x%X exceeds limit\n",
+ __func__, data->payload_len);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ sz = (uint32_t)actual_sz;
+ asm_params = kzalloc(sz, GFP_KERNEL);
+ if (!asm_params) {
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ q6asm_add_hdr_async(ac, &hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state_pp, -1);
+ hdr.opcode = adsp_reg_event_opcode[data->event_type];
+ memcpy(asm_params, &hdr, sizeof(struct apr_hdr));
+ memcpy(asm_params + sizeof(struct apr_hdr),
+ data->payload, data->payload_len);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params);
+ if (rc < 0) {
+ pr_err("%s: stream event cmd apr pkt failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_send_param;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state_pp) >= 0), 1 * HZ);
+ if (!rc) {
+ pr_err("%s: timeout for stream event cmd resp\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_send_param;
+ }
+
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
+ pr_err("%s: DSP returned error[%s] for stream event cmd\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state_pp)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state_pp));
+ goto fail_send_param;
+ }
+
+ rc = 0;
+fail_send_param:
+ kfree(asm_params);
+done:
+ return rc;
+}
+
+struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
+{
+ struct audio_client *ac;
+ int n;
+ int lcnt = 0;
+ int rc = 0;
+
+ ac = kzalloc(sizeof(struct audio_client), GFP_KERNEL);
+ if (!ac) {
+ pr_err("%s: client not present\n", __func__);
+ return NULL;
+ }
+
+ mutex_lock(&session_lock);
+ n = q6asm_session_alloc(ac);
+ if (n <= 0) {
+ pr_err("%s: ASM Session alloc fail n=%d\n", __func__, n);
+ mutex_unlock(&session_lock);
+ kfree(ac);
+ goto fail_session;
+ }
+ ac->session = n;
+ ac->cb = cb;
+ ac->path_delay = UINT_MAX;
+ ac->priv = priv;
+ ac->io_mode = SYNC_IO_MODE;
+ ac->perf_mode = LEGACY_PCM_MODE;
+ ac->fptr_cache_ops = NULL;
+ /* DSP expects stream id from 1 */
+ ac->stream_id = 1;
+
+ rc = q6asm_session_register(ac);
+ if (rc < 0) {
+ mutex_unlock(&session_lock);
+ goto fail_register;
+ }
+
+ init_waitqueue_head(&ac->cmd_wait);
+ init_waitqueue_head(&ac->time_wait);
+ init_waitqueue_head(&ac->mem_wait);
+ atomic_set(&ac->time_flag, 1);
+ atomic_set(&ac->reset, 0);
+ INIT_LIST_HEAD(&ac->port[0].mem_map_handle);
+ INIT_LIST_HEAD(&ac->port[1].mem_map_handle);
+ pr_debug("%s: mem_map_handle list init'ed\n", __func__);
+ mutex_init(&ac->cmd_lock);
+ for (lcnt = 0; lcnt <= OUT; lcnt++) {
+ mutex_init(&ac->port[lcnt].lock);
+ spin_lock_init(&ac->port[lcnt].dsp_lock);
+ }
+ atomic_set(&ac->cmd_state, 0);
+ atomic_set(&ac->cmd_state_pp, 0);
+ atomic_set(&ac->mem_state, 0);
+
+ rc = send_asm_custom_topology(ac);
+ if (rc < 0) {
+ mutex_unlock(&session_lock);
+ goto fail_send;
+ }
+
+ pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+ mutex_unlock(&session_lock);
+
+ return ac;
+fail_send:
+ q6asm_session_deregister(ac);
+fail_register:
+ q6asm_session_free(ac);
+fail_session:
+ return NULL;
+}
+
+struct audio_client *q6asm_get_audio_client(int session_id)
+{
+ if (session_id == ASM_CONTROL_SESSION) {
+ return &common_client;
+ }
+
+ if ((session_id <= 0) || (session_id > ASM_ACTIVE_STREAMS_ALLOWED)) {
+ pr_err("%s: invalid session: %d\n", __func__, session_id);
+ goto err;
+ }
+
+ if (!(session[session_id].ac)) {
+ pr_err("%s: session not active: %d\n", __func__, session_id);
+ goto err;
+ }
+ return session[session_id].ac;
+err:
+ return NULL;
+}
+
+int q6asm_audio_client_buf_alloc(unsigned int dir,
+ struct audio_client *ac,
+ unsigned int bufsz,
+ uint32_t bufcnt)
+{
+ int cnt = 0;
+ int rc = 0;
+ struct audio_buffer *buf;
+ size_t len;
+
+ if (!(ac) || !(bufsz) || ((dir != IN) && (dir != OUT))) {
+ pr_err("%s: ac %pK bufsz %d dir %d\n", __func__, ac, bufsz,
+ dir);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n", __func__, ac->session,
+ bufsz, bufcnt);
+
+ if (ac->session <= 0 || ac->session > ASM_ACTIVE_STREAMS_ALLOWED) {
+ pr_err("%s: Session ID is invalid, session = %d\n", __func__,
+ ac->session);
+ goto fail;
+ }
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ if (ac->port[dir].buf) {
+ pr_debug("%s: buffer already allocated\n", __func__);
+ return 0;
+ }
+ mutex_lock(&ac->cmd_lock);
+ if (bufcnt > (U32_MAX/sizeof(struct audio_buffer))) {
+ pr_err("%s: Buffer size overflows", __func__);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+ buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt),
+ GFP_KERNEL);
+
+ if (!buf) {
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+
+ ac->port[dir].buf = buf;
+
+ while (cnt < bufcnt) {
+ if (bufsz > 0) {
+ if (!buf[cnt].data) {
+ rc = msm_audio_ion_alloc("asm_client",
+ &buf[cnt].client, &buf[cnt].handle,
+ bufsz,
+ (ion_phys_addr_t *)&buf[cnt].phys,
+ &len,
+ &buf[cnt].data);
+ if (rc) {
+ pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
+ __func__, rc);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+
+ buf[cnt].used = 1;
+ buf[cnt].size = bufsz;
+ buf[cnt].actual_size = bufsz;
+ pr_debug("%s: data[%pK]phys[%pK][%pK]\n",
+ __func__,
+ buf[cnt].data,
+ &buf[cnt].phys,
+ &buf[cnt].phys);
+ cnt++;
+ }
+ }
+ }
+ ac->port[dir].max_buf_cnt = cnt;
+
+ mutex_unlock(&ac->cmd_lock);
+ rc = q6asm_memory_map_regions(ac, dir, bufsz, cnt, 0);
+ if (rc < 0) {
+ pr_err("%s: CMD Memory_map_regions failed %d for size %d\n",
+ __func__, rc, bufsz);
+ goto fail;
+ }
+ }
+ return 0;
+fail:
+ q6asm_audio_client_buf_free(dir, ac);
+ return -EINVAL;
+}
+
+int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir,
+ struct audio_client *ac,
+ unsigned int bufsz,
+ unsigned int bufcnt)
+{
+ int cnt = 0;
+ int rc = 0;
+ struct audio_buffer *buf;
+ size_t len;
+ int bytes_to_alloc;
+
+ if (!(ac) || ((dir != IN) && (dir != OUT))) {
+ pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n",
+ __func__, ac->session,
+ bufsz, bufcnt);
+
+ if (ac->session <= 0 || ac->session > ASM_ACTIVE_STREAMS_ALLOWED) {
+ pr_err("%s: Session ID is invalid, session = %d\n", __func__,
+ ac->session);
+ goto fail;
+ }
+
+ if (ac->port[dir].buf) {
+ pr_err("%s: buffer already allocated\n", __func__);
+ return 0;
+ }
+
+ if (bufcnt == 0) {
+ pr_err("%s: invalid buffer count\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&ac->cmd_lock);
+ buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt),
+ GFP_KERNEL);
+
+ if (!buf) {
+ pr_err("%s: buffer allocation failed\n", __func__);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+
+ ac->port[dir].buf = buf;
+
+ /* check for integer overflow */
+ if ((bufcnt > 0) && ((INT_MAX / bufcnt) < bufsz)) {
+ pr_err("%s: integer overflow\n", __func__);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+ bytes_to_alloc = bufsz * bufcnt;
+
+ /* The size to allocate should be multiple of 4K bytes */
+ bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc);
+
+ rc = msm_audio_ion_alloc("asm_client", &buf[0].client, &buf[0].handle,
+ bytes_to_alloc,
+ (ion_phys_addr_t *)&buf[0].phys, &len,
+ &buf[0].data);
+ if (rc) {
+ pr_err("%s: Audio ION alloc is failed, rc = %d\n",
+ __func__, rc);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+
+ buf[0].used = dir ^ 1;
+ buf[0].size = bufsz;
+ buf[0].actual_size = bufsz;
+ cnt = 1;
+ while (cnt < bufcnt) {
+ if (bufsz > 0) {
+ buf[cnt].data = buf[0].data + (cnt * bufsz);
+ buf[cnt].phys = buf[0].phys + (cnt * bufsz);
+ if (!buf[cnt].data) {
+ pr_err("%s: Buf alloc failed\n",
+ __func__);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+ buf[cnt].used = dir ^ 1;
+ buf[cnt].size = bufsz;
+ buf[cnt].actual_size = bufsz;
+ pr_debug("%s: data[%pK]phys[%pK][%pK]\n",
+ __func__,
+ buf[cnt].data,
+ &buf[cnt].phys,
+ &buf[cnt].phys);
+ }
+ cnt++;
+ }
+ ac->port[dir].max_buf_cnt = cnt;
+ mutex_unlock(&ac->cmd_lock);
+ rc = q6asm_memory_map_regions(ac, dir, bufsz, cnt, 1);
+ if (rc < 0) {
+ pr_err("%s: CMD Memory_map_regions failed %d for size %d\n",
+ __func__, rc, bufsz);
+ goto fail;
+ }
+ return 0;
+fail:
+ q6asm_audio_client_buf_free_contiguous(dir, ac);
+ return -EINVAL;
+}
+
+static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
+{
+ uint32_t dir = 0;
+ uint32_t i = IN;
+ uint32_t *payload;
+ unsigned long dsp_flags;
+ unsigned long flags;
+ struct asm_buffer_node *buf_node = NULL;
+ struct list_head *ptr, *next;
+ union asm_token_struct asm_token;
+
+ struct audio_client *ac = NULL;
+ struct audio_port_data *port;
+
+ int session_id;
+
+ if (!data) {
+ pr_err("%s: Invalid CB\n", __func__);
+ return 0;
+ }
+
+ payload = data->payload;
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("%s: Reset event is received: %d %d apr[%pK]\n",
+ __func__,
+ data->reset_event,
+ data->reset_proc,
+ this_mmap.apr);
+ atomic_set(&this_mmap.ref_cnt, 0);
+ apr_reset(this_mmap.apr);
+ this_mmap.apr = NULL;
+ for (; i <= OUT; i++) {
+ list_for_each_safe(ptr, next,
+ &common_client.port[i].mem_map_handle) {
+ buf_node = list_entry(ptr,
+ struct asm_buffer_node,
+ list);
+ if (buf_node->buf_phys_addr ==
+ common_client.port[i].buf->phys) {
+ list_del(&buf_node->list);
+ kfree(buf_node);
+ }
+ }
+ pr_debug("%s: Clearing custom topology\n", __func__);
+ }
+
+ cal_utils_clear_cal_block_q6maps(ASM_MAX_CAL_TYPES, cal_data);
+ common_client.mmap_apr = NULL;
+ mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+ set_custom_topology = 1;
+ mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+ topology_map_handle = 0;
+ rtac_clear_mapping(ASM_RTAC_CAL);
+ return 0;
+ }
+
+ asm_token.token = data->token;
+ session_id = asm_token._token.session_id;
+
+ if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED))
+ spin_lock_irqsave(&(session[session_id].session_lock), flags);
+
+ ac = q6asm_get_audio_client(session_id);
+ dir = q6asm_get_flag_from_token(&asm_token, ASM_DIRECTION_OFFSET);
+
+ if (!ac) {
+ pr_debug("%s: session[%d] already freed\n",
+ __func__, session_id);
+ if ((session_id > 0 &&
+ session_id <= ASM_ACTIVE_STREAMS_ALLOWED))
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+ return 0;
+ }
+
+ if (data->payload_size >= 2 * sizeof(uint32_t)) {
+ pr_debug("%s:ptr0[0x%x]ptr1[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n",
+ __func__, payload[0], payload[1], data->opcode,
+ data->token, data->payload_size, data->src_port,
+ data->dest_port, asm_token._token.session_id, dir);
+ pr_debug("%s:Payload = [0x%x] status[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ } else if (data->payload_size == sizeof(uint32_t)) {
+ pr_debug("%s:ptr0[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n",
+ __func__, payload[0], data->opcode,
+ data->token, data->payload_size, data->src_port,
+ data->dest_port, asm_token._token.session_id, dir);
+ pr_debug("%s:Payload = [0x%x]\n",
+ __func__, payload[0]);
+ }
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ switch (payload[0]) {
+ case ASM_CMD_SHARED_MEM_MAP_REGIONS:
+ case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:
+ case ASM_CMD_ADD_TOPOLOGIES:
+ if (data->payload_size >= 2 * sizeof(uint32_t)
+ && payload[1] != 0) {
+ pr_err("%s: cmd = 0x%x returned error = 0x%x sid:%d\n",
+ __func__, payload[0], payload[1],
+ asm_token._token.session_id);
+ if (payload[0] ==
+ ASM_CMD_SHARED_MEM_UNMAP_REGIONS)
+ atomic_set(&ac->unmap_cb_success, 0);
+
+ atomic_set(&ac->mem_state, payload[1]);
+ wake_up(&ac->mem_wait);
+ } else {
+ if (payload[0] ==
+ ASM_CMD_SHARED_MEM_UNMAP_REGIONS)
+ atomic_set(&ac->unmap_cb_success, 1);
+ }
+
+ if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1)
+ wake_up(&ac->mem_wait);
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ else
+ dev_vdbg(ac->dev, "%s: Payload size of %d is less than expected.\n",
+ __func__, data->payload_size);
+ break;
+ default:
+ pr_debug("%s: command[0x%x] not expecting rsp\n",
+ __func__, payload[0]);
+ break;
+ }
+ if ((session_id > 0 &&
+ session_id <= ASM_ACTIVE_STREAMS_ALLOWED))
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+ return 0;
+ }
+
+ port = &ac->port[dir];
+
+ switch (data->opcode) {
+ case ASM_CMDRSP_SHARED_MEM_MAP_REGIONS:{
+ pr_debug("%s:PL#0[0x%x] dir=0x%x s_id=0x%x\n",
+ __func__, payload[0], dir, asm_token._token.session_id);
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1) {
+ ac->port[dir].tmp_hdl = payload[0];
+ wake_up(&ac->mem_wait);
+ }
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+ break;
+ }
+ case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:{
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ pr_debug("%s: PL#0[0x%x]PL#1 [0x%x]\n",
+ __func__, payload[0], payload[1]);
+ else
+ pr_debug("%s: Payload size of %d is less than expected.\n",
+ __func__, data->payload_size);
+
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1)
+ wake_up(&ac->mem_wait);
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+
+ break;
+ }
+ default:
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ pr_debug("%s: command[0x%x]success [0x%x]\n",
+ __func__, payload[0], payload[1]);
+ else
+ pr_debug("%s: Payload size of %d is less than expected.\n",
+ __func__, data->payload_size);
+ }
+ if (ac->cb)
+ ac->cb(data->opcode, data->token,
+ data->payload, ac->priv);
+ if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED))
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+
+ return 0;
+}
+
+static void q6asm_process_mtmx_get_param_rsp(struct audio_client *ac,
+ struct asm_mtmx_strtr_get_params_cmdrsp *cmdrsp)
+{
+ struct asm_session_mtmx_strtr_param_session_time_v3_t *time;
+
+ if (cmdrsp->err_code) {
+ dev_err_ratelimited(ac->dev,
+ "%s: err=%x, mod_id=%x, param_id=%x\n",
+ __func__, cmdrsp->err_code,
+ cmdrsp->param_info.module_id,
+ cmdrsp->param_info.param_id);
+ return;
+ } else {
+ dev_dbg_ratelimited(ac->dev,
+ "%s: mod_id=%x, param_id=%x\n", __func__,
+ cmdrsp->param_info.module_id,
+ cmdrsp->param_info.param_id);
+ }
+
+ switch (cmdrsp->param_info.module_id) {
+ case ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC:
+ switch (cmdrsp->param_info.param_id) {
+ case ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3:
+ time = &cmdrsp->param_data.session_time;
+ dev_vdbg(ac->dev, "%s: GET_TIME_V3, time_lsw=%x, time_msw=%x\n",
+ __func__, time->session_time_lsw,
+ time->session_time_msw);
+ ac->time_stamp = (uint64_t)(((uint64_t)
+ time->session_time_msw << 32) |
+ time->session_time_lsw);
+ if (time->flags &
+ ASM_SESSION_MTMX_STRTR_PARAM_STIME_TSTMP_FLG_BMASK)
+ dev_warn_ratelimited(ac->dev,
+ "%s: recv inval tstmp\n",
+ __func__);
+ if (atomic_cmpxchg(&ac->time_flag, 1, 0))
+ wake_up(&ac->time_wait);
+
+ break;
+ default:
+ dev_err(ac->dev, "%s: unexpected param_id %x\n",
+ __func__, cmdrsp->param_info.param_id);
+ break;
+ }
+ break;
+ default:
+ dev_err(ac->dev, "%s: unexpected mod_id %x\n", __func__,
+ cmdrsp->param_info.module_id);
+ break;
+ }
+}
+
+static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
+{
+ int i = 0;
+ struct audio_client *ac = (struct audio_client *)priv;
+ unsigned long dsp_flags;
+ uint32_t *payload;
+ uint32_t wakeup_flag = 1;
+ int32_t ret = 0;
+ union asm_token_struct asm_token;
+ uint8_t buf_index;
+ struct msm_adsp_event_data *pp_event_package = NULL;
+ uint32_t payload_size = 0;
+ unsigned long flags;
+ int session_id;
+
+ if (ac == NULL) {
+ pr_err("%s: ac NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (data == NULL) {
+ pr_err("%s: data NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ session_id = q6asm_get_session_id_from_audio_client(ac);
+ if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) {
+ pr_err("%s: Session ID is invalid, session = %d\n", __func__,
+ session_id);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&(session[session_id].session_lock), flags);
+
+ if (!q6asm_is_valid_audio_client(ac)) {
+ pr_err("%s: audio client pointer is invalid, ac = %pK\n",
+ __func__, ac);
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+ return -EINVAL;
+ }
+
+ payload = data->payload;
+ asm_token.token = data->token;
+ if (q6asm_get_flag_from_token(&asm_token, ASM_CMD_NO_WAIT_OFFSET)) {
+ pr_debug("%s: No wait command opcode[0x%x] cmd_opcode:%x\n",
+ __func__, data->opcode, payload ? payload[0] : 0);
+ wakeup_flag = 0;
+ }
+
+ if (data->opcode == RESET_EVENTS) {
+ atomic_set(&ac->reset, 1);
+ if (ac->apr == NULL) {
+ ac->apr = ac->apr2;
+ ac->apr2 = NULL;
+ }
+ pr_debug("%s: Reset event is received: %d %d apr[%pK]\n",
+ __func__,
+ data->reset_event, data->reset_proc, ac->apr);
+ if (ac->cb)
+ ac->cb(data->opcode, data->token,
+ (uint32_t *)data->payload, ac->priv);
+ apr_reset(ac->apr);
+ ac->apr = NULL;
+ atomic_set(&ac->time_flag, 0);
+ atomic_set(&ac->cmd_state, 0);
+ atomic_set(&ac->mem_state, 0);
+ atomic_set(&ac->cmd_state_pp, 0);
+ wake_up(&ac->time_wait);
+ wake_up(&ac->cmd_wait);
+ wake_up(&ac->mem_wait);
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+ return 0;
+ }
+
+ dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x] token[0x%x]payload_size[%d] src[%d] dest[%d]\n",
+ __func__,
+ ac->session, data->opcode,
+ data->token, data->payload_size, data->src_port,
+ data->dest_port);
+ if ((data->opcode != ASM_DATA_EVENT_RENDERED_EOS) &&
+ (data->opcode != ASM_DATA_EVENT_EOS) &&
+ (data->opcode != ASM_SESSION_EVENTX_OVERFLOW) &&
+ (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) {
+ if (payload == NULL) {
+ pr_err("%s: payload is null\n", __func__);
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+ return -EINVAL;
+ }
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n",
+ __func__, payload[0], payload[1], data->opcode);
+ else
+ dev_vdbg(ac->dev, "%s: Payload size of %d is less than expected.\n",
+ __func__, data->payload_size);
+ }
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ switch (payload[0]) {
+ case ASM_STREAM_CMD_SET_PP_PARAMS_V2:
+ case ASM_STREAM_CMD_SET_PP_PARAMS_V3:
+ if (rtac_make_asm_callback(ac->session, payload,
+ data->payload_size))
+ break;
+ case ASM_SESSION_CMD_PAUSE:
+ case ASM_SESSION_CMD_SUSPEND:
+ case ASM_DATA_CMD_EOS:
+ case ASM_STREAM_CMD_CLOSE:
+ case ASM_STREAM_CMD_FLUSH:
+ case ASM_SESSION_CMD_RUN_V2:
+ case ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS:
+ case ASM_STREAM_CMD_FLUSH_READBUFS:
+ pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] src %d dest %d\n",
+ __func__, ac->session, data->opcode, data->token,
+ payload[0], data->src_port, data->dest_port);
+ ret = q6asm_is_valid_session(data, priv);
+ if (ret != 0) {
+ pr_err("%s: session invalid %d\n", __func__, ret);
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+ return ret;
+ }
+ case ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2:
+ case ASM_STREAM_CMD_OPEN_READ_V3:
+ case ASM_STREAM_CMD_OPEN_WRITE_V3:
+ case ASM_STREAM_CMD_OPEN_PULL_MODE_WRITE:
+ case ASM_STREAM_CMD_OPEN_PUSH_MODE_READ:
+ case ASM_STREAM_CMD_OPEN_READWRITE_V2:
+ case ASM_STREAM_CMD_OPEN_LOOPBACK_V2:
+ case ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK:
+ case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
+ case ASM_DATA_CMD_IEC_60958_MEDIA_FMT:
+ case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+ case ASM_STREAM_CMD_SET_ENCDEC_PARAM_V2:
+ case ASM_STREAM_CMD_REGISTER_ENCDEC_EVENTS:
+ case ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE:
+ case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:
+ case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:
+ case ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS:
+ case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED:
+ if (data->payload_size >=
+ 2 * sizeof(uint32_t) &&
+ payload[1] != 0) {
+ pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] stat 0x%x src %d dest %d\n",
+ __func__, ac->session,
+ data->opcode, data->token,
+ payload[0], payload[1],
+ data->src_port, data->dest_port);
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__,
+ payload[0],
+ payload[1]);
+ if (wakeup_flag) {
+ if ((is_adsp_reg_event(payload[0]) >= 0)
+ || (payload[0] ==
+ ASM_STREAM_CMD_SET_PP_PARAMS_V2)
+ || (payload[0] ==
+ ASM_STREAM_CMD_SET_PP_PARAMS_V3))
+ atomic_set(
+ &ac->cmd_state_pp,
+ payload[1]);
+ else
+ atomic_set(
+ &ac->cmd_state,
+ payload[1]);
+ wake_up(&ac->cmd_wait);
+ }
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock),
+ flags);
+ return 0;
+ } else {
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ }
+ if ((is_adsp_reg_event(payload[0]) >= 0) ||
+ (payload[0] == ASM_STREAM_CMD_SET_PP_PARAMS_V2) ||
+ (payload[0] == ASM_STREAM_CMD_SET_PP_PARAMS_V3)) {
+ if (atomic_read(&ac->cmd_state_pp) &&
+ wakeup_flag) {
+ atomic_set(&ac->cmd_state_pp, 0);
+ wake_up(&ac->cmd_wait);
+ }
+ } else {
+ if (atomic_read(&ac->cmd_state) &&
+ wakeup_flag) {
+ atomic_set(&ac->cmd_state, 0);
+ wake_up(&ac->cmd_wait);
+ }
+ }
+ if (ac->cb)
+ ac->cb(data->opcode, data->token,
+ (uint32_t *)data->payload, ac->priv);
+ break;
+ case ASM_CMD_ADD_TOPOLOGIES:
+ if (data->payload_size >=
+ 2 * sizeof(uint32_t) &&
+ payload[1] != 0) {
+ pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, payload[0], payload[1]);
+ if (wakeup_flag) {
+ atomic_set(&ac->mem_state, payload[1]);
+ wake_up(&ac->mem_wait);
+ }
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock),
+ flags);
+ return 0;
+ }
+ if (atomic_read(&ac->mem_state) && wakeup_flag) {
+ atomic_set(&ac->mem_state, 0);
+ wake_up(&ac->mem_wait);
+ }
+ if (ac->cb)
+ ac->cb(data->opcode, data->token,
+ (uint32_t *)data->payload, ac->priv);
+ break;
+ case ASM_DATA_EVENT_WATERMARK: {
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ pr_debug("%s: Watermark opcode[0x%x] status[0x%x]",
+ __func__, payload[0], payload[1]);
+ else
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ break;
+ }
+ case ASM_STREAM_CMD_GET_PP_PARAMS_V2:
+ case ASM_STREAM_CMD_GET_PP_PARAMS_V3:
+ pr_debug("%s: ASM_STREAM_CMD_GET_PP_PARAMS session %d opcode 0x%x token 0x%x src %d dest %d\n",
+ __func__, ac->session, data->opcode,
+ data->token, data->src_port, data->dest_port);
+ /* Should only come here if there is an APR */
+ /* error or malformed APR packet. Otherwise */
+ /* response will be returned as */
+ /* ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 */
+ if (data->payload_size >= 2 * sizeof(uint32_t)) {
+ if (payload[1] != 0) {
+ pr_err("%s: ASM get param error = %d, resuming\n",
+ __func__, payload[1]);
+ rtac_make_asm_callback(ac->session,
+ payload,
+ data->payload_size);
+ }
+ } else {
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ }
+ break;
+ case ASM_STREAM_CMD_REGISTER_PP_EVENTS:
+ pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS session %d opcode 0x%x token 0x%x src %d dest %d\n",
+ __func__, ac->session,
+ data->opcode, data->token,
+ data->src_port, data->dest_port);
+ if (data->payload_size >= 2 * sizeof(uint32_t)) {
+ if (payload[1] != 0)
+ pr_err("%s: ASM get param error = %d, resuming\n",
+ __func__, payload[1]);
+ atomic_set(&ac->cmd_state_pp, payload[1]);
+ wake_up(&ac->cmd_wait);
+ } else {
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ }
+ break;
+ default:
+ pr_debug("%s: command[0x%x] not expecting rsp\n",
+ __func__, payload[0]);
+ break;
+ }
+
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+ return 0;
+ }
+
+ switch (data->opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2:{
+ struct audio_port_data *port = &ac->port[IN];
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ dev_vdbg(ac->dev, "%s: Rxed opcode[0x%x] status[0x%x] token[%d]",
+ __func__, payload[0], payload[1],
+ data->token);
+ else
+ dev_err(ac->dev, "%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ if (ac->io_mode & SYNC_IO_MODE) {
+ if (port->buf == NULL) {
+ pr_err("%s: Unexpected Write Done\n",
+ __func__);
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock),
+ flags);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ buf_index = asm_token._token.buf_index;
+ if (buf_index < 0 ||
+ buf_index >= port->max_buf_cnt) {
+ pr_debug("%s: Invalid buffer index %u\n",
+ __func__, buf_index);
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock),
+ flags);
+ return -EINVAL;
+ }
+ if (data->payload_size >= 2 * sizeof(uint32_t) &&
+ (lower_32_bits(port->buf[buf_index].phys) !=
+ payload[0] ||
+ msm_audio_populate_upper_32_bits(
+ port->buf[buf_index].phys) !=
+ payload[1])) {
+ pr_debug("%s: Expected addr %pK\n",
+ __func__, &port->buf[buf_index].phys);
+ pr_err("%s: rxedl[0x%x] rxedu [0x%x]\n",
+ __func__, payload[0], payload[1]);
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock),
+ flags);
+ return -EINVAL;
+ }
+ port->buf[buf_index].used = 1;
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+
+
+ for (i = 0; i < port->max_buf_cnt; i++)
+ dev_vdbg(ac->dev, "%s %d\n",
+ __func__, port->buf[i].used);
+
+ }
+ config_debug_fs_write_cb();
+ break;
+ }
+ case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2:
+ case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V3:
+ pr_debug("%s: ASM_STREAM_CMDRSP_GET_PP_PARAMS session %d opcode 0x%x token 0x%x src %d dest %d\n",
+ __func__, ac->session, data->opcode, data->token,
+ data->src_port, data->dest_port);
+ if (payload[0] != 0) {
+ pr_err("%s: ASM_STREAM_CMDRSP_GET_PP_PARAMS returned error = 0x%x\n",
+ __func__, payload[0]);
+ } else if (generic_get_data) {
+ generic_get_data->valid = 1;
+ if (generic_get_data->is_inband) {
+ if (data->payload_size >= 4 * sizeof(uint32_t))
+ pr_debug("%s: payload[1] = 0x%x, payload[2]=0x%x, payload[3]=0x%x\n",
+ __func__,
+ payload[1],
+ payload[2],
+ payload[3]);
+ else
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__,
+ data->payload_size);
+
+ if (data->payload_size >=
+ (4 + (payload[3]>>2))
+ * sizeof(uint32_t)) {
+ generic_get_data->size_in_ints =
+ payload[3]>>2;
+ for (i = 0; i < payload[3]>>2; i++) {
+ generic_get_data->ints[i] =
+ payload[4+i];
+ pr_debug("%s: ASM callback val %i = %i\n",
+ __func__, i,
+ payload[4+i]);
+ }
+ } else {
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ }
+ pr_debug("%s: callback size in ints = %i\n",
+ __func__,
+ generic_get_data->size_in_ints);
+ }
+ if (atomic_read(&ac->cmd_state) && wakeup_flag) {
+ atomic_set(&ac->cmd_state, 0);
+ wake_up(&ac->cmd_wait);
+ }
+ break;
+ }
+ rtac_make_asm_callback(ac->session, payload,
+ data->payload_size);
+ break;
+ case ASM_DATA_EVENT_READ_DONE_V2:{
+
+ struct audio_port_data *port = &ac->port[OUT];
+
+ config_debug_fs_read_cb();
+
+ dev_vdbg(ac->dev, "%s: ReadDone: status=%d buff_add=0x%x act_size=%d offset=%d\n",
+ __func__, payload[READDONE_IDX_STATUS],
+ payload[READDONE_IDX_BUFADD_LSW],
+ payload[READDONE_IDX_SIZE],
+ payload[READDONE_IDX_OFFSET]);
+
+ dev_vdbg(ac->dev, "%s: ReadDone:msw_ts=%d lsw_ts=%d memmap_hdl=0x%x flags=%d id=%d num=%d\n",
+ __func__, payload[READDONE_IDX_MSW_TS],
+ payload[READDONE_IDX_LSW_TS],
+ payload[READDONE_IDX_MEMMAP_HDL],
+ payload[READDONE_IDX_FLAGS],
+ payload[READDONE_IDX_SEQ_ID],
+ payload[READDONE_IDX_NUMFRAMES]);
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ if (port->buf == NULL) {
+ pr_err("%s: Unexpected Write Done\n", __func__);
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock),
+ flags);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ buf_index = asm_token._token.buf_index;
+ if (buf_index < 0 || buf_index >= port->max_buf_cnt) {
+ pr_debug("%s: Invalid buffer index %u\n",
+ __func__, buf_index);
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock),
+ flags);
+ return -EINVAL;
+ }
+ port->buf[buf_index].used = 0;
+ if (lower_32_bits(port->buf[buf_index].phys) !=
+ payload[READDONE_IDX_BUFADD_LSW] ||
+ msm_audio_populate_upper_32_bits(
+ port->buf[buf_index].phys) !=
+ payload[READDONE_IDX_BUFADD_MSW]) {
+ dev_vdbg(ac->dev, "%s: Expected addr %pK\n",
+ __func__, &port->buf[buf_index].phys);
+ pr_err("%s: rxedl[0x%x] rxedu[0x%x]\n",
+ __func__,
+ payload[READDONE_IDX_BUFADD_LSW],
+ payload[READDONE_IDX_BUFADD_MSW]);
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ break;
+ }
+ port->buf[buf_index].actual_size =
+ payload[READDONE_IDX_SIZE];
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+ }
+ break;
+ }
+ case ASM_DATA_EVENT_EOS:
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ pr_debug("%s: EOS ACK received: rxed session %d opcode 0x%x token 0x%x src %d dest %d\n",
+ __func__, ac->session,
+ data->opcode, data->token,
+ data->src_port, data->dest_port);
+ break;
+ case ASM_SESSION_EVENTX_OVERFLOW:
+ pr_debug("%s: ASM_SESSION_EVENTX_OVERFLOW session %d opcode 0x%x token 0x%x src %d dest %d\n",
+ __func__, ac->session,
+ data->opcode, data->token,
+ data->src_port, data->dest_port);
+ break;
+ case ASM_SESSION_EVENT_RX_UNDERFLOW:
+ pr_debug("%s: ASM_SESSION_EVENT_RX_UNDERFLOW session %d opcode 0x%x token 0x%x src %d dest %d\n",
+ __func__, ac->session,
+ data->opcode, data->token,
+ data->src_port, data->dest_port);
+ break;
+ case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3:
+ if (data->payload_size >= 3 * sizeof(uint32_t)) {
+ dev_vdbg(ac->dev, "%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3, payload[0] = %d, payload[1] = %d, payload[2] = %d\n",
+ __func__,
+ payload[0], payload[1], payload[2]);
+ ac->time_stamp =
+ (uint64_t)(((uint64_t)payload[2] << 32) |
+ payload[1]);
+ } else {
+ dev_err(ac->dev, "%s: payload size of %x is less than expected.n",
+ __func__, data->payload_size);
+ }
+ if (atomic_cmpxchg(&ac->time_flag, 1, 0))
+ wake_up(&ac->time_wait);
+ break;
+ case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+ case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY:
+ pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY session %d opcode 0x%x token 0x%x src %d dest %d\n",
+ __func__, ac->session,
+ data->opcode, data->token,
+ data->src_port, data->dest_port);
+ if (data->payload_size >= 4 * sizeof(uint32_t))
+ pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0] = %d, payload[1] = %d, payload[2] = %d, payload[3] = %d\n",
+ __func__,
+ payload[0], payload[1], payload[2],
+ payload[3]);
+ else
+ pr_debug("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ break;
+ case ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2:
+ q6asm_process_mtmx_get_param_rsp(ac, (void *) payload);
+ break;
+ case ASM_STREAM_PP_EVENT:
+ case ASM_STREAM_CMD_ENCDEC_EVENTS:
+ case ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE:
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ pr_debug("%s: ASM_STREAM_EVENT payload[0][0x%x] payload[1][0x%x]",
+ __func__, payload[0], payload[1]);
+ else
+ pr_debug("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ i = is_adsp_raise_event(data->opcode);
+ if (i < 0) {
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+ return 0;
+ }
+
+ /* repack payload for asm_stream_pp_event
+ * package is composed of event type + size + actual payload
+ */
+ payload_size = data->payload_size;
+ if (payload_size > UINT_MAX -
+ sizeof(struct msm_adsp_event_data)) {
+ pr_err("%s: payload size = %d exceeds limit.\n",
+ __func__, payload_size);
+ spin_unlock(&(session[session_id].session_lock));
+ return -EINVAL;
+ }
+
+ pp_event_package = kzalloc(payload_size
+ + sizeof(struct msm_adsp_event_data),
+ GFP_ATOMIC);
+ if (!pp_event_package) {
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+ return -ENOMEM;
+ }
+
+ pp_event_package->event_type = i;
+ pp_event_package->payload_len = payload_size;
+ memcpy((void *)pp_event_package->payload,
+ data->payload, payload_size);
+ ac->cb(data->opcode, data->token,
+ (void *)pp_event_package, ac->priv);
+ kfree(pp_event_package);
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+ return 0;
+ case ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2:
+ if (data->payload_size >= 3 * sizeof(uint32_t))
+ pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n",
+ __func__, ac->session,
+ payload[0],
+ payload[2],
+ payload[1]);
+ else
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ wake_up(&ac->cmd_wait);
+ break;
+ case ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2:
+ if (data->payload_size >= 3 * sizeof(uint32_t))
+ pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n",
+ __func__, ac->session,
+ payload[0], payload[2],
+ payload[1]);
+ else
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ if (payload[0] == 0 &&
+ data->payload_size >=
+ 2 * sizeof(uint32_t)) {
+ atomic_set(&ac->cmd_state, 0);
+ /* ignore msw, as a delay that large shouldn't happen */
+ ac->path_delay = payload[1];
+ } else {
+ atomic_set(&ac->cmd_state, payload[0]);
+ ac->path_delay = UINT_MAX;
+ }
+ wake_up(&ac->cmd_wait);
+ break;
+ }
+ if (ac->cb)
+ ac->cb(data->opcode, data->token,
+ data->payload, ac->priv);
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock), flags);
+ return 0;
+}
+
+void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, uint32_t *size,
+ uint32_t *index)
+{
+ void *data;
+ unsigned char idx;
+ struct audio_port_data *port;
+
+ if (!ac || ((dir != IN) && (dir != OUT))) {
+ pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+ return NULL;
+ }
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[dir];
+
+ mutex_lock(&port->lock);
+ idx = port->cpu_buf;
+ if (port->buf == NULL) {
+ pr_err("%s: Buffer pointer null\n", __func__);
+ mutex_unlock(&port->lock);
+ return NULL;
+ }
+ /* dir 0: used = 0 means buf in use
+ dir 1: used = 1 means buf in use */
+ if (port->buf[idx].used == dir) {
+ /* To make it more robust, we could loop and get the
+ next avail buf, its risky though */
+ pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n",
+ __func__, idx, dir);
+ mutex_unlock(&port->lock);
+ return NULL;
+ }
+ *size = port->buf[idx].actual_size;
+ *index = port->cpu_buf;
+ data = port->buf[idx].data;
+ dev_vdbg(ac->dev, "%s: session[%d]index[%d] data[%pK]size[%d]\n",
+ __func__,
+ ac->session,
+ port->cpu_buf,
+ data, *size);
+ /* By default increase the cpu_buf cnt
+ user accesses this function,increase cpu
+ buf(to avoid another api)*/
+ port->buf[idx].used = dir;
+ port->cpu_buf = q6asm_get_next_buf(ac, port->cpu_buf,
+ port->max_buf_cnt);
+ mutex_unlock(&port->lock);
+ return data;
+ }
+ return NULL;
+}
+
+int q6asm_cpu_buf_release(int dir, struct audio_client *ac)
+{
+ struct audio_port_data *port;
+ int ret = 0;
+ int idx;
+
+ if (!ac || ((dir != IN) && (dir != OUT))) {
+ pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[dir];
+ mutex_lock(&port->lock);
+ idx = port->cpu_buf;
+ if (port->cpu_buf == 0) {
+ port->cpu_buf = port->max_buf_cnt - 1;
+ } else if (port->cpu_buf < port->max_buf_cnt) {
+ port->cpu_buf = port->cpu_buf - 1;
+ } else {
+ pr_err("%s: buffer index(%d) out of range\n",
+ __func__, port->cpu_buf);
+ ret = -EINVAL;
+ mutex_unlock(&port->lock);
+ goto exit;
+ }
+ port->buf[port->cpu_buf].used = dir ^ 1;
+ mutex_unlock(&port->lock);
+ }
+exit:
+ return ret;
+}
+
+void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac,
+ uint32_t *size, uint32_t *index)
+{
+ void *data;
+ unsigned char idx;
+ struct audio_port_data *port;
+
+ if (!ac || ((dir != IN) && (dir != OUT))) {
+ pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+ return NULL;
+ }
+
+ port = &ac->port[dir];
+
+ idx = port->cpu_buf;
+ if (port->buf == NULL) {
+ pr_err("%s: Buffer pointer null\n", __func__);
+ return NULL;
+ }
+ /*
+ * dir 0: used = 0 means buf in use
+ * dir 1: used = 1 means buf in use
+ */
+ if (port->buf[idx].used == dir) {
+ /*
+ * To make it more robust, we could loop and get the
+ * next avail buf, its risky though
+ */
+ pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n",
+ __func__, idx, dir);
+ return NULL;
+ }
+ *size = port->buf[idx].actual_size;
+ *index = port->cpu_buf;
+ data = port->buf[idx].data;
+ dev_vdbg(ac->dev, "%s: session[%d]index[%d] data[%pK]size[%d]\n",
+ __func__, ac->session, port->cpu_buf,
+ data, *size);
+ /*
+ * By default increase the cpu_buf cnt
+ * user accesses this function,increase cpu
+ * buf(to avoid another api)
+ */
+ port->buf[idx].used = dir;
+ port->cpu_buf = q6asm_get_next_buf(ac, port->cpu_buf,
+ port->max_buf_cnt);
+ return data;
+}
+
+int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac)
+{
+ int ret = -1;
+ struct audio_port_data *port;
+ uint32_t idx;
+
+ if (!ac || (dir != OUT)) {
+ pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+ return ret;
+ }
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[dir];
+
+ mutex_lock(&port->lock);
+ idx = port->dsp_buf;
+
+ if (port->buf[idx].used == (dir ^ 1)) {
+ /* To make it more robust, we could loop and get the
+ next avail buf, its risky though */
+ pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n",
+ __func__, idx, dir);
+ mutex_unlock(&port->lock);
+ return ret;
+ }
+ dev_vdbg(ac->dev, "%s: session[%d]dsp_buf=%d cpu_buf=%d\n",
+ __func__,
+ ac->session, port->dsp_buf, port->cpu_buf);
+ ret = ((port->dsp_buf != port->cpu_buf) ? 0 : -1);
+ mutex_unlock(&port->lock);
+ }
+ return ret;
+}
+
+static void __q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg, uint32_t stream_id)
+{
+ unsigned long flags;
+
+ dev_vdbg(ac->dev, "%s: pkt_size=%d cmd_flg=%d session=%d stream_id=%d\n",
+ __func__, pkt_size, cmd_flg, ac->session, stream_id);
+ mutex_lock(&ac->cmd_lock);
+ spin_lock_irqsave(&(session[ac->session].session_lock), flags);
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL", __func__);
+ spin_unlock_irqrestore(
+ &(session[ac->session].session_lock), flags);
+ mutex_unlock(&ac->cmd_lock);
+ return;
+ }
+
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(sizeof(struct apr_hdr)),\
+ APR_PKT_VER);
+ hdr->src_svc = ((struct apr_svc *)ac->apr)->id;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_ASM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+ hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+ if (cmd_flg)
+ q6asm_update_token(&hdr->token,
+ ac->session,
+ 0, /* Stream ID is NA */
+ 0, /* Buffer index is NA */
+ 0, /* Direction flag is NA */
+ WAIT_CMD);
+
+ hdr->pkt_size = pkt_size;
+ spin_unlock_irqrestore(
+ &(session[ac->session].session_lock), flags);
+ mutex_unlock(&ac->cmd_lock);
+ return;
+}
+
+static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg)
+{
+ __q6asm_add_hdr(ac, hdr, pkt_size, cmd_flg, ac->stream_id);
+ return;
+}
+
+static void q6asm_stream_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg, int32_t stream_id)
+{
+ __q6asm_add_hdr(ac, hdr, pkt_size, cmd_flg, stream_id);
+ return;
+}
+
+static void __q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg,
+ uint32_t stream_id, u8 no_wait_flag)
+{
+ dev_vdbg(ac->dev, "%s: pkt_size = %d, cmd_flg = %d, session = %d stream_id=%d\n",
+ __func__, pkt_size, cmd_flg, ac->session, stream_id);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(sizeof(struct apr_hdr)),\
+ APR_PKT_VER);
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR is NULL", __func__);
+ return;
+ }
+ hdr->src_svc = ((struct apr_svc *)ac->apr)->id;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_ASM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+ hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+ if (cmd_flg) {
+ q6asm_update_token(&hdr->token,
+ ac->session,
+ 0, /* Stream ID is NA */
+ 0, /* Buffer index is NA */
+ 0, /* Direction flag is NA */
+ no_wait_flag);
+
+ }
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg)
+{
+ __q6asm_add_hdr_async(ac, hdr, pkt_size, cmd_flg,
+ ac->stream_id, WAIT_CMD);
+ return;
+}
+
+static void q6asm_stream_add_hdr_async(struct audio_client *ac,
+ struct apr_hdr *hdr, uint32_t pkt_size,
+ uint32_t cmd_flg, int32_t stream_id)
+{
+ __q6asm_add_hdr_async(ac, hdr, pkt_size, cmd_flg,
+ stream_id, NO_WAIT_CMD);
+ return;
+}
+
+static void q6asm_add_hdr_custom_topology(struct audio_client *ac,
+ struct apr_hdr *hdr,
+ uint32_t pkt_size)
+{
+ pr_debug("%s: pkt_size=%d session=%d\n",
+ __func__, pkt_size, ac->session);
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return;
+ }
+
+ mutex_lock(&ac->cmd_lock);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(sizeof(struct apr_hdr)),
+ APR_PKT_VER);
+ hdr->src_svc = ((struct apr_svc *)ac->apr)->id;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_ASM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = ((ac->session << 8) & 0xFF00) | 0x01;
+ hdr->dest_port = 0;
+ q6asm_update_token(&hdr->token,
+ ac->session,
+ 0, /* Stream ID is NA */
+ 0, /* Buffer index is NA */
+ 0, /* Direction flag is NA */
+ WAIT_CMD);
+ hdr->pkt_size = pkt_size;
+ mutex_unlock(&ac->cmd_lock);
+ return;
+}
+
+static void q6asm_add_mmaphdr(struct audio_client *ac, struct apr_hdr *hdr,
+ u32 pkt_size, int dir)
+{
+ pr_debug("%s: pkt size=%d\n",
+ __func__, pkt_size);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ hdr->src_port = 0;
+ hdr->dest_port = 0;
+ q6asm_update_token(&hdr->token,
+ ac->session,
+ 0, /* Stream ID is NA */
+ 0, /* Buffer index is NA */
+ dir,
+ WAIT_CMD);
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+int q6asm_set_pp_params(struct audio_client *ac,
+ struct mem_mapping_hdr *mem_hdr, u8 *param_data,
+ u32 param_size)
+{
+ struct asm_stream_cmd_set_pp_params *asm_set_param = NULL;
+ int pkt_size = 0;
+ int ret = 0;
+
+ if (ac == NULL) {
+ pr_err("%s: Audio Client is NULL\n", __func__);
+ return -EINVAL;
+ } else if (ac->apr == NULL) {
+ pr_err("%s: APR pointer is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ pkt_size = sizeof(struct asm_stream_cmd_set_pp_params);
+ /* Add param size to packet size when sending in-band only */
+ if (param_data != NULL)
+ pkt_size += param_size;
+ asm_set_param = kzalloc(pkt_size, GFP_KERNEL);
+ if (!asm_set_param)
+ return -ENOMEM;
+
+ q6asm_add_hdr_async(ac, &asm_set_param->apr_hdr, pkt_size, TRUE);
+
+ /* With pre-packed data, only the opcode differes from V2 and V3. */
+ if (q6common_is_instance_id_supported())
+ asm_set_param->apr_hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V3;
+ else
+ asm_set_param->apr_hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+
+ asm_set_param->payload_size = param_size;
+
+ if (mem_hdr != NULL) {
+ /* Out of band case */
+ asm_set_param->mem_hdr = *mem_hdr;
+ } else if (param_data != NULL) {
+ /* In band case. Parameter data must be pre-packed with its
+ * header before calling this function. Use
+ * q6common_pack_pp_params to pack parameter data and header
+ * correctly.
+ */
+ memcpy(&asm_set_param->param_data, param_data, param_size);
+ } else {
+ pr_err("%s: Received NULL pointers for both mem header and param data\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ atomic_set(&ac->cmd_state_pp, -1);
+ ret = apr_send_pkt(ac->apr, (uint32_t *) asm_set_param);
+ if (ret < 0) {
+ pr_err("%s: apr send failed rc %d\n", __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(ac->cmd_wait,
+ atomic_read(&ac->cmd_state_pp) >= 0, 5 * HZ);
+ if (!ret) {
+ pr_err("%s: timeout sending apr pkt\n", __func__);
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
+ pr_err("%s: DSP returned error[%s]\n", __func__,
+ adsp_err_get_err_str(atomic_read(&ac->cmd_state_pp)));
+ ret = adsp_err_get_lnx_err_code(atomic_read(&ac->cmd_state_pp));
+ goto done;
+ }
+ ret = 0;
+done:
+ kfree(asm_set_param);
+ return ret;
+}
+EXPORT_SYMBOL(q6asm_set_pp_params);
+
+int q6asm_pack_and_set_pp_param_in_band(struct audio_client *ac,
+ struct param_hdr_v3 param_hdr,
+ u8 *param_data)
+{
+ u8 *packed_data = NULL;
+ u32 packed_size = sizeof(union param_hdrs) + param_hdr.param_size;
+ int ret = 0;
+
+ packed_data = kzalloc(packed_size, GFP_KERNEL);
+ if (packed_data == NULL)
+ return -ENOMEM;
+
+ ret = q6common_pack_pp_params(packed_data, &param_hdr, param_data,
+ &packed_size);
+ if (ret) {
+ pr_err("%s: Failed to pack params, error %d\n", __func__, ret);
+ goto done;
+ }
+
+ ret = q6asm_set_pp_params(ac, NULL, packed_data, packed_size);
+done:
+ kfree(packed_data);
+ return ret;
+}
+EXPORT_SYMBOL(q6asm_pack_and_set_pp_param_in_band);
+
+int q6asm_set_soft_volume_module_instance_ids(int instance,
+ struct param_hdr_v3 *param_hdr)
+{
+ if (param_hdr == NULL) {
+ pr_err("%s: Param header is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (instance) {
+ case SOFT_VOLUME_INSTANCE_2:
+ param_hdr->module_id = ASM_MODULE_ID_VOL_CTRL2;
+ param_hdr->instance_id = INSTANCE_ID_0;
+ return 0;
+ case SOFT_VOLUME_INSTANCE_1:
+ param_hdr->module_id = ASM_MODULE_ID_VOL_CTRL;
+ param_hdr->instance_id = INSTANCE_ID_0;
+ return 0;
+ default:
+ pr_err("%s: Invalid instance %d\n", __func__, instance);
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(q6asm_set_soft_volume_module_instance_ids);
+
+static int __q6asm_open_read(struct audio_client *ac,
+ uint32_t format, uint16_t bits_per_sample,
+ uint32_t pcm_format_block_ver,
+ bool ts_mode)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_read_v3 open;
+
+ config_debug_fs_reset_index();
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3;
+ /* Stream prio : High, provide meta info with encoded frames */
+ open.src_endpointype = ASM_END_POINT_DEVICE_MATRIX;
+
+ open.preprocopo_id = q6asm_get_asm_topology_cal();
+ open.bits_per_sample = bits_per_sample;
+ open.mode_flags = 0x0;
+
+ ac->topology = open.preprocopo_id;
+ ac->app_type = q6asm_get_asm_app_type_cal();
+ if (ac->perf_mode == LOW_LATENCY_PCM_MODE) {
+ open.mode_flags |= ASM_LOW_LATENCY_TX_STREAM_SESSION <<
+ ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ;
+ } else {
+ open.mode_flags |= ASM_LEGACY_STREAM_SESSION <<
+ ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ;
+ }
+
+ switch (format) {
+ case FORMAT_LINEAR_PCM:
+ open.mode_flags |= 0x00;
+ open.enc_cfg_id = q6asm_get_pcm_format_id(pcm_format_block_ver);
+ if (ts_mode)
+ open.mode_flags |= ABSOLUTE_TIMESTAMP_ENABLE;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.mode_flags |= BUFFER_META_ENABLE;
+ open.enc_cfg_id = ASM_MEDIA_FMT_AAC_V2;
+ break;
+ case FORMAT_G711_ALAW_FS:
+ open.mode_flags |= BUFFER_META_ENABLE;
+ open.enc_cfg_id = ASM_MEDIA_FMT_G711_ALAW_FS;
+ break;
+ case FORMAT_G711_MLAW_FS:
+ open.mode_flags |= BUFFER_META_ENABLE;
+ open.enc_cfg_id = ASM_MEDIA_FMT_G711_MLAW_FS;
+ break;
+ case FORMAT_V13K:
+ open.mode_flags |= BUFFER_META_ENABLE;
+ open.enc_cfg_id = ASM_MEDIA_FMT_V13K_FS;
+ break;
+ case FORMAT_EVRC:
+ open.mode_flags |= BUFFER_META_ENABLE;
+ open.enc_cfg_id = ASM_MEDIA_FMT_EVRC_FS;
+ break;
+ case FORMAT_AMRNB:
+ open.mode_flags |= BUFFER_META_ENABLE ;
+ open.enc_cfg_id = ASM_MEDIA_FMT_AMRNB_FS;
+ break;
+ case FORMAT_AMRWB:
+ open.mode_flags |= BUFFER_META_ENABLE ;
+ open.enc_cfg_id = ASM_MEDIA_FMT_AMRWB_FS;
+ break;
+ default:
+ pr_err("%s: Invalid format 0x%x\n",
+ __func__, format);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s: open failed op[0x%x]rc[%d]\n",
+ __func__, open.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for open read\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+
+ ac->io_mode |= TUN_READ_IO_MODE;
+
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_open_read(struct audio_client *ac,
+ uint32_t format)
+{
+ return __q6asm_open_read(ac, format, 16,
+ PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/,
+ false/*ts_mode*/);
+}
+
+int q6asm_open_read_v2(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_open_read(ac, format, bits_per_sample,
+ PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/,
+ false/*ts_mode*/);
+}
+
+/*
+ * asm_open_read_v3 - Opens audio capture session
+ *
+ * @ac: Client session handle
+ * @format: encoder format
+ * @bits_per_sample: bit width of capture session
+ */
+int q6asm_open_read_v3(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_open_read(ac, format, bits_per_sample,
+ PCM_MEDIA_FORMAT_V3/*media fmt block ver*/,
+ false/*ts_mode*/);
+}
+EXPORT_SYMBOL(q6asm_open_read_v3);
+
+/*
+ * asm_open_read_v4 - Opens audio capture session
+ *
+ * @ac: Client session handle
+ * @format: encoder format
+ * @bits_per_sample: bit width of capture session
+ * @ts_mode: timestamp mode
+ */
+int q6asm_open_read_v4(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample, bool ts_mode)
+{
+ return __q6asm_open_read(ac, format, bits_per_sample,
+ PCM_MEDIA_FORMAT_V4 /*media fmt block ver*/,
+ ts_mode);
+}
+EXPORT_SYMBOL(q6asm_open_read_v4);
+
+/*
+ * asm_open_read_v5 - Opens audio capture session
+ *
+ * @ac: Client session handle
+ * @format: encoder format
+ * @bits_per_sample: bit width of capture session
+ * @ts_mode: timestamp mode
+ */
+int q6asm_open_read_v5(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample, bool ts_mode)
+{
+ return __q6asm_open_read(ac, format, bits_per_sample,
+ PCM_MEDIA_FORMAT_V5 /*media fmt block ver*/,
+ ts_mode);
+}
+EXPORT_SYMBOL(q6asm_open_read_v5);
+
+static int q6asm_open_read_version_adaptor(struct audio_client *ac,
+ uint32_t format, uint16_t bits_per_sample, bool ts_mode)
+{
+ if (q6asm_get_svc_version(APR_SVC_ASM) >= ADSP_ASM_API_VERSION_V2)
+ return q6asm_open_read_v5(ac, FORMAT_LINEAR_PCM,
+ bits_per_sample, false);
+ else
+ return q6asm_open_read_v4(ac, FORMAT_LINEAR_PCM,
+ bits_per_sample, false);
+}
+
+/*
+ * q6asm_open_read_with_retry - Opens audio capture session, with retrying
+ * in case of session ID conflict
+ *
+ * @ac: Client session handle
+ * @format: encoder format
+ * @bits_per_sample: bit width of capture session
+ * @ts_mode: timestamp mode
+ */
+int q6asm_open_read_with_retry(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample, bool ts_mode)
+{
+ int i, rc;
+
+ mutex_lock(&session_lock);
+ for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED; i++) {
+ rc = q6asm_open_read_version_adaptor(ac, format,
+ bits_per_sample, ts_mode);
+ if (rc != -EALREADY)
+ break;
+
+ pr_debug("%s: session %d is occupied, try next\n",
+ __func__, ac->session);
+ q6asm_session_set_ignore(ac->session);
+ rc = q6asm_session_try_next(ac);
+ if (rc < 0)
+ break;
+ }
+ q6asm_session_clean_ignore();
+ mutex_unlock(&session_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_open_read_with_retry);
+
+int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format,
+ uint32_t passthrough_flag)
+{
+ int rc = 0;
+ struct asm_stream_cmd_open_write_compressed open;
+
+ if (ac == NULL) {
+ pr_err("%s: ac[%pK] NULL\n", __func__, ac);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (ac->apr == NULL) {
+ pr_err("%s: APR handle[%pK] NULL\n", __func__, ac->apr);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ pr_debug("%s: session[%d] wr_format[0x%x]", __func__, ac->session,
+ format);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED;
+ atomic_set(&ac->cmd_state, -1);
+
+ switch (format) {
+ case FORMAT_AC3:
+ open.fmt_id = ASM_MEDIA_FMT_AC3;
+ break;
+ case FORMAT_EAC3:
+ open.fmt_id = ASM_MEDIA_FMT_EAC3;
+ break;
+ case FORMAT_DTS:
+ open.fmt_id = ASM_MEDIA_FMT_DTS;
+ break;
+ case FORMAT_DSD:
+ open.fmt_id = ASM_MEDIA_FMT_DSD;
+ break;
+ case FORMAT_GEN_COMPR:
+ open.fmt_id = ASM_MEDIA_FMT_GENERIC_COMPRESSED;
+ break;
+ case FORMAT_TRUEHD:
+ open.fmt_id = ASM_MEDIA_FMT_TRUEHD;
+ break;
+ case FORMAT_IEC61937:
+ open.fmt_id = ASM_MEDIA_FMT_IEC;
+ break;
+ default:
+ pr_err("%s: Invalid format[%d]\n", __func__, format);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ /*Below flag indicates the DSP that Compressed audio input
+ stream is not IEC 61937 or IEC 60958 packetizied*/
+ if (passthrough_flag == COMPRESSED_PASSTHROUGH ||
+ passthrough_flag == COMPRESSED_PASSTHROUGH_DSD ||
+ passthrough_flag == COMPRESSED_PASSTHROUGH_GEN) {
+ open.flags = 0x0;
+ pr_debug("%s: Flag 0 COMPRESSED_PASSTHROUGH\n", __func__);
+ } else if (passthrough_flag == COMPRESSED_PASSTHROUGH_CONVERT) {
+ open.flags = 0x8;
+ pr_debug("%s: Flag 8 - COMPRESSED_PASSTHROUGH_CONVERT\n",
+ __func__);
+ } else if (passthrough_flag == COMPRESSED_PASSTHROUGH_IEC61937) {
+ open.flags = 0x1;
+ pr_debug("%s: Flag 1 - COMPRESSED_PASSTHROUGH_IEC61937\n",
+ __func__);
+ } else {
+ pr_err("%s: Invalid passthrough type[%d]\n",
+ __func__, passthrough_flag);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s: open failed op[0x%x]rc[%d]\n",
+ __func__, open.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 1*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for OPEN_WRITE_COMPR rc[%d]\n",
+ __func__, rc);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+
+ return 0;
+
+fail_cmd:
+ return rc;
+}
+
+static int __q6asm_open_write(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample, uint32_t stream_id,
+ bool is_gapless_mode,
+ uint32_t pcm_format_block_ver)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_write_v3 open;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_vdbg(ac->dev, "%s: session[%d] wr_format[0x%x]\n",
+ __func__, ac->session, format);
+
+ q6asm_stream_add_hdr(ac, &open.hdr, sizeof(open), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ q6asm_update_token(&open.hdr.token,
+ ac->session,
+ stream_id,
+ 0, /* Buffer index is NA */
+ 0, /* Direction flag is NA */
+ WAIT_CMD);
+
+ dev_vdbg(ac->dev, "%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, open.hdr.token, stream_id, ac->session);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3;
+ open.mode_flags = 0x00;
+ if (ac->perf_mode == ULL_POST_PROCESSING_PCM_MODE)
+ open.mode_flags |= ASM_ULL_POST_PROCESSING_STREAM_SESSION;
+ else if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE)
+ open.mode_flags |= ASM_ULTRA_LOW_LATENCY_STREAM_SESSION;
+ else if (ac->perf_mode == LOW_LATENCY_PCM_MODE)
+ open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION;
+ else {
+ open.mode_flags |= ASM_LEGACY_STREAM_SESSION;
+ if (is_gapless_mode)
+ open.mode_flags |= 1 << ASM_SHIFT_GAPLESS_MODE_FLAG;
+ }
+
+ /* source endpoint : matrix */
+ open.sink_endpointype = ASM_END_POINT_DEVICE_MATRIX;
+ open.bits_per_sample = bits_per_sample;
+
+ open.postprocopo_id = q6asm_get_asm_topology_cal();
+ if (ac->perf_mode != LEGACY_PCM_MODE)
+ open.postprocopo_id = ASM_STREAM_POSTPROCOPO_ID_NONE;
+
+ pr_debug("%s: perf_mode %d asm_topology 0x%x bps %d\n", __func__,
+ ac->perf_mode, open.postprocopo_id, open.bits_per_sample);
+
+ /*
+ * For Gapless playback it will use the same session for next stream,
+ * So use the same topology
+ */
+ if (!ac->topology) {
+ ac->topology = open.postprocopo_id;
+ ac->app_type = q6asm_get_asm_app_type_cal();
+ }
+ switch (format) {
+ case FORMAT_LINEAR_PCM:
+ open.dec_fmt_id = q6asm_get_pcm_format_id(pcm_format_block_ver);
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2;
+ break;
+ case FORMAT_MPEG4_MULTI_AAC:
+ open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2;
+ break;
+ case FORMAT_WMA_V9:
+ open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V9_V2;
+ break;
+ case FORMAT_WMA_V10PRO:
+ open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V10PRO_V2;
+ break;
+ case FORMAT_MP3:
+ open.dec_fmt_id = ASM_MEDIA_FMT_MP3;
+ break;
+ case FORMAT_AC3:
+ open.dec_fmt_id = ASM_MEDIA_FMT_AC3;
+ break;
+ case FORMAT_EAC3:
+ open.dec_fmt_id = ASM_MEDIA_FMT_EAC3;
+ break;
+ case FORMAT_MP2:
+ open.dec_fmt_id = ASM_MEDIA_FMT_MP2;
+ break;
+ case FORMAT_FLAC:
+ open.dec_fmt_id = ASM_MEDIA_FMT_FLAC;
+ break;
+ case FORMAT_ALAC:
+ open.dec_fmt_id = ASM_MEDIA_FMT_ALAC;
+ break;
+ case FORMAT_VORBIS:
+ open.dec_fmt_id = ASM_MEDIA_FMT_VORBIS;
+ break;
+ case FORMAT_APE:
+ open.dec_fmt_id = ASM_MEDIA_FMT_APE;
+ break;
+ case FORMAT_DSD:
+ open.dec_fmt_id = ASM_MEDIA_FMT_DSD;
+ break;
+ case FORMAT_APTX:
+ open.dec_fmt_id = ASM_MEDIA_FMT_APTX;
+ break;
+ case FORMAT_APTXHD:
+ open.dec_fmt_id = ASM_MEDIA_FMT_APTX_HD;
+ break;
+ case FORMAT_GEN_COMPR:
+ open.dec_fmt_id = ASM_MEDIA_FMT_GENERIC_COMPRESSED;
+ break;
+ default:
+ pr_err("%s: Invalid format 0x%x\n", __func__, format);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s: open failed op[0x%x]rc[%d]\n", \
+ __func__, open.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for open write\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ ac->io_mode |= TUN_WRITE_IO_MODE;
+
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_open_write(struct audio_client *ac, uint32_t format)
+{
+ return __q6asm_open_write(ac, format, 16, ac->stream_id,
+ false /*gapless*/,
+ PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/);
+}
+
+int q6asm_open_write_v2(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_open_write(ac, format, bits_per_sample,
+ ac->stream_id, false /*gapless*/,
+ PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/);
+}
+
+/*
+ * q6asm_open_write_v3 - Opens audio playback session
+ *
+ * @ac: Client session handle
+ * @format: decoder format
+ * @bits_per_sample: bit width of playback session
+ */
+int q6asm_open_write_v3(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_open_write(ac, format, bits_per_sample,
+ ac->stream_id, false /*gapless*/,
+ PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/);
+}
+EXPORT_SYMBOL(q6asm_open_write_v3);
+
+/*
+ * q6asm_open_write_v4 - Opens audio playback session
+ *
+ * @ac: Client session handle
+ * @format: decoder format
+ * @bits_per_sample: bit width of playback session
+ */
+int q6asm_open_write_v4(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_open_write(ac, format, bits_per_sample,
+ ac->stream_id, false /*gapless*/,
+ PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/);
+}
+EXPORT_SYMBOL(q6asm_open_write_v4);
+
+/*
+ * q6asm_open_write_v5 - Opens audio playback session
+ *
+ * @ac: Client session handle
+ * @format: decoder format
+ * @bits_per_sample: bit width of playback session
+ */
+int q6asm_open_write_v5(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_open_write(ac, format, bits_per_sample,
+ ac->stream_id, false /*gapless*/,
+ PCM_MEDIA_FORMAT_V5 /*pcm_format_block_ver*/);
+}
+EXPORT_SYMBOL(q6asm_open_write_v5);
+
+static int q6asm_open_write_version_adaptor(struct audio_client *ac,
+ uint32_t format, uint16_t bits_per_sample)
+{
+ if (q6asm_get_svc_version(APR_SVC_ASM) >= ADSP_ASM_API_VERSION_V2)
+ return q6asm_open_write_v5(ac, format, bits_per_sample);
+ else
+ return q6asm_open_write_v4(ac, format, bits_per_sample);
+}
+
+/*
+ * q6asm_open_write_with_retry - Opens audio playback session, with retrying
+ * in case of session ID conflict
+ *
+ * @ac: Client session handle
+ * @format: decoder format
+ * @bits_per_sample: bit width of playback session
+ */
+int q6asm_open_write_with_retry(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ int i, rc;
+
+ mutex_lock(&session_lock);
+ for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED; i++) {
+ rc = q6asm_open_write_version_adaptor(ac, format,
+ bits_per_sample);
+ if (rc != -EALREADY)
+ break;
+
+ pr_debug("%s: session %d is occupied, try next\n",
+ __func__, ac->session);
+ q6asm_session_set_ignore(ac->session);
+ rc = q6asm_session_try_next(ac);
+ if (rc < 0)
+ break;
+ }
+ q6asm_session_clean_ignore();
+ mutex_unlock(&session_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_open_write_with_retry);
+
+int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample, int32_t stream_id,
+ bool is_gapless_mode)
+{
+ return __q6asm_open_write(ac, format, bits_per_sample,
+ stream_id, is_gapless_mode,
+ PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/);
+}
+
+/*
+ * q6asm_stream_open_write_v3 - Creates audio stream for playback
+ *
+ * @ac: Client session handle
+ * @format: asm playback format
+ * @bits_per_sample: bit width of requested stream
+ * @stream_id: stream id of stream to be associated with this session
+ * @is_gapless_mode: true if gapless mode needs to be enabled
+ */
+int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample, int32_t stream_id,
+ bool is_gapless_mode)
+{
+ return __q6asm_open_write(ac, format, bits_per_sample,
+ stream_id, is_gapless_mode,
+ PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/);
+}
+EXPORT_SYMBOL(q6asm_stream_open_write_v3);
+
+/*
+ * q6asm_stream_open_write_v4 - Creates audio stream for playback
+ *
+ * @ac: Client session handle
+ * @format: asm playback format
+ * @bits_per_sample: bit width of requested stream
+ * @stream_id: stream id of stream to be associated with this session
+ * @is_gapless_mode: true if gapless mode needs to be enabled
+ */
+int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample, int32_t stream_id,
+ bool is_gapless_mode)
+{
+ return __q6asm_open_write(ac, format, bits_per_sample,
+ stream_id, is_gapless_mode,
+ PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/);
+}
+EXPORT_SYMBOL(q6asm_stream_open_write_v4);
+
+static int __q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format,
+ uint32_t wr_format, bool is_meta_data_mode,
+ uint32_t bits_per_sample,
+ bool overwrite_topology, int topology)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_readwrite_v2 open;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]\n", __func__, ac->session);
+ pr_debug("%s: wr_format[0x%x]rd_format[0x%x]\n",
+ __func__, wr_format, rd_format);
+
+ ac->io_mode |= NT_MODE;
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_READWRITE_V2;
+
+ open.mode_flags = is_meta_data_mode ? BUFFER_META_ENABLE : 0;
+ open.bits_per_sample = bits_per_sample;
+ /* source endpoint : matrix */
+ open.postprocopo_id = q6asm_get_asm_topology_cal();
+
+ open.postprocopo_id = overwrite_topology ?
+ topology : open.postprocopo_id;
+ ac->topology = open.postprocopo_id;
+ ac->app_type = q6asm_get_asm_app_type_cal();
+
+
+ switch (wr_format) {
+ case FORMAT_LINEAR_PCM:
+ case FORMAT_MULTI_CHANNEL_LINEAR_PCM:
+ open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2;
+ break;
+ case FORMAT_MPEG4_MULTI_AAC:
+ open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2;
+ break;
+ case FORMAT_WMA_V9:
+ open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V9_V2;
+ break;
+ case FORMAT_WMA_V10PRO:
+ open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V10PRO_V2;
+ break;
+ case FORMAT_AMRNB:
+ open.dec_fmt_id = ASM_MEDIA_FMT_AMRNB_FS;
+ break;
+ case FORMAT_AMRWB:
+ open.dec_fmt_id = ASM_MEDIA_FMT_AMRWB_FS;
+ break;
+ case FORMAT_AMR_WB_PLUS:
+ open.dec_fmt_id = ASM_MEDIA_FMT_AMR_WB_PLUS_V2;
+ break;
+ case FORMAT_V13K:
+ open.dec_fmt_id = ASM_MEDIA_FMT_V13K_FS;
+ break;
+ case FORMAT_EVRC:
+ open.dec_fmt_id = ASM_MEDIA_FMT_EVRC_FS;
+ break;
+ case FORMAT_EVRCB:
+ open.dec_fmt_id = ASM_MEDIA_FMT_EVRCB_FS;
+ break;
+ case FORMAT_EVRCWB:
+ open.dec_fmt_id = ASM_MEDIA_FMT_EVRCWB_FS;
+ break;
+ case FORMAT_MP3:
+ open.dec_fmt_id = ASM_MEDIA_FMT_MP3;
+ break;
+ case FORMAT_ALAC:
+ open.dec_fmt_id = ASM_MEDIA_FMT_ALAC;
+ break;
+ case FORMAT_APE:
+ open.dec_fmt_id = ASM_MEDIA_FMT_APE;
+ break;
+ case FORMAT_DSD:
+ open.dec_fmt_id = ASM_MEDIA_FMT_DSD;
+ break;
+ case FORMAT_G711_ALAW_FS:
+ open.dec_fmt_id = ASM_MEDIA_FMT_G711_ALAW_FS;
+ break;
+ case FORMAT_G711_MLAW_FS:
+ open.dec_fmt_id = ASM_MEDIA_FMT_G711_MLAW_FS;
+ break;
+ default:
+ pr_err("%s: Invalid format 0x%x\n",
+ __func__, wr_format);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ switch (rd_format) {
+ case FORMAT_LINEAR_PCM:
+ case FORMAT_MULTI_CHANNEL_LINEAR_PCM:
+ open.enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.enc_cfg_id = ASM_MEDIA_FMT_AAC_V2;
+ break;
+ case FORMAT_G711_ALAW_FS:
+ open.enc_cfg_id = ASM_MEDIA_FMT_G711_ALAW_FS;
+ break;
+ case FORMAT_G711_MLAW_FS:
+ open.enc_cfg_id = ASM_MEDIA_FMT_G711_MLAW_FS;
+ break;
+ case FORMAT_V13K:
+ open.enc_cfg_id = ASM_MEDIA_FMT_V13K_FS;
+ break;
+ case FORMAT_EVRC:
+ open.enc_cfg_id = ASM_MEDIA_FMT_EVRC_FS;
+ break;
+ case FORMAT_AMRNB:
+ open.enc_cfg_id = ASM_MEDIA_FMT_AMRNB_FS;
+ break;
+ case FORMAT_AMRWB:
+ open.enc_cfg_id = ASM_MEDIA_FMT_AMRWB_FS;
+ break;
+ case FORMAT_ALAC:
+ open.enc_cfg_id = ASM_MEDIA_FMT_ALAC;
+ break;
+ case FORMAT_APE:
+ open.enc_cfg_id = ASM_MEDIA_FMT_APE;
+ break;
+ default:
+ pr_err("%s: Invalid format 0x%x\n",
+ __func__, rd_format);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ dev_vdbg(ac->dev, "%s: rdformat[0x%x]wrformat[0x%x]\n", __func__,
+ open.enc_cfg_id, open.dec_fmt_id);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s: open failed op[0x%x]rc[%d]\n",
+ __func__, open.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for open read-write\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format,
+ uint32_t wr_format)
+{
+ return __q6asm_open_read_write(ac, rd_format, wr_format,
+ true/*meta data mode*/,
+ 16 /*bits_per_sample*/,
+ false /*overwrite_topology*/, 0);
+}
+
+int q6asm_open_read_write_v2(struct audio_client *ac, uint32_t rd_format,
+ uint32_t wr_format, bool is_meta_data_mode,
+ uint32_t bits_per_sample, bool overwrite_topology,
+ int topology)
+{
+ return __q6asm_open_read_write(ac, rd_format, wr_format,
+ is_meta_data_mode, bits_per_sample,
+ overwrite_topology, topology);
+}
+
+int q6asm_open_loopback_v2(struct audio_client *ac, uint16_t bits_per_sample)
+{
+ int rc = 0x00;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+ if (ac->perf_mode == LOW_LATENCY_PCM_MODE) {
+ struct asm_stream_cmd_open_transcode_loopback_t open;
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK;
+
+ open.mode_flags = 0;
+ open.src_endpoint_type = 0;
+ open.sink_endpoint_type = 0;
+ open.src_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+ open.sink_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+ /* source endpoint : matrix */
+ open.audproc_topo_id = q6asm_get_asm_topology_cal();
+
+ ac->app_type = q6asm_get_asm_app_type_cal();
+ if (ac->perf_mode == LOW_LATENCY_PCM_MODE)
+ open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION;
+ else
+ open.mode_flags |= ASM_LEGACY_STREAM_SESSION;
+ ac->topology = open.audproc_topo_id;
+ open.bits_per_sample = bits_per_sample;
+ open.reserved = 0;
+ pr_debug("%s: opening a transcode_loopback with mode_flags =[%d] session[%d]\n",
+ __func__, open.mode_flags, ac->session);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s: open failed op[0x%x]rc[%d]\n",
+ __func__, open.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {/*if(ac->perf_mode == LEGACY_PCM_MODE)*/
+ struct asm_stream_cmd_open_loopback_v2 open;
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_LOOPBACK_V2;
+
+ open.mode_flags = 0;
+ open.src_endpointype = 0;
+ open.sink_endpointype = 0;
+ /* source endpoint : matrix */
+ open.postprocopo_id = q6asm_get_asm_topology_cal();
+
+ ac->app_type = q6asm_get_asm_app_type_cal();
+ ac->topology = open.postprocopo_id;
+ open.bits_per_sample = bits_per_sample;
+ open.reserved = 0;
+ pr_debug("%s: opening a loopback_v2 with mode_flags =[%d] session[%d]\n",
+ __func__, open.mode_flags, ac->session);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s: open failed op[0x%x]rc[%d]\n",
+ __func__, open.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for open_loopback\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+
+ return 0;
+fail_cmd:
+ return rc;
+}
+int q6asm_open_loopback_with_retry(struct audio_client *ac,
+ uint16_t bits_per_sample)
+{
+ int i, rc;
+
+ mutex_lock(&session_lock);
+ for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED; i++) {
+ rc = q6asm_open_loopback_v2(ac, bits_per_sample);
+ if (rc != -EALREADY)
+ break;
+ pr_debug("%s: session %d is occupied, try next\n",
+ __func__, ac->session);
+ q6asm_session_set_ignore(ac->session);
+ rc = q6asm_session_try_next(ac);
+ if (rc < 0)
+ break;
+ }
+ q6asm_session_clean_ignore();
+ mutex_unlock(&session_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_open_loopback_with_retry);
+
+int q6asm_open_transcode_loopback(struct audio_client *ac,
+ uint16_t bits_per_sample,
+ uint32_t source_format, uint32_t sink_format)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_transcode_loopback_t open;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK;
+
+ open.mode_flags = 0;
+ open.src_endpoint_type = 0;
+ open.sink_endpoint_type = 0;
+ switch (source_format) {
+ case FORMAT_LINEAR_PCM:
+ case FORMAT_MULTI_CHANNEL_LINEAR_PCM:
+ open.src_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3;
+ break;
+ case FORMAT_AC3:
+ open.src_format_id = ASM_MEDIA_FMT_AC3;
+ break;
+ case FORMAT_EAC3:
+ open.src_format_id = ASM_MEDIA_FMT_EAC3;
+ break;
+ default:
+ pr_err("%s: Unsupported src fmt [%d]\n",
+ __func__, source_format);
+ return -EINVAL;
+ }
+ switch (sink_format) {
+ case FORMAT_LINEAR_PCM:
+ case FORMAT_MULTI_CHANNEL_LINEAR_PCM:
+ open.sink_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3;
+ break;
+ default:
+ pr_err("%s: Unsupported sink fmt [%d]\n",
+ __func__, sink_format);
+ return -EINVAL;
+ }
+
+ /* source endpoint : matrix */
+ open.audproc_topo_id = q6asm_get_asm_topology_cal();
+
+ ac->app_type = q6asm_get_asm_app_type_cal();
+ if (ac->perf_mode == LOW_LATENCY_PCM_MODE)
+ open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION;
+ else
+ open.mode_flags |= ASM_LEGACY_STREAM_SESSION;
+ ac->topology = open.audproc_topo_id;
+ open.bits_per_sample = bits_per_sample;
+ open.reserved = 0;
+ pr_debug("%s: opening a transcode_loopback with mode_flags =[%d] session[%d]\n",
+ __func__, open.mode_flags, ac->session);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s: open failed op[0x%x]rc[%d]\n",
+ __func__, open.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for open_transcode_loopback\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+static
+int q6asm_set_shared_circ_buff(struct audio_client *ac,
+ struct asm_stream_cmd_open_shared_io *open,
+ int bufsz, int bufcnt,
+ int dir)
+{
+ struct audio_buffer *buf_circ;
+ int bytes_to_alloc, rc;
+ size_t len;
+
+ mutex_lock(&ac->cmd_lock);
+
+ if (ac->port[dir].buf) {
+ pr_err("%s: Buffer already allocated\n", __func__);
+ rc = -EINVAL;
+ mutex_unlock(&ac->cmd_lock);
+ goto done;
+ }
+
+ buf_circ = kzalloc(sizeof(struct audio_buffer), GFP_KERNEL);
+
+ if (!buf_circ) {
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ bytes_to_alloc = bufsz * bufcnt;
+ bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc);
+
+ rc = msm_audio_ion_alloc("audio_client", &buf_circ->client,
+ &buf_circ->handle, bytes_to_alloc,
+ (ion_phys_addr_t *)&buf_circ->phys,
+ &len, &buf_circ->data);
+
+ if (rc) {
+ pr_err("%s: Audio ION alloc is failed, rc = %d\n", __func__,
+ rc);
+ kfree(buf_circ);
+ mutex_unlock(&ac->cmd_lock);
+ goto done;
+ }
+
+ ac->port[dir].buf = buf_circ;
+ buf_circ->used = dir ^ 1;
+ buf_circ->size = bytes_to_alloc;
+ buf_circ->actual_size = bytes_to_alloc;
+ memset(buf_circ->data, 0, buf_circ->actual_size);
+
+ ac->port[dir].max_buf_cnt = 1;
+
+ open->shared_circ_buf_mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+ open->shared_circ_buf_num_regions = 1;
+ open->shared_circ_buf_property_flag = 0x00;
+ open->shared_circ_buf_start_phy_addr_lsw =
+ lower_32_bits(buf_circ->phys);
+ open->shared_circ_buf_start_phy_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_circ->phys);
+ open->shared_circ_buf_size = bufsz * bufcnt;
+
+ open->map_region_circ_buf.shm_addr_lsw = lower_32_bits(buf_circ->phys);
+ open->map_region_circ_buf.shm_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_circ->phys);
+ open->map_region_circ_buf.mem_size_bytes = bytes_to_alloc;
+
+ mutex_unlock(&ac->cmd_lock);
+done:
+ return rc;
+}
+
+
+static
+int q6asm_set_shared_pos_buff(struct audio_client *ac,
+ struct asm_stream_cmd_open_shared_io *open,
+ int dir)
+{
+ struct audio_buffer *buf_pos = &ac->shared_pos_buf;
+ int rc;
+ size_t len;
+ int bytes_to_alloc = sizeof(struct asm_shared_position_buffer);
+
+ mutex_lock(&ac->cmd_lock);
+
+ bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc);
+
+ rc = msm_audio_ion_alloc("audio_client", &buf_pos->client,
+ &buf_pos->handle, bytes_to_alloc,
+ (ion_phys_addr_t *)&buf_pos->phys, &len,
+ &buf_pos->data);
+
+ if (rc) {
+ pr_err("%s: Audio pos buf ION alloc is failed, rc = %d\n",
+ __func__, rc);
+ goto done;
+ }
+
+ buf_pos->used = dir ^ 1;
+ buf_pos->size = bytes_to_alloc;
+ buf_pos->actual_size = bytes_to_alloc;
+
+ open->shared_pos_buf_mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+ open->shared_pos_buf_num_regions = 1;
+ open->shared_pos_buf_property_flag = 0x00;
+ open->shared_pos_buf_phy_addr_lsw = lower_32_bits(buf_pos->phys);
+ open->shared_pos_buf_phy_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_pos->phys);
+
+ open->map_region_pos_buf.shm_addr_lsw = lower_32_bits(buf_pos->phys);
+ open->map_region_pos_buf.shm_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_pos->phys);
+ open->map_region_pos_buf.mem_size_bytes = bytes_to_alloc;
+
+done:
+ mutex_unlock(&ac->cmd_lock);
+ return rc;
+}
+
+/*
+ * q6asm_open_shared_io: Open an ASM session for pull mode (playback)
+ * or push mode (capture).
+ * parameters
+ * config - session parameters (channels, bits_per_sample, sr)
+ * dir - stream direction (IN for playback, OUT for capture)
+ * returns 0 if successful, error code otherwise
+ */
+int q6asm_open_shared_io(struct audio_client *ac,
+ struct shared_io_config *config,
+ int dir)
+{
+ struct asm_stream_cmd_open_shared_io *open;
+ u8 *channel_mapping;
+ int i, size_of_open, num_watermarks, bufsz, bufcnt, rc, flags = 0;
+
+ if (!ac || !config)
+ return -EINVAL;
+
+ if (config->channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__,
+ config->channels);
+ return -EINVAL;
+ }
+
+ bufsz = config->bufsz;
+ bufcnt = config->bufcnt;
+ num_watermarks = 0;
+
+ ac->config = *config;
+
+ if (ac->session <= 0 || ac->session > SESSION_MAX) {
+ pr_err("%s: Session %d is out of bounds\n",
+ __func__, ac->session);
+ return -EINVAL;
+ }
+
+ size_of_open = sizeof(struct asm_stream_cmd_open_shared_io) +
+ (sizeof(struct asm_shared_watermark_level) * num_watermarks);
+
+ open = kzalloc(PAGE_ALIGN(size_of_open), GFP_KERNEL);
+ if (!open)
+ return -ENOMEM;
+
+ q6asm_stream_add_hdr(ac, &open->hdr, size_of_open, TRUE,
+ ac->stream_id);
+
+ atomic_set(&ac->cmd_state, 1);
+
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x, perf %d\n",
+ __func__, open->hdr.token, ac->stream_id, ac->session,
+ ac->perf_mode);
+
+ open->hdr.opcode =
+ dir == IN ? ASM_STREAM_CMD_OPEN_PULL_MODE_WRITE :
+ ASM_STREAM_CMD_OPEN_PUSH_MODE_READ;
+
+ pr_debug("%s perf_mode %d\n", __func__, ac->perf_mode);
+ if (dir == IN)
+ if (ac->perf_mode == ULL_POST_PROCESSING_PCM_MODE)
+ flags = 4 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE;
+ else if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE)
+ flags = 2 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE;
+ else if (ac->perf_mode == LOW_LATENCY_PCM_MODE)
+ flags = 1 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE;
+ else
+ pr_err("Invalid perf mode for pull write\n");
+ else
+ if (ac->perf_mode == LOW_LATENCY_PCM_MODE)
+ flags = ASM_LOW_LATENCY_TX_STREAM_SESSION <<
+ ASM_SHIFT_STREAM_PERF_FLAG_PUSH_MODE_READ;
+ else
+ pr_err("Invalid perf mode for push read\n");
+
+ if (flags == 0) {
+ pr_err("%s: Invalid mode[%d]\n", __func__,
+ ac->perf_mode);
+ kfree(open);
+ return -EINVAL;
+
+ }
+
+ pr_debug("open.mode_flags = 0x%x\n", flags);
+ open->mode_flags = flags;
+ open->endpoint_type = ASM_END_POINT_DEVICE_MATRIX;
+ open->topo_bits_per_sample = config->bits_per_sample;
+
+ open->topo_id = q6asm_get_asm_topology_cal();
+
+ if (config->format == FORMAT_LINEAR_PCM)
+ open->fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3;
+ else {
+ pr_err("%s: Invalid format[%d]\n", __func__, config->format);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rc = q6asm_set_shared_circ_buff(ac, open, bufsz, bufcnt, dir);
+
+ if (rc)
+ goto done;
+
+ ac->port[dir].tmp_hdl = 0;
+
+ rc = q6asm_set_shared_pos_buff(ac, open, dir);
+
+ if (rc)
+ goto done;
+
+ /* asm_multi_channel_pcm_fmt_blk_v3 */
+ open->fmt.num_channels = config->channels;
+ open->fmt.bits_per_sample = config->bits_per_sample;
+ open->fmt.sample_rate = config->rate;
+ open->fmt.is_signed = 1;
+ open->fmt.sample_word_size = config->sample_word_size;
+
+ channel_mapping = open->fmt.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ rc = q6asm_map_channels(channel_mapping, config->channels, false);
+ if (rc) {
+ pr_err("%s: Map channels failed, ret: %d\n", __func__, rc);
+ goto done;
+ }
+
+ open->num_watermark_levels = num_watermarks;
+ for (i = 0; i < num_watermarks; i++) {
+ open->watermark[i].watermark_level_bytes = i *
+ ((bufsz * bufcnt) / num_watermarks);
+ pr_debug("%s: Watermark level set for %i\n",
+ __func__,
+ open->watermark[i].watermark_level_bytes);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) open);
+ if (rc < 0) {
+ pr_err("%s: Open failed op[0x%x]rc[%d]\n",
+ __func__, open->hdr.opcode, rc);
+ goto done;
+ }
+
+ pr_debug("%s: sent open apr pkt\n", __func__);
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) <= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: Timeout. Waited for open write apr pkt rc[%d]\n",
+ __func__, rc);
+ rc = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (atomic_read(&ac->cmd_state) < 0) {
+ pr_err("%s: DSP returned error [%d]\n", __func__,
+ atomic_read(&ac->cmd_state));
+ rc = -EINVAL;
+ goto done;
+ }
+
+ ac->io_mode |= TUN_WRITE_IO_MODE;
+ rc = 0;
+done:
+ kfree(open);
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_open_shared_io);
+
+/*
+ * q6asm_shared_io_buf: Returns handle to the shared circular buffer being
+ * used for pull/push mode.
+ * parameters
+ * dir - used to identify input/output port
+ * returns buffer handle
+ */
+struct audio_buffer *q6asm_shared_io_buf(struct audio_client *ac,
+ int dir)
+{
+ struct audio_port_data *port;
+
+ if (!ac) {
+ pr_err("%s: ac is null\n", __func__);
+ return NULL;
+ }
+ port = &ac->port[dir];
+ return port->buf;
+}
+EXPORT_SYMBOL(q6asm_shared_io_buf);
+
+/*
+ * q6asm_shared_io_free: Frees memory allocated for a pull/push session
+ * parameters
+ * dir - port direction
+ * returns 0 if successful, error otherwise
+ */
+int q6asm_shared_io_free(struct audio_client *ac, int dir)
+{
+ struct audio_port_data *port;
+
+ if (!ac) {
+ pr_err("%s: audio client is null\n", __func__);
+ return -EINVAL;
+ }
+ port = &ac->port[dir];
+ mutex_lock(&ac->cmd_lock);
+ if (port->buf && port->buf->data) {
+ msm_audio_ion_free(port->buf->client, port->buf->handle);
+ port->buf->client = NULL;
+ port->buf->handle = NULL;
+ port->max_buf_cnt = 0;
+ kfree(port->buf);
+ port->buf = NULL;
+ }
+ if (ac->shared_pos_buf.data) {
+ msm_audio_ion_free(ac->shared_pos_buf.client,
+ ac->shared_pos_buf.handle);
+ ac->shared_pos_buf.client = NULL;
+ ac->shared_pos_buf.handle = NULL;
+ }
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+}
+EXPORT_SYMBOL(q6asm_shared_io_free);
+
+/*
+ * q6asm_get_shared_pos: Returns current read index/write index as observed
+ * by the DSP. Note that this is an offset and iterates from [0,BUF_SIZE - 1]
+ * parameters - (all output)
+ * read_index - offset
+ * wall_clk_msw1 - ADSP wallclock msw
+ * wall_clk_lsw1 - ADSP wallclock lsw
+ * returns 0 if successful, -EAGAIN if DSP failed to update after some
+ * retries
+ */
+int q6asm_get_shared_pos(struct audio_client *ac, uint32_t *read_index,
+ uint32_t *wall_clk_msw1, uint32_t *wall_clk_lsw1)
+{
+ struct asm_shared_position_buffer *pos_buf;
+ uint32_t frame_cnt1, frame_cnt2;
+ int i, j;
+
+ if (!ac) {
+ pr_err("%s: audio client is null\n", __func__);
+ return -EINVAL;
+ }
+
+ pos_buf = ac->shared_pos_buf.data;
+
+ /* always try to get the latest update in the shared pos buffer */
+ for (i = 0; i < 2; i++) {
+ /* retry until there is an update from DSP */
+ for (j = 0; j < 5; j++) {
+ frame_cnt1 = pos_buf->frame_counter;
+ if (frame_cnt1 != 0)
+ break;
+ }
+
+ *wall_clk_msw1 = pos_buf->wall_clock_us_msw;
+ *wall_clk_lsw1 = pos_buf->wall_clock_us_lsw;
+ *read_index = pos_buf->index;
+ frame_cnt2 = pos_buf->frame_counter;
+
+ if (frame_cnt1 != frame_cnt2)
+ continue;
+ return 0;
+ }
+ pr_err("%s out of tries trying to get a good read, try again\n",
+ __func__);
+ return -EAGAIN;
+}
+
+int q6asm_run(struct audio_client *ac, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts)
+{
+ struct asm_session_cmd_run_v2 run;
+ int rc;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &run.hdr, sizeof(run), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ run.hdr.opcode = ASM_SESSION_CMD_RUN_V2;
+ run.flags = flags;
+ run.time_lsw = lsw_ts;
+ run.time_msw = msw_ts;
+
+ config_debug_fs_run();
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &run);
+ if (rc < 0) {
+ pr_err("%s: Commmand run failed[%d]",
+ __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for run success",
+ __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+static int __q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts, uint32_t stream_id)
+{
+ struct asm_session_cmd_run_v2 run;
+ int rc;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+ q6asm_stream_add_hdr_async(ac, &run.hdr, sizeof(run), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, 1);
+ run.hdr.opcode = ASM_SESSION_CMD_RUN_V2;
+ run.flags = flags;
+ run.time_lsw = lsw_ts;
+ run.time_msw = msw_ts;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &run);
+ if (rc < 0) {
+ pr_err("%s: Commmand run failed[%d]", __func__, rc);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts)
+{
+ return __q6asm_run_nowait(ac, flags, msw_ts, lsw_ts, ac->stream_id);
+}
+
+int q6asm_stream_run_nowait(struct audio_client *ac, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts, uint32_t stream_id)
+{
+ return __q6asm_run_nowait(ac, flags, msw_ts, lsw_ts, stream_id);
+}
+
+int q6asm_enc_cfg_blk_aac(struct audio_client *ac,
+ uint32_t frames_per_buf,
+ uint32_t sample_rate, uint32_t channels,
+ uint32_t bit_rate, uint32_t mode, uint32_t format)
+{
+ struct asm_aac_enc_cfg_v2 enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s: session[%d]frames[%d]SR[%d]ch[%d]bitrate[%d]mode[%d] format[%d]\n",
+ __func__, ac->session, frames_per_buf,
+ sample_rate, channels, bit_rate, mode, format);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(struct asm_aac_enc_cfg_v2) -
+ sizeof(struct asm_stream_cmd_set_encdec_param);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(struct asm_enc_cfg_blk_param_v2);
+ enc_cfg.bit_rate = bit_rate;
+ enc_cfg.enc_mode = mode;
+ enc_cfg.aac_fmt_flag = format;
+ enc_cfg.channel_cfg = channels;
+ enc_cfg.sample_rate = sample_rate;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Comamnd %d failed %d\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for FORMAT_UPDATE\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_enc_cfg_blk_g711(struct audio_client *ac,
+ uint32_t frames_per_buf,
+ uint32_t sample_rate)
+{
+ struct asm_g711_enc_cfg_v2 enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s: session[%d]frames[%d]SR[%d]\n",
+ __func__, ac->session, frames_per_buf,
+ sample_rate);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(struct asm_g711_enc_cfg_v2) -
+ sizeof(struct asm_stream_cmd_set_encdec_param);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(struct asm_enc_cfg_blk_param_v2);
+ enc_cfg.sample_rate = sample_rate;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Comamnd %d failed %d\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for FORMAT_UPDATE\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_set_encdec_chan_map(struct audio_client *ac,
+ uint32_t num_channels)
+{
+ struct asm_dec_out_chan_map_param chan_map;
+ u8 *channel_mapping;
+ int rc = 0;
+ pr_debug("%s: Session %d, num_channels = %d\n",
+ __func__, ac->session, num_channels);
+
+ if (num_channels > MAX_CHAN_MAP_CHANNELS) {
+ pr_err("%s: Invalid channel count %d\n", __func__,
+ num_channels);
+ return -EINVAL;
+ }
+
+ q6asm_add_hdr(ac, &chan_map.hdr, sizeof(chan_map), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ chan_map.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ chan_map.encdec.param_id = ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP;
+ chan_map.encdec.param_size = sizeof(struct asm_dec_out_chan_map_param) -
+ (sizeof(struct apr_hdr) +
+ sizeof(struct asm_stream_cmd_set_encdec_param));
+ chan_map.num_channels = num_channels;
+ channel_mapping = chan_map.channel_mapping;
+ memset(channel_mapping, PCM_CHANNEL_NULL, MAX_CHAN_MAP_CHANNELS);
+
+ if (q6asm_map_channels(channel_mapping, num_channels, false)) {
+ pr_err("%s: map channels failed %d\n", __func__, num_channels);
+ return -EINVAL;
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &chan_map);
+ if (rc < 0) {
+ pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+ ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout opcode[0x%x]\n", __func__,
+ chan_map.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+/*
+ * q6asm_enc_cfg_blk_pcm_v5 - sends encoder configuration parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map to be used
+ * @use_back_flavor: to configure back left and right channel
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+static int q6asm_enc_cfg_blk_pcm_v5(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample, bool use_default_chmap,
+ bool use_back_flavor, u8 *channel_map,
+ uint16_t sample_word_size, uint16_t endianness,
+ uint16_t mode)
+{
+ struct asm_multi_channel_pcm_enc_cfg_v5 enc_cfg;
+ struct asm_enc_cfg_blk_param_v2 enc_fg_blk;
+ u8 *channel_mapping;
+ u32 frames_per_buf = 0;
+ int rc;
+
+ if (!use_default_chmap && (channel_map == NULL)) {
+ pr_err("%s: No valid chan map and can't use default\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL_V2) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+ ac->session, rate, channels,
+ bits_per_sample, sample_word_size);
+
+ memset(&enc_cfg, 0, sizeof(enc_cfg));
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
+ sizeof(enc_cfg.encdec);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(enc_fg_blk);
+ enc_cfg.num_channels = channels;
+ enc_cfg.bits_per_sample = bits_per_sample;
+ enc_cfg.sample_rate = rate;
+ enc_cfg.is_signed = 1;
+ enc_cfg.sample_word_size = sample_word_size;
+ enc_cfg.endianness = endianness;
+ enc_cfg.mode = mode;
+ channel_mapping = enc_cfg.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL_V2);
+
+ if (use_default_chmap) {
+ pr_debug("%s: setting default channel map for %d channels",
+ __func__, channels);
+ if (q6asm_map_channels(channel_mapping, channels,
+ use_back_flavor)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {
+ pr_debug("%s: Using pre-defined channel map", __func__);
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL_V2);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Command open failed %d\n", __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, enc_cfg.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v5);
+
+/*
+ * q6asm_enc_cfg_blk_pcm_v4 - sends encoder configuration parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map to be used
+ * @use_back_flavor: to configure back left and right channel
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample, bool use_default_chmap,
+ bool use_back_flavor, u8 *channel_map,
+ uint16_t sample_word_size, uint16_t endianness,
+ uint16_t mode)
+{
+ struct asm_multi_channel_pcm_enc_cfg_v4 enc_cfg;
+ struct asm_enc_cfg_blk_param_v2 enc_fg_blk;
+ u8 *channel_mapping;
+ u32 frames_per_buf = 0;
+ int rc;
+
+ if (!use_default_chmap && (channel_map == NULL)) {
+ pr_err("%s: No valid chan map and can't use default\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+ ac->session, rate, channels,
+ bits_per_sample, sample_word_size);
+
+ memset(&enc_cfg, 0, sizeof(enc_cfg));
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
+ sizeof(enc_cfg.encdec);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(enc_fg_blk);
+ enc_cfg.num_channels = channels;
+ enc_cfg.bits_per_sample = bits_per_sample;
+ enc_cfg.sample_rate = rate;
+ enc_cfg.is_signed = 1;
+ enc_cfg.sample_word_size = sample_word_size;
+ enc_cfg.endianness = endianness;
+ enc_cfg.mode = mode;
+ channel_mapping = enc_cfg.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ pr_debug("%s: setting default channel map for %d channels",
+ __func__, channels);
+ if (q6asm_map_channels(channel_mapping, channels,
+ use_back_flavor)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {
+ pr_debug("%s: Using pre-defined channel map", __func__);
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Command open failed %d\n", __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, enc_cfg.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v4);
+
+/*
+ * q6asm_enc_cfg_blk_pcm_v3 - sends encoder configuration parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map to be used
+ * @use_back_flavor: to configure back left and right channel
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ */
+int q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample, bool use_default_chmap,
+ bool use_back_flavor, u8 *channel_map,
+ uint16_t sample_word_size)
+{
+ struct asm_multi_channel_pcm_enc_cfg_v3 enc_cfg;
+ struct asm_enc_cfg_blk_param_v2 enc_fg_blk;
+ u8 *channel_mapping;
+ u32 frames_per_buf = 0;
+ int rc;
+
+ if (!use_default_chmap && (channel_map == NULL)) {
+ pr_err("%s: No valid chan map and can't use default\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+ ac->session, rate, channels,
+ bits_per_sample, sample_word_size);
+
+ memset(&enc_cfg, 0, sizeof(enc_cfg));
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
+ sizeof(enc_cfg.encdec);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(enc_fg_blk);
+ enc_cfg.num_channels = channels;
+ enc_cfg.bits_per_sample = bits_per_sample;
+ enc_cfg.sample_rate = rate;
+ enc_cfg.is_signed = 1;
+ enc_cfg.sample_word_size = sample_word_size;
+ channel_mapping = enc_cfg.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ pr_debug("%s: setting default channel map for %d channels",
+ __func__, channels);
+ if (q6asm_map_channels(channel_mapping, channels,
+ use_back_flavor)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {
+ pr_debug("%s: Using pre-defined channel map", __func__);
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, enc_cfg.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v3);
+
+int q6asm_enc_cfg_blk_pcm_v2(struct audio_client *ac,
+ uint32_t rate, uint32_t channels, uint16_t bits_per_sample,
+ bool use_default_chmap, bool use_back_flavor, u8 *channel_map)
+{
+ struct asm_multi_channel_pcm_enc_cfg_v2 enc_cfg;
+ u8 *channel_mapping;
+ u32 frames_per_buf = 0;
+
+ int rc = 0;
+
+ if (!use_default_chmap && (channel_map == NULL)) {
+ pr_err("%s: No valid chan map and can't use default\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__,
+ ac->session, rate, channels);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
+ sizeof(enc_cfg.encdec);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(struct asm_enc_cfg_blk_param_v2);
+
+ enc_cfg.num_channels = channels;
+ enc_cfg.bits_per_sample = bits_per_sample;
+ enc_cfg.sample_rate = rate;
+ enc_cfg.is_signed = 1;
+ channel_mapping = enc_cfg.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ pr_debug("%s: setting default channel map for %d channels",
+ __func__, channels);
+ if (q6asm_map_channels(channel_mapping, channels,
+ use_back_flavor)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+ } else {
+ pr_debug("%s: Using pre-defined channel map", __func__);
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, enc_cfg.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+static int __q6asm_enc_cfg_blk_pcm_v5(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ return q6asm_enc_cfg_blk_pcm_v5(ac, rate, channels,
+ bits_per_sample, true, false, NULL,
+ sample_word_size, endianness, mode);
+}
+
+static int __q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ return q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels,
+ bits_per_sample, true, false, NULL,
+ sample_word_size, endianness, mode);
+}
+
+static int __q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size)
+{
+ return q6asm_enc_cfg_blk_pcm_v3(ac, rate, channels,
+ bits_per_sample, true, false, NULL,
+ sample_word_size);
+}
+
+static int __q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels, uint16_t bits_per_sample)
+{
+ return q6asm_enc_cfg_blk_pcm_v2(ac, rate, channels,
+ bits_per_sample, true, false, NULL);
+}
+
+int q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ return __q6asm_enc_cfg_blk_pcm(ac, rate, channels, 16);
+}
+
+int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
+ uint32_t rate, uint32_t channels, uint16_t bits_per_sample)
+{
+ return __q6asm_enc_cfg_blk_pcm(ac, rate, channels, bits_per_sample);
+}
+
+/*
+ * q6asm_enc_cfg_blk_pcm_format_support_v3 - sends encoder configuration
+ * parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ */
+int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size)
+{
+ return __q6asm_enc_cfg_blk_pcm_v3(ac, rate, channels,
+ bits_per_sample, sample_word_size);
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v3);
+
+/*
+ * q6asm_enc_cfg_blk_pcm_format_support_v4 - sends encoder configuration
+ * parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ return __q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels,
+ bits_per_sample, sample_word_size,
+ endianness, mode);
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v4);
+
+/*
+ * q6asm_enc_cfg_blk_pcm_format_support_v5 - sends encoder configuration
+ * parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_enc_cfg_blk_pcm_format_support_v5(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ return __q6asm_enc_cfg_blk_pcm_v5(ac, rate, channels,
+ bits_per_sample, sample_word_size,
+ endianness, mode);
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v5);
+
+int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct asm_multi_channel_pcm_enc_cfg_v2 enc_cfg;
+ u8 *channel_mapping;
+ u32 frames_per_buf = 0;
+ int rc = 0;
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__,
+ ac->session, rate, channels);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
+ sizeof(enc_cfg.encdec);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(struct asm_enc_cfg_blk_param_v2);
+
+ enc_cfg.num_channels = 0;/*channels;*/
+ enc_cfg.bits_per_sample = 16;
+ enc_cfg.sample_rate = 0;/*rate;*/
+ enc_cfg.is_signed = 1;
+ channel_mapping = enc_cfg.channel_mapping;
+
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (q6asm_map_channels(channel_mapping, channels, false)) {
+ pr_err("%s: map channels failed %d\n", __func__, channels);
+ return -EINVAL;
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, enc_cfg.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+static int q6asm_map_channels(u8 *channel_mapping, uint32_t channels,
+ bool use_back_flavor)
+{
+ u8 *lchannel_mapping;
+ lchannel_mapping = channel_mapping;
+ pr_debug("%s: channels passed: %d\n", __func__, channels);
+ if (channels == 1) {
+ lchannel_mapping[0] = PCM_CHANNEL_FC;
+ } else if (channels == 2) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channels == 3) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_FC;
+ } else if (channels == 4) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = use_back_flavor ?
+ PCM_CHANNEL_LB : PCM_CHANNEL_LS;
+ lchannel_mapping[3] = use_back_flavor ?
+ PCM_CHANNEL_RB : PCM_CHANNEL_RS;
+ } else if (channels == 5) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_FC;
+ lchannel_mapping[3] = use_back_flavor ?
+ PCM_CHANNEL_LB : PCM_CHANNEL_LS;
+ lchannel_mapping[4] = use_back_flavor ?
+ PCM_CHANNEL_RB : PCM_CHANNEL_RS;
+ } else if (channels == 6) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_FC;
+ lchannel_mapping[3] = PCM_CHANNEL_LFE;
+ lchannel_mapping[4] = use_back_flavor ?
+ PCM_CHANNEL_LB : PCM_CHANNEL_LS;
+ lchannel_mapping[5] = use_back_flavor ?
+ PCM_CHANNEL_RB : PCM_CHANNEL_RS;
+ } else if (channels == 7) {
+ /*
+ * Configured for 5.1 channel mapping + 1 channel for debug
+ * Can be customized based on DSP.
+ */
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_FC;
+ lchannel_mapping[3] = PCM_CHANNEL_LFE;
+ lchannel_mapping[4] = use_back_flavor ?
+ PCM_CHANNEL_LB : PCM_CHANNEL_LS;
+ lchannel_mapping[5] = use_back_flavor ?
+ PCM_CHANNEL_RB : PCM_CHANNEL_RS;
+ lchannel_mapping[6] = PCM_CHANNEL_CS;
+ } else if (channels == 8) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_FC;
+ lchannel_mapping[3] = PCM_CHANNEL_LFE;
+ lchannel_mapping[4] = PCM_CHANNEL_LB;
+ lchannel_mapping[5] = PCM_CHANNEL_RB;
+ lchannel_mapping[6] = PCM_CHANNEL_LS;
+ lchannel_mapping[7] = PCM_CHANNEL_RS;
+ } else if (channels == 10) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_LFE;
+ lchannel_mapping[3] = PCM_CHANNEL_FC;
+ lchannel_mapping[4] = PCM_CHANNEL_LS;
+ lchannel_mapping[5] = PCM_CHANNEL_RS;
+ lchannel_mapping[6] = PCM_CHANNEL_LB;
+ lchannel_mapping[7] = PCM_CHANNEL_RB;
+ lchannel_mapping[8] = PCM_CHANNEL_CS;
+ lchannel_mapping[9] = PCM_CHANNELS;
+ } else if (channels == 16) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_LFE;
+ lchannel_mapping[3] = PCM_CHANNEL_FC;
+ lchannel_mapping[4] = PCM_CHANNEL_LS;
+ lchannel_mapping[5] = PCM_CHANNEL_RS;
+ lchannel_mapping[6] = PCM_CHANNEL_LB;
+ lchannel_mapping[7] = PCM_CHANNEL_RB;
+ lchannel_mapping[8] = PCM_CHANNEL_CS;
+ lchannel_mapping[9] = PCM_CHANNELS;
+ lchannel_mapping[10] = PCM_CHANNEL_CVH;
+ lchannel_mapping[11] = PCM_CHANNEL_MS;
+ lchannel_mapping[12] = PCM_CHANNEL_FLC;
+ lchannel_mapping[13] = PCM_CHANNEL_FRC;
+ lchannel_mapping[14] = PCM_CHANNEL_RLC;
+ lchannel_mapping[15] = PCM_CHANNEL_RRC;
+ } else if (channels == 32) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ lchannel_mapping[2] = PCM_CHANNEL_LFE;
+ lchannel_mapping[3] = PCM_CHANNEL_FC;
+ lchannel_mapping[4] = PCM_CHANNEL_LS;
+ lchannel_mapping[5] = PCM_CHANNEL_RS;
+ lchannel_mapping[6] = PCM_CHANNEL_LB;
+ lchannel_mapping[7] = PCM_CHANNEL_RB;
+ lchannel_mapping[8] = PCM_CHANNEL_CS;
+ lchannel_mapping[9] = PCM_CHANNELS;
+ lchannel_mapping[10] = PCM_CHANNEL_CVH;
+ lchannel_mapping[11] = PCM_CHANNEL_MS;
+ lchannel_mapping[12] = PCM_CHANNEL_FLC;
+ lchannel_mapping[13] = PCM_CHANNEL_FRC;
+ lchannel_mapping[14] = PCM_CHANNEL_RLC;
+ lchannel_mapping[15] = PCM_CHANNEL_RRC;
+ lchannel_mapping[16] = PCM_CHANNEL_LFE2;
+ lchannel_mapping[17] = PCM_CHANNEL_SL;
+ lchannel_mapping[18] = PCM_CHANNEL_SR;
+ lchannel_mapping[19] = PCM_CHANNEL_TFL;
+ lchannel_mapping[20] = PCM_CHANNEL_TFR;
+ lchannel_mapping[21] = PCM_CHANNEL_TC;
+ lchannel_mapping[22] = PCM_CHANNEL_TBL;
+ lchannel_mapping[23] = PCM_CHANNEL_TBR;
+ lchannel_mapping[24] = PCM_CHANNEL_TSL;
+ lchannel_mapping[25] = PCM_CHANNEL_TSR;
+ lchannel_mapping[26] = PCM_CHANNEL_TBC;
+ lchannel_mapping[27] = PCM_CHANNEL_BFC;
+ lchannel_mapping[28] = PCM_CHANNEL_BFL;
+ lchannel_mapping[29] = PCM_CHANNEL_BFR;
+ lchannel_mapping[30] = PCM_CHANNEL_LW;
+ lchannel_mapping[31] = PCM_CHANNEL_RW;
+ } else {
+ pr_err("%s: ERROR.unsupported num_ch = %u\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int q6asm_enable_sbrps(struct audio_client *ac,
+ uint32_t sbr_ps_enable)
+{
+ struct asm_aac_sbr_ps_flag_param sbrps;
+ u32 frames_per_buf = 0;
+
+ int rc = 0;
+
+ pr_debug("%s: Session %d\n", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &sbrps.hdr, sizeof(sbrps), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ sbrps.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ sbrps.encdec.param_id = ASM_PARAM_ID_AAC_SBR_PS_FLAG;
+ sbrps.encdec.param_size = sizeof(struct asm_aac_sbr_ps_flag_param) -
+ sizeof(struct asm_stream_cmd_set_encdec_param);
+ sbrps.encblk.frames_per_buf = frames_per_buf;
+ sbrps.encblk.enc_cfg_blk_size = sbrps.encdec.param_size -
+ sizeof(struct asm_enc_cfg_blk_param_v2);
+
+ sbrps.sbr_ps_flag = sbr_ps_enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &sbrps);
+ if (rc < 0) {
+ pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n",
+ __func__,
+ ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+ ASM_PARAM_ID_AAC_SBR_PS_FLAG, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout opcode[0x%x] ", __func__, sbrps.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
+ uint16_t sce_left, uint16_t sce_right)
+{
+ struct asm_aac_dual_mono_mapping_param dual_mono;
+
+ int rc = 0;
+
+ pr_debug("%s: Session %d, sce_left = %d, sce_right = %d\n",
+ __func__, ac->session, sce_left, sce_right);
+
+ q6asm_add_hdr(ac, &dual_mono.hdr, sizeof(dual_mono), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ dual_mono.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ dual_mono.encdec.param_id = ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING;
+ dual_mono.encdec.param_size = sizeof(dual_mono.left_channel_sce) +
+ sizeof(dual_mono.right_channel_sce);
+ dual_mono.left_channel_sce = sce_left;
+ dual_mono.right_channel_sce = sce_right;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &dual_mono);
+ if (rc < 0) {
+ pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+ ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout opcode[0x%x]\n", __func__,
+ dual_mono.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+/* Support for selecting stereo mixing coefficients for B family not done */
+int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff)
+{
+ struct asm_aac_stereo_mix_coeff_selection_param_v2 aac_mix_coeff;
+ int rc = 0;
+
+ q6asm_add_hdr(ac, &aac_mix_coeff.hdr, sizeof(aac_mix_coeff), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ aac_mix_coeff.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ aac_mix_coeff.param_id =
+ ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2;
+ aac_mix_coeff.param_size =
+ sizeof(struct asm_aac_stereo_mix_coeff_selection_param_v2);
+ aac_mix_coeff.aac_stereo_mix_coeff_flag = mix_coeff;
+ pr_debug("%s: mix_coeff = %u\n", __func__, mix_coeff);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &aac_mix_coeff);
+ if (rc < 0) {
+ pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+ ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2,
+ rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, aac_mix_coeff.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t min_rate, uint16_t max_rate,
+ uint16_t reduced_rate_level, uint16_t rate_modulation_cmd)
+{
+ struct asm_v13k_enc_cfg enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s: session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] reduced_rate_level[0x%4x]rate_modulation_cmd[0x%4x]\n",
+ __func__,
+ ac->session, frames_per_buf, min_rate, max_rate,
+ reduced_rate_level, rate_modulation_cmd);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(struct asm_v13k_enc_cfg) -
+ sizeof(struct asm_stream_cmd_set_encdec_param);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(struct asm_enc_cfg_blk_param_v2);
+
+ enc_cfg.min_rate = min_rate;
+ enc_cfg.max_rate = max_rate;
+ enc_cfg.reduced_rate_cmd = reduced_rate_level;
+ enc_cfg.rate_mod_cmd = rate_modulation_cmd;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Comamnd %d failed %d\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for setencdec v13k resp\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t min_rate, uint16_t max_rate,
+ uint16_t rate_modulation_cmd)
+{
+ struct asm_evrc_enc_cfg enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s: session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] rate_modulation_cmd[0x%4x]\n",
+ __func__, ac->session,
+ frames_per_buf, min_rate, max_rate, rate_modulation_cmd);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(struct asm_evrc_enc_cfg) -
+ sizeof(struct asm_stream_cmd_set_encdec_param);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(struct asm_enc_cfg_blk_param_v2);
+
+ enc_cfg.min_rate = min_rate;
+ enc_cfg.max_rate = max_rate;
+ enc_cfg.rate_mod_cmd = rate_modulation_cmd;
+ enc_cfg.reserved = 0;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Comamnd %d failed %d\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for encdec evrc\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t band_mode, uint16_t dtx_enable)
+{
+ struct asm_amrnb_enc_cfg enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s: session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]\n",
+ __func__, ac->session, frames_per_buf, band_mode, dtx_enable);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(struct asm_amrnb_enc_cfg) -
+ sizeof(struct asm_stream_cmd_set_encdec_param);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(struct asm_enc_cfg_blk_param_v2);
+
+ enc_cfg.enc_mode = band_mode;
+ enc_cfg.dtx_mode = dtx_enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Comamnd %d failed %d\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for set encdec amrnb\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_enc_cfg_blk_amrwb(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t band_mode, uint16_t dtx_enable)
+{
+ struct asm_amrwb_enc_cfg enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s: session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]\n",
+ __func__, ac->session, frames_per_buf, band_mode, dtx_enable);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(struct asm_amrwb_enc_cfg) -
+ sizeof(struct asm_stream_cmd_set_encdec_param);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(struct asm_enc_cfg_blk_param_v2);
+
+ enc_cfg.enc_mode = band_mode;
+ enc_cfg.dtx_mode = dtx_enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Comamnd %d failed %d\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+
+static int __q6asm_media_format_block_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample, int stream_id,
+ bool use_default_chmap, char *channel_map)
+{
+ struct asm_multi_channel_pcm_fmt_blk_v2 fmt;
+ u8 *channel_mapping;
+ int rc = 0;
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate,
+ channels);
+
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ q6asm_update_token(&fmt.hdr.token,
+ ac->session,
+ stream_id,
+ 0, /* Buffer index is NA */
+ 0, /* Direction flag is NA */
+ WAIT_CMD);
+
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, fmt.hdr.token, stream_id, ac->session);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.num_channels = channels;
+ fmt.bits_per_sample = bits_per_sample;
+ fmt.sample_rate = rate;
+ fmt.is_signed = 1;
+
+ channel_mapping = fmt.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ if (q6asm_map_channels(channel_mapping, channels, false)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+ } else {
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for format update\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+static int __q6asm_media_format_block_pcm_v3(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ int stream_id,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t sample_word_size)
+{
+ struct asm_multi_channel_pcm_fmt_blk_param_v3 fmt;
+ u8 *channel_mapping;
+ int rc;
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+ ac->session, rate, channels,
+ bits_per_sample, sample_word_size);
+
+ memset(&fmt, 0, sizeof(fmt));
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) |
+ (stream_id & 0xFF);
+
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, fmt.hdr.token, stream_id, ac->session);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.param.num_channels = channels;
+ fmt.param.bits_per_sample = bits_per_sample;
+ fmt.param.sample_rate = rate;
+ fmt.param.is_signed = 1;
+ fmt.param.sample_word_size = sample_word_size;
+ channel_mapping = fmt.param.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ if (q6asm_map_channels(channel_mapping, channels, false)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for format update\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+static int __q6asm_media_format_block_pcm_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ int stream_id,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt;
+ u8 *channel_mapping;
+ int rc;
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+ ac->session, rate, channels,
+ bits_per_sample, sample_word_size);
+
+ memset(&fmt, 0, sizeof(fmt));
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) |
+ (stream_id & 0xFF);
+
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, fmt.hdr.token, stream_id, ac->session);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.param.num_channels = channels;
+ fmt.param.bits_per_sample = bits_per_sample;
+ fmt.param.sample_rate = rate;
+ fmt.param.is_signed = 1;
+ fmt.param.sample_word_size = sample_word_size;
+ fmt.param.endianness = endianness;
+ fmt.param.mode = mode;
+ channel_mapping = fmt.param.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ if (q6asm_map_channels(channel_mapping, channels, false)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for format update\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_media_format_block_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ return __q6asm_media_format_block_pcm(ac, rate,
+ channels, 16, ac->stream_id,
+ true, NULL);
+}
+
+int q6asm_media_format_block_pcm_format_support(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_media_format_block_pcm(ac, rate,
+ channels, bits_per_sample, ac->stream_id,
+ true, NULL);
+}
+
+int q6asm_media_format_block_pcm_format_support_v2(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample, int stream_id,
+ bool use_default_chmap, char *channel_map)
+{
+ if (!use_default_chmap && (channel_map == NULL)) {
+ pr_err("%s: No valid chan map and can't use default\n",
+ __func__);
+ return -EINVAL;
+ }
+ return __q6asm_media_format_block_pcm(ac, rate,
+ channels, bits_per_sample, stream_id,
+ use_default_chmap, channel_map);
+}
+
+/*
+ * q6asm_media_format_block_pcm_format_support_v3- sends pcm decoder
+ * configuration parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @stream_id: stream id of stream to be associated with this session
+ * @use_default_chmap: true if default channel map to be used
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ */
+int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac,
+ uint32_t rate,
+ uint32_t channels,
+ uint16_t bits_per_sample,
+ int stream_id,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t sample_word_size)
+{
+ if (!use_default_chmap && (channel_map == NULL)) {
+ pr_err("%s: No valid chan map and can't use default\n",
+ __func__);
+ return -EINVAL;
+ }
+ return __q6asm_media_format_block_pcm_v3(ac, rate,
+ channels, bits_per_sample, stream_id,
+ use_default_chmap, channel_map,
+ sample_word_size);
+
+}
+EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v3);
+
+/*
+ * q6asm_media_format_block_pcm_format_support_v4- sends pcm decoder
+ * configuration parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @stream_id: stream id of stream to be associated with this session
+ * @use_default_chmap: true if default channel map to be used
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac,
+ uint32_t rate,
+ uint32_t channels,
+ uint16_t bits_per_sample,
+ int stream_id,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ if (!use_default_chmap && (channel_map == NULL)) {
+ pr_err("%s: No valid chan map and can't use default\n",
+ __func__);
+ return -EINVAL;
+ }
+ return __q6asm_media_format_block_pcm_v4(ac, rate,
+ channels, bits_per_sample, stream_id,
+ use_default_chmap, channel_map,
+ sample_word_size, endianness,
+ mode);
+
+}
+EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v4);
+
+
+static int __q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap, char *channel_map,
+ uint16_t bits_per_sample)
+{
+ struct asm_multi_channel_pcm_fmt_blk_v2 fmt;
+ u8 *channel_mapping;
+ int rc = 0;
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate,
+ channels);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.num_channels = channels;
+ fmt.bits_per_sample = bits_per_sample;
+ fmt.sample_rate = rate;
+ fmt.is_signed = 1;
+
+ channel_mapping = fmt.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ if (q6asm_map_channels(channel_mapping, channels, false)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+ } else {
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for format update\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+static int __q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac,
+ uint32_t rate,
+ uint32_t channels,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size)
+{
+ struct asm_multi_channel_pcm_fmt_blk_param_v3 fmt;
+ u8 *channel_mapping;
+ int rc;
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+ ac->session, rate, channels,
+ bits_per_sample, sample_word_size);
+
+ memset(&fmt, 0, sizeof(fmt));
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.param.num_channels = channels;
+ fmt.param.bits_per_sample = bits_per_sample;
+ fmt.param.sample_rate = rate;
+ fmt.param.is_signed = 1;
+ fmt.param.sample_word_size = sample_word_size;
+ channel_mapping = fmt.param.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ if (q6asm_map_channels(channel_mapping, channels, false)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for format update\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+static int __q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac,
+ uint32_t rate,
+ uint32_t channels,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt;
+ u8 *channel_mapping;
+ int rc;
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+ ac->session, rate, channels,
+ bits_per_sample, sample_word_size);
+
+ memset(&fmt, 0, sizeof(fmt));
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.param.num_channels = channels;
+ fmt.param.bits_per_sample = bits_per_sample;
+ fmt.param.sample_rate = rate;
+ fmt.param.is_signed = 1;
+ fmt.param.sample_word_size = sample_word_size;
+ fmt.param.endianness = endianness;
+ fmt.param.mode = mode;
+ channel_mapping = fmt.param.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ if (q6asm_map_channels(channel_mapping, channels, false)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for format update\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+static int __q6asm_media_format_block_multi_ch_pcm_v5(struct audio_client *ac,
+ uint32_t rate,
+ uint32_t channels,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ struct asm_multi_channel_pcm_fmt_blk_param_v5 fmt;
+ u8 *channel_mapping;
+ int rc;
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL_V2) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+ ac->session, rate, channels,
+ bits_per_sample, sample_word_size);
+
+ memset(&fmt, 0, sizeof(fmt));
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.param.num_channels = channels;
+ fmt.param.bits_per_sample = bits_per_sample;
+ fmt.param.sample_rate = rate;
+ fmt.param.is_signed = 1;
+ fmt.param.sample_word_size = sample_word_size;
+ fmt.param.endianness = endianness;
+ fmt.param.mode = mode;
+ channel_mapping = fmt.param.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL_V2);
+
+ if (use_default_chmap) {
+ if (q6asm_map_channels(channel_mapping, channels, false)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL_V2);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for format update\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap, char *channel_map)
+{
+ return __q6asm_media_format_block_multi_ch_pcm(ac, rate,
+ channels, use_default_chmap, channel_map, 16);
+}
+
+int q6asm_media_format_block_multi_ch_pcm_v2(
+ struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap, char *channel_map,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_media_format_block_multi_ch_pcm(ac, rate,
+ channels, use_default_chmap, channel_map,
+ bits_per_sample);
+}
+
+/*
+ * q6asm_media_format_block_multi_ch_pcm_v3 - sends pcm decoder configuration
+ * parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map to be used
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ */
+int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size)
+{
+ return __q6asm_media_format_block_multi_ch_pcm_v3(ac, rate, channels,
+ use_default_chmap,
+ channel_map,
+ bits_per_sample,
+ sample_word_size);
+}
+EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v3);
+
+/*
+ * q6asm_media_format_block_multi_ch_pcm_v4 - sends pcm decoder configuration
+ * parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map to be used
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ return __q6asm_media_format_block_multi_ch_pcm_v4(ac, rate, channels,
+ use_default_chmap,
+ channel_map,
+ bits_per_sample,
+ sample_word_size,
+ endianness,
+ mode);
+}
+EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v4);
+
+/*
+ * q6asm_media_format_block_multi_ch_pcm_v5 - sends pcm decoder configuration
+ * parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map to be used
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_media_format_block_multi_ch_pcm_v5(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ return __q6asm_media_format_block_multi_ch_pcm_v5(ac, rate, channels,
+ use_default_chmap,
+ channel_map,
+ bits_per_sample,
+ sample_word_size,
+ endianness,
+ mode);
+}
+EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v5);
+
+
+/*
+ * q6asm_media_format_block_gen_compr - set up generic compress format params
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @use_default_chmap: true if default channel map to be used
+ * @channel_map: input channel map
+ * @bits_per_sample: bit width of gen compress stream
+ */
+int q6asm_media_format_block_gen_compr(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap, char *channel_map,
+ uint16_t bits_per_sample)
+{
+ struct asm_generic_compressed_fmt_blk_t fmt;
+ u8 *channel_mapping;
+ int rc = 0;
+
+ if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) {
+ pr_err("%s: Invalid channel count %d\n", __func__, channels);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]\n",
+ __func__, ac->session, rate,
+ channels, bits_per_sample);
+
+ memset(&fmt, 0, sizeof(fmt));
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.num_channels = channels;
+ fmt.bits_per_sample = bits_per_sample;
+ fmt.sampling_rate = rate;
+
+ channel_mapping = fmt.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ if (q6asm_map_channels(channel_mapping, channels, false)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+ } else {
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ atomic_set(&ac->cmd_state, -1);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for format update\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_media_format_block_gen_compr);
+
+
+/*
+ * q6asm_media_format_block_iec - set up IEC61937 (compressed) or IEC60958
+ * (pcm) format params. Both audio standards
+ * use the same format and are used for
+ * HDMI or SPDIF.
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ */
+int q6asm_media_format_block_iec(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct asm_iec_compressed_fmt_blk_t fmt;
+ int rc = 0;
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]\n",
+ __func__, ac->session, rate,
+ channels);
+
+ memset(&fmt, 0, sizeof(fmt));
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_IEC_60958_MEDIA_FMT;
+ fmt.num_channels = channels;
+ fmt.sampling_rate = rate;
+
+ atomic_set(&ac->cmd_state, -1);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for format update\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_media_format_block_iec);
+
+static int __q6asm_media_format_block_multi_aac(struct audio_client *ac,
+ struct asm_aac_cfg *cfg, int stream_id)
+{
+ struct asm_aac_fmt_blk_v2 fmt;
+ int rc = 0;
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session,
+ cfg->sample_rate, cfg->ch_cfg);
+
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ q6asm_update_token(&fmt.hdr.token,
+ ac->session,
+ stream_id,
+ 0, /* Buffer index is NA */
+ 0, /* Direction flag is NA */
+ WAIT_CMD);
+
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, fmt.hdr.token, stream_id, ac->session);
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.aac_fmt_flag = cfg->format;
+ fmt.audio_objype = cfg->aot;
+ /* If zero, PCE is assumed to be available in bitstream*/
+ fmt.total_size_of_PCE_bits = 0;
+ fmt.channel_config = cfg->ch_cfg;
+ fmt.sample_rate = cfg->sample_rate;
+
+ pr_debug("%s: format=0x%x cfg_size=%d aac-cfg=0x%x aot=%d ch=%d sr=%d\n",
+ __func__, fmt.aac_fmt_flag, fmt.fmt_blk.fmt_blk_size,
+ fmt.aac_fmt_flag,
+ fmt.audio_objype,
+ fmt.channel_config,
+ fmt.sample_rate);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_media_format_block_multi_aac(struct audio_client *ac,
+ struct asm_aac_cfg *cfg)
+{
+ return __q6asm_media_format_block_multi_aac(ac, cfg, ac->stream_id);
+}
+
+int q6asm_media_format_block_aac(struct audio_client *ac,
+ struct asm_aac_cfg *cfg)
+{
+ return __q6asm_media_format_block_multi_aac(ac, cfg, ac->stream_id);
+}
+
+int q6asm_stream_media_format_block_aac(struct audio_client *ac,
+ struct asm_aac_cfg *cfg, int stream_id)
+{
+ return __q6asm_media_format_block_multi_aac(ac, cfg, stream_id);
+}
+
+int q6asm_media_format_block_wma(struct audio_client *ac,
+ void *cfg, int stream_id)
+{
+ struct asm_wmastdv9_fmt_blk_v2 fmt;
+ struct asm_wma_cfg *wma_cfg = (struct asm_wma_cfg *)cfg;
+ int rc = 0;
+
+ pr_debug("session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d], balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x]\n",
+ ac->session, wma_cfg->format_tag, wma_cfg->sample_rate,
+ wma_cfg->ch_cfg, wma_cfg->avg_bytes_per_sec,
+ wma_cfg->block_align, wma_cfg->valid_bits_per_sample,
+ wma_cfg->ch_mask, wma_cfg->encode_opt);
+
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmtblk);
+ fmt.fmtag = wma_cfg->format_tag;
+ fmt.num_channels = wma_cfg->ch_cfg;
+ fmt.sample_rate = wma_cfg->sample_rate;
+ fmt.avg_bytes_per_sec = wma_cfg->avg_bytes_per_sec;
+ fmt.blk_align = wma_cfg->block_align;
+ fmt.bits_per_sample =
+ wma_cfg->valid_bits_per_sample;
+ fmt.channel_mask = wma_cfg->ch_mask;
+ fmt.enc_options = wma_cfg->encode_opt;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_media_format_block_wmapro(struct audio_client *ac,
+ void *cfg, int stream_id)
+{
+ struct asm_wmaprov10_fmt_blk_v2 fmt;
+ struct asm_wmapro_cfg *wmapro_cfg = (struct asm_wmapro_cfg *)cfg;
+ int rc = 0;
+
+ pr_debug("%s: session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d], balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x], adv_enc_opt[0x%4x], adv_enc_opt2[0x%8x]\n",
+ __func__,
+ ac->session, wmapro_cfg->format_tag, wmapro_cfg->sample_rate,
+ wmapro_cfg->ch_cfg, wmapro_cfg->avg_bytes_per_sec,
+ wmapro_cfg->block_align, wmapro_cfg->valid_bits_per_sample,
+ wmapro_cfg->ch_mask, wmapro_cfg->encode_opt,
+ wmapro_cfg->adv_encode_opt, wmapro_cfg->adv_encode_opt2);
+
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmtblk);
+
+ fmt.fmtag = wmapro_cfg->format_tag;
+ fmt.num_channels = wmapro_cfg->ch_cfg;
+ fmt.sample_rate = wmapro_cfg->sample_rate;
+ fmt.avg_bytes_per_sec =
+ wmapro_cfg->avg_bytes_per_sec;
+ fmt.blk_align = wmapro_cfg->block_align;
+ fmt.bits_per_sample = wmapro_cfg->valid_bits_per_sample;
+ fmt.channel_mask = wmapro_cfg->ch_mask;
+ fmt.enc_options = wmapro_cfg->encode_opt;
+ fmt.usAdvancedEncodeOpt = wmapro_cfg->adv_encode_opt;
+ fmt.advanced_enc_options2 = wmapro_cfg->adv_encode_opt2;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_media_format_block_amrwbplus(struct audio_client *ac,
+ struct asm_amrwbplus_cfg *cfg)
+{
+ struct asm_amrwbplus_fmt_blk_v2 fmt;
+ int rc = 0;
+
+ pr_debug("%s: session[%d]band-mode[%d]frame-fmt[%d]ch[%d]\n",
+ __func__,
+ ac->session,
+ cfg->amr_band_mode,
+ cfg->amr_frame_fmt,
+ cfg->num_channels);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmtblk);
+ fmt.amr_frame_fmt = cfg->amr_frame_fmt;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd media format update failed.. %d\n",
+ __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_stream_media_format_block_flac(struct audio_client *ac,
+ struct asm_flac_cfg *cfg, int stream_id)
+{
+ struct asm_flac_fmt_blk_v2 fmt;
+ int rc = 0;
+
+ pr_debug("%s :session[%d] rate[%d] ch[%d] size[%d] stream_id[%d]\n",
+ __func__, ac->session, cfg->sample_rate, cfg->ch_cfg,
+ cfg->sample_size, stream_id);
+
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmtblk);
+
+ fmt.is_stream_info_present = cfg->stream_info_present;
+ fmt.num_channels = cfg->ch_cfg;
+ fmt.min_blk_size = cfg->min_blk_size;
+ fmt.max_blk_size = cfg->max_blk_size;
+ fmt.sample_rate = cfg->sample_rate;
+ fmt.min_frame_size = cfg->min_frame_size;
+ fmt.max_frame_size = cfg->max_frame_size;
+ fmt.sample_size = cfg->sample_size;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s :Comamnd media format update failed %d\n",
+ __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_media_format_block_alac(struct audio_client *ac,
+ struct asm_alac_cfg *cfg, int stream_id)
+{
+ struct asm_alac_fmt_blk_v2 fmt;
+ int rc = 0;
+
+ pr_debug("%s :session[%d]rate[%d]ch[%d]\n", __func__,
+ ac->session, cfg->sample_rate, cfg->num_channels);
+
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmtblk);
+
+ fmt.frame_length = cfg->frame_length;
+ fmt.compatible_version = cfg->compatible_version;
+ fmt.bit_depth = cfg->bit_depth;
+ fmt.pb = cfg->pb;
+ fmt.mb = cfg->mb;
+ fmt.kb = cfg->kb;
+ fmt.num_channels = cfg->num_channels;
+ fmt.max_run = cfg->max_run;
+ fmt.max_frame_bytes = cfg->max_frame_bytes;
+ fmt.avg_bit_rate = cfg->avg_bit_rate;
+ fmt.sample_rate = cfg->sample_rate;
+ fmt.channel_layout_tag = cfg->channel_layout_tag;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s :Comamnd media format update failed %d\n",
+ __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+/*
+ * q6asm_media_format_block_g711 - sends g711 decoder configuration
+ * parameters
+ * @ac: Client session handle
+ * @cfg: Audio stream manager configuration parameters
+ * @stream_id: Stream id
+ */
+int q6asm_media_format_block_g711(struct audio_client *ac,
+ struct asm_g711_dec_cfg *cfg, int stream_id)
+{
+ struct asm_g711_dec_fmt_blk_v2 fmt;
+ int rc = 0;
+
+ if (!ac) {
+ pr_err("%s: audio client is null\n", __func__);
+ return -EINVAL;
+ }
+ if (!cfg) {
+ pr_err("%s: Invalid ASM config\n", __func__);
+ return -EINVAL;
+ }
+
+ if (stream_id <= 0) {
+ pr_err("%s: Invalid stream id\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s :session[%d]rate[%d]\n", __func__,
+ ac->session, cfg->sample_rate);
+
+ memset(&fmt, 0, sizeof(struct asm_g711_dec_fmt_blk_v2));
+
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmtblk);
+
+ fmt.sample_rate = cfg->sample_rate;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s :Command media format update failed %d\n",
+ __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_media_format_block_g711);
+
+int q6asm_stream_media_format_block_vorbis(struct audio_client *ac,
+ struct asm_vorbis_cfg *cfg, int stream_id)
+{
+ struct asm_vorbis_fmt_blk_v2 fmt;
+ int rc = 0;
+
+ pr_debug("%s :session[%d] bit_stream_fmt[%d] stream_id[%d]\n",
+ __func__, ac->session, cfg->bit_stream_fmt, stream_id);
+
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmtblk);
+
+ fmt.bit_stream_fmt = cfg->bit_stream_fmt;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s :Comamnd media format update failed %d\n",
+ __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_media_format_block_ape(struct audio_client *ac,
+ struct asm_ape_cfg *cfg, int stream_id)
+{
+ struct asm_ape_fmt_blk_v2 fmt;
+ int rc = 0;
+
+ pr_debug("%s :session[%d]rate[%d]ch[%d]\n", __func__,
+ ac->session, cfg->sample_rate, cfg->num_channels);
+
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmtblk);
+
+ fmt.compatible_version = cfg->compatible_version;
+ fmt.compression_level = cfg->compression_level;
+ fmt.format_flags = cfg->format_flags;
+ fmt.blocks_per_frame = cfg->blocks_per_frame;
+ fmt.final_frame_blocks = cfg->final_frame_blocks;
+ fmt.total_frames = cfg->total_frames;
+ fmt.bits_per_sample = cfg->bits_per_sample;
+ fmt.num_channels = cfg->num_channels;
+ fmt.sample_rate = cfg->sample_rate;
+ fmt.seek_table_present = cfg->seek_table_present;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s :Comamnd media format update failed %d\n",
+ __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+/*
+ * q6asm_media_format_block_dsd- Sends DSD Decoder
+ * configuration parameters
+ *
+ * @ac: Client session handle
+ * @cfg: DSD Media Format Configuration.
+ * @stream_id: stream id of stream to be associated with this session
+ *
+ * Return 0 on success or negative error code on failure
+ */
+int q6asm_media_format_block_dsd(struct audio_client *ac,
+ struct asm_dsd_cfg *cfg, int stream_id)
+{
+ struct asm_dsd_fmt_blk_v2 fmt;
+ int rc;
+
+ pr_debug("%s: session[%d] data_rate[%d] ch[%d]\n", __func__,
+ ac->session, cfg->dsd_data_rate, cfg->num_channels);
+
+ memset(&fmt, 0, sizeof(fmt));
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmtblk);
+
+ fmt.num_version = cfg->num_version;
+ fmt.is_bitwise_big_endian = cfg->is_bitwise_big_endian;
+ fmt.dsd_channel_block_size = cfg->dsd_channel_block_size;
+ fmt.num_channels = cfg->num_channels;
+ fmt.dsd_data_rate = cfg->dsd_data_rate;
+ atomic_set(&ac->cmd_state, -1);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Command DSD media format update failed, err: %d\n",
+ __func__, rc);
+ goto done;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for DSD FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto done;
+ }
+ return 0;
+done:
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_media_format_block_dsd);
+
+int q6asm_stream_media_format_block_aptx_dec(struct audio_client *ac,
+ uint32_t srate, int stream_id)
+{
+ struct asm_aptx_dec_fmt_blk_v2 aptx_fmt;
+ int rc = 0;
+
+ if (!ac->session) {
+ pr_err("%s: ac session invalid\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ pr_debug("%s :session[%d] rate[%d] stream_id[%d]\n",
+ __func__, ac->session, srate, stream_id);
+
+ q6asm_stream_add_hdr(ac, &aptx_fmt.hdr, sizeof(aptx_fmt), TRUE,
+ stream_id);
+ atomic_set(&ac->cmd_state, -1);
+
+ aptx_fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ aptx_fmt.fmtblk.fmt_blk_size = sizeof(aptx_fmt) - sizeof(aptx_fmt.hdr) -
+ sizeof(aptx_fmt.fmtblk);
+
+ aptx_fmt.sample_rate = srate;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &aptx_fmt);
+ if (rc < 0) {
+ pr_err("%s :Comamnd media format update failed %d\n",
+ __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+static int __q6asm_ds1_set_endp_params(struct audio_client *ac, int param_id,
+ int param_value, int stream_id)
+{
+ struct asm_dec_ddp_endp_param_v2 ddp_cfg;
+ int rc = 0;
+
+ pr_debug("%s: session[%d] stream[%d],param_id[%d]param_value[%d]",
+ __func__, ac->session, stream_id, param_id, param_value);
+
+ q6asm_stream_add_hdr(ac, &ddp_cfg.hdr, sizeof(ddp_cfg), TRUE,
+ stream_id);
+ atomic_set(&ac->cmd_state, -1);
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ q6asm_update_token(&ddp_cfg.hdr.token,
+ ac->session,
+ stream_id,
+ 0, /* Buffer index is NA */
+ 0, /* Direction flag is NA */
+ WAIT_CMD);
+ ddp_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ ddp_cfg.encdec.param_id = param_id;
+ ddp_cfg.encdec.param_size = sizeof(struct asm_dec_ddp_endp_param_v2) -
+ (sizeof(struct apr_hdr) +
+ sizeof(struct asm_stream_cmd_set_encdec_param));
+ ddp_cfg.endp_param_value = param_value;
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &ddp_cfg);
+ if (rc < 0) {
+ pr_err("%s: Command opcode[0x%x] failed %d\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout opcode[0x%x]\n", __func__,
+ ddp_cfg.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_ds1_set_endp_params(struct audio_client *ac,
+ int param_id, int param_value)
+{
+ return __q6asm_ds1_set_endp_params(ac, param_id, param_value,
+ ac->stream_id);
+}
+
+int q6asm_ds1_set_stream_endp_params(struct audio_client *ac,
+ int param_id, int param_value,
+ int stream_id)
+{
+ return __q6asm_ds1_set_endp_params(ac, param_id, param_value,
+ stream_id);
+}
+
+int q6asm_memory_map(struct audio_client *ac, phys_addr_t buf_add, int dir,
+ uint32_t bufsz, uint32_t bufcnt)
+{
+ struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+ struct avs_shared_map_region_payload *mregions = NULL;
+ struct audio_port_data *port = NULL;
+ void *mmap_region_cmd = NULL;
+ void *payload = NULL;
+ struct asm_buffer_node *buffer_node = NULL;
+ int rc = 0;
+ int cmd_size = 0;
+
+ if (!ac) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->mmap_apr == NULL) {
+ pr_err("%s: mmap APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ buffer_node = kmalloc(sizeof(struct asm_buffer_node), GFP_KERNEL);
+ if (!buffer_node) {
+ pr_err("%s: no memory\n", __func__);
+ return -ENOMEM;
+ }
+ cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions)
+ + sizeof(struct avs_shared_map_region_payload) * bufcnt;
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (mmap_region_cmd == NULL) {
+ pr_err("%s: Mem alloc failed\n", __func__);
+ rc = -EINVAL;
+ kfree(buffer_node);
+ return rc;
+ }
+ mmap_regions = (struct avs_cmd_shared_mem_map_regions *)
+ mmap_region_cmd;
+ q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, dir);
+ atomic_set(&ac->mem_state, -1);
+ mmap_regions->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS;
+ mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+ mmap_regions->num_regions = bufcnt & 0x00ff;
+ mmap_regions->property_flag = 0x00;
+ payload = ((u8 *) mmap_region_cmd +
+ sizeof(struct avs_cmd_shared_mem_map_regions));
+ mregions = (struct avs_shared_map_region_payload *)payload;
+
+ ac->port[dir].tmp_hdl = 0;
+ port = &ac->port[dir];
+ pr_debug("%s: buf_add 0x%pK, bufsz: %d\n", __func__,
+ &buf_add, bufsz);
+ mregions->shm_addr_lsw = lower_32_bits(buf_add);
+ mregions->shm_addr_msw = msm_audio_populate_upper_32_bits(buf_add);
+ mregions->mem_size_bytes = bufsz;
+ ++mregions;
+
+ rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) mmap_region_cmd);
+ if (rc < 0) {
+ pr_err("%s: mmap op[0x%x]rc[%d]\n", __func__,
+ mmap_regions->hdr.opcode, rc);
+ rc = -EINVAL;
+ kfree(buffer_node);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->mem_wait,
+ (atomic_read(&ac->mem_state) >= 0 &&
+ ac->port[dir].tmp_hdl), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for memory_map\n", __func__);
+ rc = -ETIMEDOUT;
+ kfree(buffer_node);
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->mem_state) > 0) {
+ pr_err("%s: DSP returned error[%s] for memory_map\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->mem_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->mem_state));
+ kfree(buffer_node);
+ goto fail_cmd;
+ }
+ buffer_node->buf_phys_addr = buf_add;
+ buffer_node->mmap_hdl = ac->port[dir].tmp_hdl;
+ list_add_tail(&buffer_node->list, &ac->port[dir].mem_map_handle);
+ ac->port[dir].tmp_hdl = 0;
+ rc = 0;
+
+fail_cmd:
+ kfree(mmap_region_cmd);
+ return rc;
+}
+
+int q6asm_memory_unmap(struct audio_client *ac, phys_addr_t buf_add, int dir)
+{
+ struct avs_cmd_shared_mem_unmap_regions mem_unmap;
+ struct asm_buffer_node *buf_node = NULL;
+ struct list_head *ptr, *next;
+
+ int rc = 0;
+
+ if (!ac) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (this_mmap.apr == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ q6asm_add_mmaphdr(ac, &mem_unmap.hdr,
+ sizeof(struct avs_cmd_shared_mem_unmap_regions),
+ dir);
+ atomic_set(&ac->mem_state, -1);
+ mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
+ mem_unmap.mem_map_handle = 0;
+ list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_phys_addr == buf_add) {
+ pr_debug("%s: Found the element\n", __func__);
+ mem_unmap.mem_map_handle = buf_node->mmap_hdl;
+ break;
+ }
+ }
+ pr_debug("%s: mem_unmap-mem_map_handle: 0x%x\n",
+ __func__, mem_unmap.mem_map_handle);
+
+ if (mem_unmap.mem_map_handle == 0) {
+ pr_err("%s: Do not send null mem handle to DSP\n", __func__);
+ rc = 0;
+ goto fail_cmd;
+ }
+ rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) &mem_unmap);
+ if (rc < 0) {
+ pr_err("%s: mem_unmap op[0x%x]rc[%d]\n", __func__,
+ mem_unmap.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->mem_wait,
+ (atomic_read(&ac->mem_state) >= 0), 5 * HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for memory_unmap of handle 0x%x\n",
+ __func__, mem_unmap.mem_map_handle);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ } else if (atomic_read(&ac->mem_state) > 0) {
+ pr_err("%s DSP returned error [%s] map handle 0x%x\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->mem_state)),
+ mem_unmap.mem_map_handle);
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->mem_state));
+ goto fail_cmd;
+ } else if (atomic_read(&ac->unmap_cb_success) == 0) {
+ pr_err("%s: Error in mem unmap callback of handle 0x%x\n",
+ __func__, mem_unmap.mem_map_handle);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = 0;
+fail_cmd:
+ list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_phys_addr == buf_add) {
+ list_del(&buf_node->list);
+ kfree(buf_node);
+ break;
+ }
+ }
+ return rc;
+}
+
+
+static int q6asm_memory_map_regions(struct audio_client *ac, int dir,
+ uint32_t bufsz, uint32_t bufcnt,
+ bool is_contiguous)
+{
+ struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+ struct avs_shared_map_region_payload *mregions = NULL;
+ struct audio_port_data *port = NULL;
+ struct audio_buffer *ab = NULL;
+ void *mmap_region_cmd = NULL;
+ void *payload = NULL;
+ struct asm_buffer_node *buffer_node = NULL;
+ int rc = 0;
+ int i = 0;
+ uint32_t cmd_size = 0;
+ uint32_t bufcnt_t;
+ uint32_t bufsz_t;
+
+ if (!ac) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->mmap_apr == NULL) {
+ pr_err("%s: mmap APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ bufcnt_t = (is_contiguous) ? 1 : bufcnt;
+ bufsz_t = (is_contiguous) ? (bufsz * bufcnt) : bufsz;
+
+ if (is_contiguous) {
+ /* The size to memory map should be multiple of 4K bytes */
+ bufsz_t = PAGE_ALIGN(bufsz_t);
+ }
+
+ if (bufcnt_t > (UINT_MAX
+ - sizeof(struct avs_cmd_shared_mem_map_regions))
+ / sizeof(struct avs_shared_map_region_payload)) {
+ pr_err("%s: Unsigned Integer Overflow. bufcnt_t = %u\n",
+ __func__, bufcnt_t);
+ return -EINVAL;
+ }
+
+ cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions)
+ + (sizeof(struct avs_shared_map_region_payload)
+ * bufcnt_t);
+
+
+ if (bufcnt > (UINT_MAX / sizeof(struct asm_buffer_node))) {
+ pr_err("%s: Unsigned Integer Overflow. bufcnt = %u\n",
+ __func__, bufcnt);
+ return -EINVAL;
+ }
+
+ buffer_node = kzalloc(sizeof(struct asm_buffer_node) * bufcnt,
+ GFP_KERNEL);
+ if (!buffer_node) {
+ pr_err("%s: Mem alloc failed for asm_buffer_node\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (mmap_region_cmd == NULL) {
+ pr_err("%s: Mem alloc failed\n", __func__);
+ rc = -EINVAL;
+ kfree(buffer_node);
+ return rc;
+ }
+ mmap_regions = (struct avs_cmd_shared_mem_map_regions *)
+ mmap_region_cmd;
+ q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, dir);
+ atomic_set(&ac->mem_state, -1);
+ pr_debug("%s: mmap_region=0x%pK token=0x%x\n", __func__,
+ mmap_regions, ((ac->session << 8) | dir));
+
+ mmap_regions->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS;
+ mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+ mmap_regions->num_regions = bufcnt_t; /*bufcnt & 0x00ff; */
+ mmap_regions->property_flag = 0x00;
+ pr_debug("%s: map_regions->nregions = %d\n", __func__,
+ mmap_regions->num_regions);
+ payload = ((u8 *) mmap_region_cmd +
+ sizeof(struct avs_cmd_shared_mem_map_regions));
+ mregions = (struct avs_shared_map_region_payload *)payload;
+
+ ac->port[dir].tmp_hdl = 0;
+ port = &ac->port[dir];
+ for (i = 0; i < bufcnt_t; i++) {
+ ab = &port->buf[i];
+ mregions->shm_addr_lsw = lower_32_bits(ab->phys);
+ mregions->shm_addr_msw =
+ msm_audio_populate_upper_32_bits(ab->phys);
+ mregions->mem_size_bytes = bufsz_t;
+ ++mregions;
+ }
+
+ rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) mmap_region_cmd);
+ if (rc < 0) {
+ pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
+ mmap_regions->hdr.opcode, rc);
+ rc = -EINVAL;
+ kfree(buffer_node);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->mem_wait,
+ (atomic_read(&ac->mem_state) >= 0)
+ , 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for memory_map\n", __func__);
+ rc = -ETIMEDOUT;
+ kfree(buffer_node);
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->mem_state) > 0) {
+ pr_err("%s DSP returned error for memory_map [%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->mem_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->mem_state));
+ kfree(buffer_node);
+ goto fail_cmd;
+ }
+ mutex_lock(&ac->cmd_lock);
+
+ for (i = 0; i < bufcnt; i++) {
+ ab = &port->buf[i];
+ buffer_node[i].buf_phys_addr = ab->phys;
+ buffer_node[i].mmap_hdl = ac->port[dir].tmp_hdl;
+ list_add_tail(&buffer_node[i].list,
+ &ac->port[dir].mem_map_handle);
+ pr_debug("%s: i=%d, bufadd[i] = 0x%pK, maphdl[i] = 0x%x\n",
+ __func__, i, &buffer_node[i].buf_phys_addr,
+ buffer_node[i].mmap_hdl);
+ }
+ ac->port[dir].tmp_hdl = 0;
+ mutex_unlock(&ac->cmd_lock);
+ rc = 0;
+fail_cmd:
+ kfree(mmap_region_cmd);
+ return rc;
+}
+
+static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir)
+{
+ struct avs_cmd_shared_mem_unmap_regions mem_unmap;
+ struct audio_port_data *port = NULL;
+ struct asm_buffer_node *buf_node = NULL;
+ struct list_head *ptr, *next;
+ phys_addr_t buf_add;
+ int rc = 0;
+ int cmd_size = 0;
+
+ if (!ac) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->mmap_apr == NULL) {
+ pr_err("%s: mmap APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions);
+ q6asm_add_mmaphdr(ac, &mem_unmap.hdr, cmd_size, dir);
+ atomic_set(&ac->mem_state, -1);
+ port = &ac->port[dir];
+ buf_add = port->buf->phys;
+ mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
+ mem_unmap.mem_map_handle = 0;
+ list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_phys_addr == buf_add) {
+ pr_debug("%s: Found the element\n", __func__);
+ mem_unmap.mem_map_handle = buf_node->mmap_hdl;
+ break;
+ }
+ }
+
+ pr_debug("%s: mem_unmap-mem_map_handle: 0x%x\n",
+ __func__, mem_unmap.mem_map_handle);
+
+ if (mem_unmap.mem_map_handle == 0) {
+ pr_err("%s: Do not send null mem handle to DSP\n", __func__);
+ rc = 0;
+ goto fail_cmd;
+ }
+ rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) &mem_unmap);
+ if (rc < 0) {
+ pr_err("mmap_regions op[0x%x]rc[%d]\n",
+ mem_unmap.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->mem_wait,
+ (atomic_read(&ac->mem_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for memory_unmap of handle 0x%x\n",
+ __func__, mem_unmap.mem_map_handle);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ } else if (atomic_read(&ac->mem_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->mem_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->mem_state));
+ goto fail_cmd;
+ } else if (atomic_read(&ac->unmap_cb_success) == 0) {
+ pr_err("%s: Error in mem unmap callback of handle 0x%x\n",
+ __func__, mem_unmap.mem_map_handle);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+
+fail_cmd:
+ list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_phys_addr == buf_add) {
+ list_del(&buf_node->list);
+ kfree(buf_node);
+ break;
+ }
+ }
+ return rc;
+}
+
+int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain)
+{
+ struct asm_volume_ctrl_multichannel_gain multi_ch_gain;
+ struct param_hdr_v3 param_info = {0};
+ int rc = 0;
+
+ memset(&multi_ch_gain, 0, sizeof(multi_ch_gain));
+
+ param_info.module_id = ASM_MODULE_ID_VOL_CTRL;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = ASM_PARAM_ID_MULTICHANNEL_GAIN;
+ param_info.param_size = sizeof(multi_ch_gain);
+
+ multi_ch_gain.gain_data[0].channeltype = PCM_CHANNEL_FL;
+ multi_ch_gain.gain_data[0].gain = left_gain << 15;
+ multi_ch_gain.gain_data[1].channeltype = PCM_CHANNEL_FR;
+ multi_ch_gain.gain_data[1].gain = right_gain << 15;
+ multi_ch_gain.num_channels = 2;
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info,
+ (u8 *) &multi_ch_gain);
+ if (rc < 0)
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, param_info.param_id, rc);
+
+ return rc;
+}
+
+/*
+ * q6asm_set_multich_gain: set multiple channel gains on an ASM session
+ * @ac: audio client handle
+ * @channels: number of channels caller intends to set gains
+ * @gains: list of gains of audio channels
+ * @ch_map: list of channel mapping. Only valid if use_default is false
+ * @use_default: flag to indicate whether to use default mapping
+ */
+int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels,
+ uint32_t *gains, uint8_t *ch_map, bool use_default)
+{
+ struct asm_volume_ctrl_multichannel_gain multich_gain;
+ struct param_hdr_v3 param_info = {0};
+ int rc = 0;
+ int i;
+ u8 default_chmap[VOLUME_CONTROL_MAX_CHANNELS];
+
+ if (ac == NULL) {
+ pr_err("%s: Audio client is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (gains == NULL) {
+ dev_err(ac->dev, "%s: gain_list is NULL\n", __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+ if (channels > VOLUME_CONTROL_MAX_CHANNELS) {
+ dev_err(ac->dev, "%s: Invalid channel count %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto done;
+ }
+ if (!use_default && ch_map == NULL) {
+ dev_err(ac->dev, "%s: NULL channel map\n", __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ memset(&multich_gain, 0, sizeof(multich_gain));
+ param_info.module_id = ASM_MODULE_ID_VOL_CTRL;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = ASM_PARAM_ID_MULTICHANNEL_GAIN;
+ param_info.param_size = sizeof(multich_gain);
+
+ if (use_default) {
+ rc = q6asm_map_channels(default_chmap, channels, false);
+ if (rc < 0)
+ goto done;
+ for (i = 0; i < channels; i++) {
+ multich_gain.gain_data[i].channeltype =
+ default_chmap[i];
+ multich_gain.gain_data[i].gain = gains[i] << 15;
+ }
+ } else {
+ for (i = 0; i < channels; i++) {
+ multich_gain.gain_data[i].channeltype = ch_map[i];
+ multich_gain.gain_data[i].gain = gains[i] << 15;
+ }
+ }
+ multich_gain.num_channels = channels;
+
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info,
+ (u8 *) &multich_gain);
+ if (rc)
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, param_info.param_id, rc);
+done:
+ return rc;
+}
+
+int q6asm_set_mute(struct audio_client *ac, int muteflag)
+{
+ struct asm_volume_ctrl_mute_config mute = {0};
+ struct param_hdr_v3 param_info = {0};
+ int rc = 0;
+
+ param_info.module_id = ASM_MODULE_ID_VOL_CTRL;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG;
+ param_info.param_size = sizeof(mute);
+ mute.mute_flag = muteflag;
+
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info, (u8 *) &mute);
+ if (rc)
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, param_info.param_id, rc);
+ return rc;
+}
+
+static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance)
+{
+ struct asm_volume_ctrl_master_gain vol = {0};
+ struct param_hdr_v3 param_info = {0};
+ int rc = 0;
+
+ rc = q6asm_set_soft_volume_module_instance_ids(instance, &param_info);
+ if (rc) {
+ pr_err("%s: Failed to pack soft volume module and instance IDs, error %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ param_info.param_id = ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN;
+ param_info.param_size = sizeof(vol);
+ vol.master_gain = volume;
+
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info, (u8 *) &vol);
+ if (rc)
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, param_info.param_id, rc);
+
+ return rc;
+}
+
+int q6asm_set_volume(struct audio_client *ac, int volume)
+{
+ return __q6asm_set_volume(ac, volume, SOFT_VOLUME_INSTANCE_1);
+}
+
+int q6asm_set_volume_v2(struct audio_client *ac, int volume, int instance)
+{
+ return __q6asm_set_volume(ac, volume, instance);
+}
+
+int q6asm_set_aptx_dec_bt_addr(struct audio_client *ac,
+ struct aptx_dec_bt_addr_cfg *cfg)
+{
+ struct aptx_dec_bt_dev_addr paylod;
+ int sz = 0;
+ int rc = 0;
+
+ pr_debug("%s: BT addr nap %d, uap %d, lap %d\n", __func__, cfg->nap,
+ cfg->uap, cfg->lap);
+
+ if (ac == NULL) {
+ pr_err("%s: AC handle NULL\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ sz = sizeof(struct aptx_dec_bt_dev_addr);
+ q6asm_add_hdr_async(ac, &paylod.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ paylod.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ paylod.encdec.param_id = APTX_DECODER_BT_ADDRESS;
+ paylod.encdec.param_size = sz - sizeof(paylod.hdr)
+ - sizeof(paylod.encdec);
+ paylod.bt_addr_cfg.lap = cfg->lap;
+ paylod.bt_addr_cfg.uap = cfg->uap;
+ paylod.bt_addr_cfg.nap = cfg->nap;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &paylod);
+ if (rc < 0) {
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, paylod.encdec.param_id, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
+ paylod.encdec.param_id);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)),
+ paylod.encdec.param_id);
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ pr_debug("%s: set BT addr is success\n", __func__);
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_audio_map_shm_fd(struct audio_client *ac, struct ion_client **client,
+ struct ion_handle **handle, int fd)
+{
+ ion_phys_addr_t paddr = 0;
+ size_t pa_len = 0;
+ int ret;
+ int sz = 0;
+ struct avs_rtic_shared_mem_addr shm;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = msm_audio_ion_phys_assign("audio_shm_mem_client", client,
+ handle, fd,
+ &paddr, &pa_len, HLOS_TO_ADSP);
+ if (ret) {
+ pr_err("%s: shm ION phys failed, rc = %d\n", __func__, ret);
+ goto fail_cmd;
+ }
+ /* get payload length */
+ sz = sizeof(struct avs_rtic_shared_mem_addr);
+ q6asm_add_hdr_async(ac, &shm.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ shm.shm_buf_addr_lsw = lower_32_bits(paddr);
+ shm.shm_buf_addr_msw = msm_audio_populate_upper_32_bits(paddr);
+ shm.buf_size = pa_len;
+ shm.shm_buf_num_regions = 1;
+ shm.shm_buf_mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+ shm.shm_buf_flag = 0x00;
+ shm.encdec.param_id = AVS_PARAM_ID_RTIC_SHARED_MEMORY_ADDR;
+ shm.encdec.param_size = sizeof(struct avs_rtic_shared_mem_addr) -
+ sizeof(struct apr_hdr) -
+ sizeof(struct asm_stream_cmd_set_encdec_param_v2);
+ shm.encdec.service_id = OUT;
+ shm.encdec.reserved = 0;
+ shm.map_region.shm_addr_lsw = shm.shm_buf_addr_lsw;
+ shm.map_region.shm_addr_msw = shm.shm_buf_addr_msw;
+ shm.map_region.mem_size_bytes = pa_len;
+ shm.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM_V2;
+ ret = apr_send_pkt(ac->apr, (uint32_t *) &shm);
+ if (ret < 0) {
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, shm.encdec.param_id, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 1*HZ);
+ if (!ret) {
+ pr_err("%s: timeout, shm.encdec paramid[0x%x]\n", __func__,
+ shm.encdec.param_id);
+ ret = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s] shm.encdec paramid[0x%x]\n",
+ __func__,
+ adsp_err_get_err_str(atomic_read(&ac->cmd_state)),
+ shm.encdec.param_id);
+ ret = adsp_err_get_lnx_err_code(atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ ret = 0;
+fail_cmd:
+ return ret;
+}
+
+int q6asm_send_rtic_event_ack(struct audio_client *ac,
+ void *param, uint32_t params_length)
+{
+ char *asm_params = NULL;
+ int sz, rc;
+ struct avs_param_rtic_event_ack ack;
+
+ if (!param || !ac) {
+ pr_err("%s: %s is NULL\n", __func__,
+ (!param) ? "param" : "ac");
+ rc = -EINVAL;
+ goto done;
+ }
+
+ sz = sizeof(struct avs_param_rtic_event_ack) + params_length;
+ asm_params = kzalloc(sz, GFP_KERNEL);
+ if (!asm_params) {
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ q6asm_stream_add_hdr_async(ac, &ack.hdr,
+ sizeof(struct avs_param_rtic_event_ack) +
+ params_length, TRUE, ac->stream_id);
+ ack.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM_V2;
+ ack.encdec.param_id = AVS_PARAM_ID_RTIC_EVENT_ACK;
+ ack.encdec.param_size = params_length;
+ ack.encdec.reserved = 0;
+ ack.encdec.service_id = OUT;
+ memcpy(asm_params, &ack, sizeof(struct avs_param_rtic_event_ack));
+ memcpy(asm_params + sizeof(struct avs_param_rtic_event_ack),
+ param, params_length);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params);
+ if (rc < 0)
+ pr_err("%s: apr pkt failed for rtic event ack\n", __func__);
+ else
+ rc = 0;
+
+ kfree(asm_params);
+done:
+ return rc;
+}
+
+int q6asm_set_softpause(struct audio_client *ac,
+ struct asm_softpause_params *pause_param)
+{
+ struct asm_soft_pause_params softpause = {0};
+ struct param_hdr_v3 param_info = {0};
+ int rc = 0;
+
+ param_info.module_id = ASM_MODULE_ID_VOL_CTRL;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = ASM_PARAM_ID_SOFT_PAUSE_PARAMETERS;
+ param_info.param_size = sizeof(softpause);
+
+ softpause.enable_flag = pause_param->enable;
+ softpause.period = pause_param->period;
+ softpause.step = pause_param->step;
+ softpause.ramping_curve = pause_param->rampingcurve;
+
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info,
+ (u8 *) &softpause);
+ if (rc)
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, param_info.param_id, rc);
+
+ return rc;
+}
+
+static int __q6asm_set_softvolume(struct audio_client *ac,
+ struct asm_softvolume_params *softvol_param,
+ int instance)
+{
+ struct asm_soft_step_volume_params softvol = {0};
+ struct param_hdr_v3 param_info = {0};
+ int rc = 0;
+
+ rc = q6asm_set_soft_volume_module_instance_ids(instance, &param_info);
+ if (rc) {
+ pr_err("%s: Failed to pack soft volume module and instance IDs, error %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ param_info.param_id = ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS;
+ param_info.param_size = sizeof(softvol);
+
+ softvol.period = softvol_param->period;
+ softvol.step = softvol_param->step;
+ softvol.ramping_curve = softvol_param->rampingcurve;
+
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info,
+ (u8 *) &softvol);
+ if (rc)
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, param_info.param_id, rc);
+
+ return rc;
+}
+
+int q6asm_set_softvolume(struct audio_client *ac,
+ struct asm_softvolume_params *softvol_param)
+{
+ return __q6asm_set_softvolume(ac, softvol_param,
+ SOFT_VOLUME_INSTANCE_1);
+}
+
+int q6asm_set_softvolume_v2(struct audio_client *ac,
+ struct asm_softvolume_params *softvol_param,
+ int instance)
+{
+ return __q6asm_set_softvolume(ac, softvol_param, instance);
+}
+
+int q6asm_set_vol_ctrl_gain_pair(struct audio_client *ac,
+ struct asm_stream_pan_ctrl_params *pan_param)
+{
+ struct audproc_volume_ctrl_multichannel_gain gain_data;
+ struct param_hdr_v3 param_hdr = {0};
+ int num_out_ch = 0;
+ int i = 0;
+ int rc = 0;
+
+ if (pan_param == NULL) {
+ pr_err("%s: Pan parameter is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(&gain_data, 0, sizeof(gain_data));
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_VOL_CTRL;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_MULTICHANNEL_GAIN;
+ param_hdr.param_size = sizeof(gain_data);
+
+ num_out_ch = pan_param->num_output_channels;
+ if (num_out_ch > ASM_MAX_CHANNELS) {
+ pr_err("%s: Invalid number of output channels %d\n", __func__,
+ num_out_ch);
+ return -EINVAL;
+ }
+ gain_data.num_channels = num_out_ch;
+
+ for (i = 0; i < num_out_ch; i++) {
+ gain_data.gain_data[i].channel_type =
+ pan_param->output_channel_map[i];
+ gain_data.gain_data[i].gain = pan_param->gain[i];
+ }
+
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_hdr,
+ (uint8_t *) &gain_data);
+ if (rc < 0)
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, param_hdr.param_id, rc);
+ return rc;
+}
+
+int q6asm_set_mfc_panning_params(struct audio_client *ac,
+ struct asm_stream_pan_ctrl_params *pan_param)
+{
+
+ struct audproc_mfc_param_media_fmt mfc_cfg = {0};
+ struct audproc_chmixer_param_coeff *chmixer_cfg;
+ struct param_hdr_v3 param_hdr = {0};
+ u16 *dst_gain_ptr = NULL;
+ int num_out_ch;
+ int num_in_ch;
+ int chmixer_cfg_size = 0;
+ int packed_data_size = 0;
+ int out_ch_map_size;
+ int in_ch_map_size;
+ int gain_size;
+ int i = 0;
+ int rc = 0;
+
+ if (!pan_param)
+ return -EINVAL;
+
+ num_out_ch = pan_param->num_output_channels;
+ num_in_ch = pan_param->num_input_channels;
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_MFC;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT;
+ param_hdr.param_size = sizeof(mfc_cfg);
+
+ mfc_cfg.sampling_rate = 0;
+ mfc_cfg.bits_per_sample = 0;
+ mfc_cfg.num_channels = num_out_ch;
+ for (i = 0; i < num_out_ch; i++)
+ mfc_cfg.channel_type[i] = pan_param->output_channel_map[i];
+
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_hdr,
+ (uint8_t *) &mfc_cfg);
+ if (rc < 0) {
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, param_hdr.param_id, rc);
+ return rc;
+ }
+
+ out_ch_map_size = num_out_ch * sizeof(uint16_t);
+ in_ch_map_size = num_in_ch * sizeof(uint16_t);
+ gain_size = num_out_ch * num_in_ch * sizeof(uint16_t);
+
+ chmixer_cfg_size = sizeof(struct audproc_chmixer_param_coeff) +
+ out_ch_map_size + in_ch_map_size + gain_size;
+ chmixer_cfg = kzalloc(chmixer_cfg_size, GFP_KERNEL);
+ if (!chmixer_cfg)
+ return -ENOMEM;
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_MFC;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_CHMIXER_PARAM_ID_COEFF;
+ param_hdr.param_size = chmixer_cfg_size;
+
+ chmixer_cfg->index = 0;
+ chmixer_cfg->num_output_channels = num_out_ch;
+ chmixer_cfg->num_input_channels = num_in_ch;
+
+ /* Repack channel data as max channels may not be used */
+ memcpy(chmixer_cfg->payload, pan_param->output_channel_map,
+ out_ch_map_size);
+ packed_data_size += out_ch_map_size;
+
+ memcpy(chmixer_cfg->payload + packed_data_size,
+ pan_param->input_channel_map, in_ch_map_size);
+ packed_data_size += in_ch_map_size;
+
+ dst_gain_ptr = (uint16_t *) chmixer_cfg->payload + packed_data_size;
+ for (i = 0; i < num_out_ch * num_in_ch; i++)
+ dst_gain_ptr[i] = (uint16_t) pan_param->gain[i];
+
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_hdr,
+ (uint8_t *) chmixer_cfg);
+ if (rc < 0) {
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, param_hdr.param_id, rc);
+ rc = -EINVAL;
+ }
+ kfree(chmixer_cfg);
+
+ return rc;
+}
+
+int q6asm_equalizer(struct audio_client *ac, void *eq_p)
+{
+ struct asm_eq_params eq = {0};
+ struct msm_audio_eq_stream_config *eq_params = NULL;
+ struct param_hdr_v3 param_info = {0};
+ int i = 0;
+ int rc = 0;
+
+ if (ac == NULL) {
+ pr_err("%s: Audio client is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (eq_p == NULL) {
+ pr_err("%s: [%d]: Invalid Eq param\n", __func__, ac->session);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ eq_params = (struct msm_audio_eq_stream_config *) eq_p;
+ param_info.module_id = ASM_MODULE_ID_EQUALIZER;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = ASM_PARAM_ID_EQUALIZER_PARAMETERS;
+ param_info.param_size = sizeof(eq);
+ eq.enable_flag = eq_params->enable;
+ eq.num_bands = eq_params->num_bands;
+
+ pr_debug("%s: enable:%d numbands:%d\n", __func__, eq_params->enable,
+ eq_params->num_bands);
+ for (i = 0; i < eq_params->num_bands; i++) {
+ eq.eq_bands[i].band_idx =
+ eq_params->eq_bands[i].band_idx;
+ eq.eq_bands[i].filterype =
+ eq_params->eq_bands[i].filter_type;
+ eq.eq_bands[i].center_freq_hz =
+ eq_params->eq_bands[i].center_freq_hz;
+ eq.eq_bands[i].filter_gain =
+ eq_params->eq_bands[i].filter_gain;
+ eq.eq_bands[i].q_factor =
+ eq_params->eq_bands[i].q_factor;
+ pr_debug("%s: filter_type:%u bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].filter_type, i);
+ pr_debug("%s: center_freq_hz:%u bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].center_freq_hz, i);
+ pr_debug("%s: filter_gain:%d bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].filter_gain, i);
+ pr_debug("%s: q_factor:%d bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].q_factor, i);
+ }
+ rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info, (u8 *) &eq);
+ if (rc)
+ pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+ __func__, param_info.param_id, rc);
+
+fail_cmd:
+ return rc;
+}
+
+static int __q6asm_read(struct audio_client *ac, bool is_custom_len_reqd,
+ int len)
+{
+ struct asm_data_cmd_read_v2 read;
+ struct asm_buffer_node *buf_node = NULL;
+ struct list_head *ptr, *next;
+ struct audio_buffer *ab;
+ int dsp_buf;
+ struct audio_port_data *port;
+ int rc;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[OUT];
+
+ q6asm_add_hdr(ac, &read.hdr, sizeof(read), FALSE);
+
+ mutex_lock(&port->lock);
+
+ dsp_buf = port->dsp_buf;
+ if (port->buf == NULL) {
+ pr_err("%s: buf is NULL\n", __func__);
+ mutex_unlock(&port->lock);
+ return -EINVAL;
+ }
+ ab = &port->buf[dsp_buf];
+
+ dev_vdbg(ac->dev, "%s: session[%d]dsp-buf[%d][%pK]cpu_buf[%d][%pK]\n",
+ __func__,
+ ac->session,
+ dsp_buf,
+ port->buf[dsp_buf].data,
+ port->cpu_buf,
+ &port->buf[port->cpu_buf].phys);
+
+ read.hdr.opcode = ASM_DATA_CMD_READ_V2;
+ read.buf_addr_lsw = lower_32_bits(ab->phys);
+ read.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys);
+
+ list_for_each_safe(ptr, next, &ac->port[OUT].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_phys_addr == ab->phys)
+ read.mem_map_handle = buf_node->mmap_hdl;
+ }
+ dev_vdbg(ac->dev, "memory_map handle in q6asm_read: [%0x]:",
+ read.mem_map_handle);
+ read.buf_size = is_custom_len_reqd ? len : ab->size;
+ read.seq_id = port->dsp_buf;
+ q6asm_update_token(&read.hdr.token,
+ 0, /* Session ID is NA */
+ 0, /* Stream ID is NA */
+ port->dsp_buf,
+ 0, /* Direction flag is NA */
+ WAIT_CMD);
+ port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf,
+ port->max_buf_cnt);
+ mutex_unlock(&port->lock);
+ dev_vdbg(ac->dev, "%s: buf add[%pK] token[0x%x] uid[%d]\n",
+ __func__, &ab->phys, read.hdr.token,
+ read.seq_id);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+ if (rc < 0) {
+ pr_err("%s: read op[0x%x]rc[%d]\n",
+ __func__, read.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_read(struct audio_client *ac)
+{
+ return __q6asm_read(ac, false/*is_custom_len_reqd*/, 0);
+}
+int q6asm_read_v2(struct audio_client *ac, uint32_t len)
+{
+ return __q6asm_read(ac, true /*is_custom_len_reqd*/, len);
+}
+
+int q6asm_read_nolock(struct audio_client *ac)
+{
+ struct asm_data_cmd_read_v2 read;
+ struct asm_buffer_node *buf_node = NULL;
+ struct list_head *ptr, *next;
+ struct audio_buffer *ab;
+ int dsp_buf;
+ struct audio_port_data *port;
+ int rc;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[OUT];
+
+ q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE);
+
+
+ dsp_buf = port->dsp_buf;
+ ab = &port->buf[dsp_buf];
+
+ dev_vdbg(ac->dev, "%s: session[%d]dsp-buf[%d][%pK]cpu_buf[%d][%pK]\n",
+ __func__,
+ ac->session,
+ dsp_buf,
+ port->buf[dsp_buf].data,
+ port->cpu_buf,
+ &port->buf[port->cpu_buf].phys);
+
+ read.hdr.opcode = ASM_DATA_CMD_READ_V2;
+ read.buf_addr_lsw = lower_32_bits(ab->phys);
+ read.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys);
+ read.buf_size = ab->size;
+ read.seq_id = port->dsp_buf;
+ q6asm_update_token(&read.hdr.token,
+ 0, /* Session ID is NA */
+ 0, /* Stream ID is NA */
+ port->dsp_buf,
+ 0, /* Direction flag is NA */
+ WAIT_CMD);
+
+ list_for_each_safe(ptr, next, &ac->port[OUT].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_phys_addr == ab->phys) {
+ read.mem_map_handle = buf_node->mmap_hdl;
+ break;
+ }
+ }
+
+ port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf,
+ port->max_buf_cnt);
+ dev_vdbg(ac->dev, "%s: buf add[%pK] token[0x%x] uid[%d]\n",
+ __func__, &ab->phys, read.hdr.token,
+ read.seq_id);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+ if (rc < 0) {
+ pr_err("%s: read op[0x%x]rc[%d]\n",
+ __func__, read.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_async_write(struct audio_client *ac,
+ struct audio_aio_write_param *param)
+{
+ int rc = 0;
+ struct asm_data_cmd_write_v2 write;
+ struct asm_buffer_node *buf_node = NULL;
+ struct list_head *ptr, *next;
+ struct audio_buffer *ab;
+ struct audio_port_data *port;
+ phys_addr_t lbuf_phys_addr;
+ u32 liomode;
+ u32 io_compressed;
+ u32 io_compressed_stream;
+ int offset = 0;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6asm_stream_add_hdr_async(
+ ac, &write.hdr, sizeof(write), TRUE, ac->stream_id);
+ port = &ac->port[IN];
+ ab = &port->buf[port->dsp_buf];
+
+ /* Pass session id as token for AIO scheme */
+ write.hdr.token = param->uid;
+ write.hdr.opcode = ASM_DATA_CMD_WRITE_V2;
+ write.buf_addr_lsw = lower_32_bits(param->paddr);
+ write.buf_addr_msw = msm_audio_populate_upper_32_bits(param->paddr);
+ write.buf_size = param->len;
+ write.timestamp_msw = param->msw_ts;
+ write.timestamp_lsw = param->lsw_ts;
+ liomode = (ASYNC_IO_MODE | NT_MODE);
+ io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO);
+ io_compressed_stream = (ASYNC_IO_MODE | COMPRESSED_STREAM_IO);
+
+ if (ac->io_mode == liomode)
+ lbuf_phys_addr = (param->paddr - 32);
+ else if (ac->io_mode == io_compressed ||
+ ac->io_mode == io_compressed_stream)
+ lbuf_phys_addr = (param->paddr - param->metadata_len);
+ else {
+ if (param->flags & SET_TIMESTAMP)
+ lbuf_phys_addr = param->paddr -
+ sizeof(struct snd_codec_metadata);
+ else
+ lbuf_phys_addr = param->paddr;
+ }
+ dev_vdbg(ac->dev, "%s: token[0x%x], buf_addr[%pK], buf_size[0x%x], ts_msw[0x%x], ts_lsw[0x%x], lbuf_phys_addr: 0x[%pK]\n",
+ __func__,
+ write.hdr.token, &param->paddr,
+ write.buf_size, write.timestamp_msw,
+ write.timestamp_lsw, &lbuf_phys_addr);
+
+ /* Use 0xFF00 for disabling timestamps */
+ if (param->flags == 0xFF00)
+ write.flags = (0x00000000 | (param->flags & 0x800000FF));
+ else
+ write.flags = (0x80000000 | param->flags);
+ write.flags |= param->last_buffer << ASM_SHIFT_LAST_BUFFER_FLAG;
+ write.seq_id = param->uid;
+ list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_phys_addr == lbuf_phys_addr) {
+ write.mem_map_handle = buf_node->mmap_hdl;
+ break;
+ }
+ }
+
+ if (ab != NULL) {
+ offset = lbuf_phys_addr - ab->phys;
+ config_debug_fs_write(ab, offset);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+ if (rc < 0) {
+ pr_err("%s: write op[0x%x]rc[%d]\n", __func__,
+ write.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_async_read(struct audio_client *ac,
+ struct audio_aio_read_param *param)
+{
+ int rc = 0;
+ struct asm_data_cmd_read_v2 read;
+ struct asm_buffer_node *buf_node = NULL;
+ struct list_head *ptr, *next;
+ phys_addr_t lbuf_phys_addr;
+ u32 liomode;
+ u32 io_compressed;
+ int dir = 0;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE);
+
+ /* Pass session id as token for AIO scheme */
+ read.hdr.token = param->uid;
+ read.hdr.opcode = ASM_DATA_CMD_READ_V2;
+ read.buf_addr_lsw = lower_32_bits(param->paddr);
+ read.buf_addr_msw = msm_audio_populate_upper_32_bits(param->paddr);
+ read.buf_size = param->len;
+ read.seq_id = param->uid;
+ liomode = (NT_MODE | ASYNC_IO_MODE);
+ io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO);
+ if (ac->io_mode == liomode) {
+ lbuf_phys_addr = (param->paddr - 32);
+ /*legacy wma driver case*/
+ dir = IN;
+ } else if (ac->io_mode == io_compressed) {
+ lbuf_phys_addr = (param->paddr - 64);
+ dir = OUT;
+ } else {
+ if (param->flags & COMPRESSED_TIMESTAMP_FLAG)
+ lbuf_phys_addr = param->paddr -
+ sizeof(struct snd_codec_metadata);
+ else
+ lbuf_phys_addr = param->paddr;
+ dir = OUT;
+ }
+
+ list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_phys_addr == lbuf_phys_addr) {
+ read.mem_map_handle = buf_node->mmap_hdl;
+ break;
+ }
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+ if (rc < 0) {
+ pr_err("%s: read op[0x%x]rc[%d]\n", __func__,
+ read.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t flags)
+{
+ int rc = 0;
+ struct asm_data_cmd_write_v2 write;
+ struct asm_buffer_node *buf_node = NULL;
+ struct audio_port_data *port;
+ struct audio_buffer *ab;
+ int dsp_buf = 0;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_vdbg(ac->dev, "%s: session[%d] len=%d\n",
+ __func__, ac->session, len);
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[IN];
+
+ q6asm_add_hdr(ac, &write.hdr, sizeof(write),
+ FALSE);
+ mutex_lock(&port->lock);
+
+ dsp_buf = port->dsp_buf;
+ ab = &port->buf[dsp_buf];
+
+ q6asm_update_token(&write.hdr.token,
+ 0, /* Session ID is NA */
+ 0, /* Stream ID is NA */
+ port->dsp_buf,
+ 0, /* Direction flag is NA */
+ NO_WAIT_CMD);
+ write.hdr.opcode = ASM_DATA_CMD_WRITE_V2;
+ write.buf_addr_lsw = lower_32_bits(ab->phys);
+ write.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys);
+ write.buf_size = len;
+ write.seq_id = port->dsp_buf;
+ write.timestamp_lsw = lsw_ts;
+ write.timestamp_msw = msw_ts;
+ /* Use 0xFF00 for disabling timestamps */
+ if (flags == 0xFF00)
+ write.flags = (0x00000000 | (flags & 0x800000FF));
+ else
+ write.flags = (0x80000000 | flags);
+ port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf,
+ port->max_buf_cnt);
+ buf_node = list_first_entry(&ac->port[IN].mem_map_handle,
+ struct asm_buffer_node,
+ list);
+ write.mem_map_handle = buf_node->mmap_hdl;
+
+ dev_vdbg(ac->dev, "%s: ab->phys[%pK]bufadd[0x%x] token[0x%x]buf_id[0x%x]buf_size[0x%x]mmaphdl[0x%x]"
+ , __func__,
+ &ab->phys,
+ write.buf_addr_lsw,
+ write.hdr.token,
+ write.seq_id,
+ write.buf_size,
+ write.mem_map_handle);
+ mutex_unlock(&port->lock);
+
+ config_debug_fs_write(ab, 0);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+ if (rc < 0) {
+ pr_err("%s: write op[0x%x]rc[%d]\n",
+ __func__, write.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t flags)
+{
+ int rc = 0;
+ struct asm_data_cmd_write_v2 write;
+ struct asm_buffer_node *buf_node = NULL;
+ struct audio_port_data *port;
+ struct audio_buffer *ab;
+ int dsp_buf = 0;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_vdbg(ac->dev, "%s: session[%d] len=%d\n",
+ __func__, ac->session, len);
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[IN];
+
+ q6asm_add_hdr_async(ac, &write.hdr, sizeof(write),
+ FALSE);
+
+ dsp_buf = port->dsp_buf;
+ ab = &port->buf[dsp_buf];
+
+ q6asm_update_token(&write.hdr.token,
+ 0, /* Session ID is NA */
+ 0, /* Stream ID is NA */
+ port->dsp_buf,
+ 0, /* Direction flag is NA */
+ NO_WAIT_CMD);
+
+ write.hdr.opcode = ASM_DATA_CMD_WRITE_V2;
+ write.buf_addr_lsw = lower_32_bits(ab->phys);
+ write.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys);
+ write.buf_size = len;
+ write.seq_id = port->dsp_buf;
+ write.timestamp_lsw = lsw_ts;
+ write.timestamp_msw = msw_ts;
+ buf_node = list_first_entry(&ac->port[IN].mem_map_handle,
+ struct asm_buffer_node,
+ list);
+ write.mem_map_handle = buf_node->mmap_hdl;
+ /* Use 0xFF00 for disabling timestamps */
+ if (flags == 0xFF00)
+ write.flags = (0x00000000 | (flags & 0x800000FF));
+ else
+ write.flags = (0x80000000 | flags);
+ port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf,
+ port->max_buf_cnt);
+
+ dev_vdbg(ac->dev, "%s: ab->phys[%pK]bufadd[0x%x]token[0x%x] buf_id[0x%x]buf_size[0x%x]mmaphdl[0x%x]"
+ , __func__,
+ &ab->phys,
+ write.buf_addr_lsw,
+ write.hdr.token,
+ write.seq_id,
+ write.buf_size,
+ write.mem_map_handle);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+ if (rc < 0) {
+ pr_err("%s: write op[0x%x]rc[%d]\n",
+ __func__, write.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp)
+{
+ struct asm_mtmx_strtr_get_params mtmx_params;
+ int rc;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (tstamp == NULL) {
+ pr_err("%s: tstamp NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6asm_add_hdr(ac, &mtmx_params.hdr, sizeof(mtmx_params), TRUE);
+ mtmx_params.hdr.opcode = ASM_SESSION_CMD_GET_MTMX_STRTR_PARAMS_V2;
+ mtmx_params.param_info.data_payload_addr_lsw = 0;
+ mtmx_params.param_info.data_payload_addr_msw = 0;
+ mtmx_params.param_info.mem_map_handle = 0;
+ mtmx_params.param_info.direction = (ac->io_mode & TUN_READ_IO_MODE
+ ? 1 : 0);
+ mtmx_params.param_info.module_id =
+ ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC;
+ mtmx_params.param_info.param_id =
+ ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3;
+ mtmx_params.param_info.param_max_size =
+ sizeof(struct param_hdr_v1) +
+ sizeof(struct asm_session_mtmx_strtr_param_session_time_v3_t);
+ atomic_set(&ac->time_flag, 1);
+
+ dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x]\n", __func__,
+ ac->session, mtmx_params.hdr.opcode);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &mtmx_params);
+ if (rc < 0) {
+ pr_err("%s: Commmand 0x%x failed %d\n", __func__,
+ mtmx_params.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->time_wait,
+ (atomic_read(&ac->time_flag) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in getting session time from DSP\n",
+ __func__);
+ goto fail_cmd;
+ }
+
+ *tstamp = ac->time_stamp;
+ return 0;
+
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_get_session_time_legacy(struct audio_client *ac, uint64_t *tstamp)
+{
+ struct apr_hdr hdr;
+ int rc;
+
+ if (ac == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (tstamp == NULL) {
+ pr_err("%s: tstamp NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE);
+ hdr.opcode = ASM_SESSION_CMD_GET_SESSIONTIME_V3;
+ atomic_set(&ac->time_flag, 1);
+
+ dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x]\n", __func__,
+ ac->session,
+ hdr.opcode);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("%s: Commmand 0x%x failed %d\n",
+ __func__, hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->time_wait,
+ (atomic_read(&ac->time_flag) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in getting session time from DSP\n",
+ __func__);
+ goto fail_cmd;
+ }
+
+ *tstamp = ac->time_stamp;
+ return 0;
+
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_send_mtmx_strtr_window(struct audio_client *ac,
+ struct asm_session_mtmx_strtr_param_window_v2_t *window_param,
+ uint32_t param_id)
+{
+ struct asm_mtmx_strtr_params matrix;
+ int sz = 0;
+ int rc = 0;
+
+ pr_debug("%s: Window lsw is %d, window msw is %d\n", __func__,
+ window_param->window_lsw, window_param->window_msw);
+
+ if (!ac) {
+ pr_err("%s: audio client handle is NULL\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (ac->apr == NULL) {
+ pr_err("%s: ac->apr is NULL", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ sz = sizeof(struct asm_mtmx_strtr_params);
+ q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2;
+
+ matrix.param.data_payload_addr_lsw = 0;
+ matrix.param.data_payload_addr_msw = 0;
+ matrix.param.mem_map_handle = 0;
+ matrix.param.data_payload_size =
+ sizeof(struct param_hdr_v1) +
+ sizeof(struct asm_session_mtmx_strtr_param_window_v2_t);
+ matrix.param.direction = 0; /* RX */
+ matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC;
+ matrix.data.param_id = param_id;
+ matrix.data.param_size =
+ sizeof(struct asm_session_mtmx_strtr_param_window_v2_t);
+ matrix.data.reserved = 0;
+ memcpy(&(matrix.config.window_param),
+ window_param,
+ sizeof(struct asm_session_mtmx_strtr_param_window_v2_t));
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix);
+ if (rc < 0) {
+ pr_err("%s: Render window start send failed paramid [0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout, Render window start paramid[0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_send_mtmx_strtr_render_mode(struct audio_client *ac,
+ uint32_t render_mode)
+{
+ struct asm_mtmx_strtr_params matrix;
+ struct asm_session_mtmx_strtr_param_render_mode_t render_param;
+ int sz = 0;
+ int rc = 0;
+
+ pr_debug("%s: render mode is %d\n", __func__, render_mode);
+
+ if (!ac) {
+ pr_err("%s: audio client handle is NULL\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (ac->apr == NULL) {
+ pr_err("%s: ac->apr is NULL\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if ((render_mode != ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT) &&
+ (render_mode != ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC)) {
+ pr_err("%s: Invalid render mode %d\n", __func__, render_mode);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ memset(&render_param, 0,
+ sizeof(struct asm_session_mtmx_strtr_param_render_mode_t));
+ render_param.flags = render_mode;
+
+ memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params));
+ sz = sizeof(struct asm_mtmx_strtr_params);
+ q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2;
+
+ matrix.param.data_payload_addr_lsw = 0;
+ matrix.param.data_payload_addr_msw = 0;
+ matrix.param.mem_map_handle = 0;
+ matrix.param.data_payload_size =
+ sizeof(struct param_hdr_v1) +
+ sizeof(struct asm_session_mtmx_strtr_param_render_mode_t);
+ matrix.param.direction = 0; /* RX */
+ matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC;
+ matrix.data.param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_MODE_CMD;
+ matrix.data.param_size =
+ sizeof(struct asm_session_mtmx_strtr_param_render_mode_t);
+ matrix.data.reserved = 0;
+ memcpy(&(matrix.config.render_param),
+ &render_param,
+ sizeof(struct asm_session_mtmx_strtr_param_render_mode_t));
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix);
+ if (rc < 0) {
+ pr_err("%s: Render mode send failed paramid [0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout, Render mode send paramid [0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -ETIMEDOUT;
+ goto exit;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto exit;
+ }
+ rc = 0;
+exit:
+ return rc;
+}
+
+int q6asm_send_mtmx_strtr_clk_rec_mode(struct audio_client *ac,
+ uint32_t clk_rec_mode)
+{
+ struct asm_mtmx_strtr_params matrix;
+ struct asm_session_mtmx_strtr_param_clk_rec_t clk_rec_param;
+ int sz = 0;
+ int rc = 0;
+
+ pr_debug("%s: clk rec mode is %d\n", __func__, clk_rec_mode);
+
+ if (!ac) {
+ pr_err("%s: audio client handle is NULL\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (ac->apr == NULL) {
+ pr_err("%s: ac->apr is NULL\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if ((clk_rec_mode != ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_NONE) &&
+ (clk_rec_mode != ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_AUTO)) {
+ pr_err("%s: Invalid clk rec mode %d\n", __func__, clk_rec_mode);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ memset(&clk_rec_param, 0,
+ sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t));
+ clk_rec_param.flags = clk_rec_mode;
+
+ memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params));
+ sz = sizeof(struct asm_mtmx_strtr_params);
+ q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2;
+
+ matrix.param.data_payload_addr_lsw = 0;
+ matrix.param.data_payload_addr_msw = 0;
+ matrix.param.mem_map_handle = 0;
+ matrix.param.data_payload_size =
+ sizeof(struct param_hdr_v1) +
+ sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t);
+ matrix.param.direction = 0; /* RX */
+ matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC;
+ matrix.data.param_id = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_CMD;
+ matrix.data.param_size =
+ sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t);
+ matrix.data.reserved = 0;
+ memcpy(&(matrix.config.clk_rec_param),
+ &clk_rec_param,
+ sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t));
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix);
+ if (rc < 0) {
+ pr_err("%s: clk rec mode send failed paramid [0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout, clk rec mode send paramid [0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -ETIMEDOUT;
+ goto exit;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto exit;
+ }
+ rc = 0;
+exit:
+ return rc;
+}
+
+int q6asm_send_mtmx_strtr_enable_adjust_session_clock(struct audio_client *ac,
+ bool enable)
+{
+ struct asm_mtmx_strtr_params matrix;
+ struct asm_session_mtmx_param_adjust_session_time_ctl_t adjust_time;
+ int sz = 0;
+ int rc = 0;
+
+ pr_debug("%s: adjust session enable %d\n", __func__, enable);
+
+ if (!ac) {
+ pr_err("%s: audio client handle is NULL\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (ac->apr == NULL) {
+ pr_err("%s: ac->apr is NULL\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ adjust_time.enable = enable;
+ memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params));
+ sz = sizeof(struct asm_mtmx_strtr_params);
+ q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2;
+
+ matrix.param.data_payload_addr_lsw = 0;
+ matrix.param.data_payload_addr_msw = 0;
+ matrix.param.mem_map_handle = 0;
+ matrix.param.data_payload_size =
+ sizeof(struct param_hdr_v1) +
+ sizeof(struct asm_session_mtmx_param_adjust_session_time_ctl_t);
+ matrix.param.direction = 0; /* RX */
+ matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC;
+ matrix.data.param_id = ASM_SESSION_MTMX_PARAM_ADJUST_SESSION_TIME_CTL;
+ matrix.data.param_size =
+ sizeof(struct asm_session_mtmx_param_adjust_session_time_ctl_t);
+ matrix.data.reserved = 0;
+ matrix.config.adj_time_param.enable = adjust_time.enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix);
+ if (rc < 0) {
+ pr_err("%s: enable adjust session failed failed paramid [0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: enable adjust session failed failed paramid [0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -ETIMEDOUT;
+ goto exit;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto exit;
+ }
+ rc = 0;
+exit:
+ return rc;
+}
+
+
+static int __q6asm_cmd(struct audio_client *ac, int cmd, uint32_t stream_id)
+{
+ struct apr_hdr hdr;
+ int rc;
+ atomic_t *state;
+ int cnt = 0;
+
+ if (!ac) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ q6asm_stream_add_hdr(ac, &hdr, sizeof(hdr), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ q6asm_update_token(&hdr.token,
+ ac->session,
+ stream_id,
+ 0, /* Buffer index is NA */
+ 0, /* Direction flag is NA */
+ WAIT_CMD);
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, hdr.token, stream_id, ac->session);
+ switch (cmd) {
+ case CMD_PAUSE:
+ pr_debug("%s: CMD_PAUSE\n", __func__);
+ hdr.opcode = ASM_SESSION_CMD_PAUSE;
+ state = &ac->cmd_state;
+ break;
+ case CMD_SUSPEND:
+ pr_debug("%s: CMD_SUSPEND\n", __func__);
+ hdr.opcode = ASM_SESSION_CMD_SUSPEND;
+ state = &ac->cmd_state;
+ break;
+ case CMD_FLUSH:
+ pr_debug("%s: CMD_FLUSH\n", __func__);
+ hdr.opcode = ASM_STREAM_CMD_FLUSH;
+ state = &ac->cmd_state;
+ break;
+ case CMD_OUT_FLUSH:
+ pr_debug("%s: CMD_OUT_FLUSH\n", __func__);
+ hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS;
+ state = &ac->cmd_state;
+ break;
+ case CMD_EOS:
+ pr_debug("%s: CMD_EOS\n", __func__);
+ hdr.opcode = ASM_DATA_CMD_EOS;
+ atomic_set(&ac->cmd_state, 0);
+ state = &ac->cmd_state;
+ break;
+ case CMD_CLOSE:
+ pr_debug("%s: CMD_CLOSE\n", __func__);
+ hdr.opcode = ASM_STREAM_CMD_CLOSE;
+ state = &ac->cmd_state;
+ break;
+ default:
+ pr_err("%s: Invalid format[%d]\n", __func__, cmd);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ pr_debug("%s: session[%d]opcode[0x%x]\n", __func__,
+ ac->session,
+ hdr.opcode);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("%s: Commmand 0x%x failed %d\n",
+ __func__, hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait, (atomic_read(state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for response opcode[0x%x]\n",
+ __func__, hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(state) > 0) {
+ pr_err("%s: DSP returned error[%s] opcode %d\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(state)),
+ hdr.opcode);
+ rc = adsp_err_get_lnx_err_code(atomic_read(state));
+ goto fail_cmd;
+ }
+
+ if (cmd == CMD_FLUSH)
+ q6asm_reset_buf_state(ac);
+ if (cmd == CMD_CLOSE) {
+ /* check if DSP return all buffers */
+ if (ac->port[IN].buf) {
+ for (cnt = 0; cnt < ac->port[IN].max_buf_cnt;
+ cnt++) {
+ if (ac->port[IN].buf[cnt].used == IN) {
+ dev_vdbg(ac->dev, "Write Buf[%d] not returned\n",
+ cnt);
+ }
+ }
+ }
+ if (ac->port[OUT].buf) {
+ for (cnt = 0; cnt < ac->port[OUT].max_buf_cnt; cnt++) {
+ if (ac->port[OUT].buf[cnt].used == OUT) {
+ dev_vdbg(ac->dev, "Read Buf[%d] not returned\n",
+ cnt);
+ }
+ }
+ }
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_cmd(struct audio_client *ac, int cmd)
+{
+ return __q6asm_cmd(ac, cmd, ac->stream_id);
+}
+
+int q6asm_stream_cmd(struct audio_client *ac, int cmd, uint32_t stream_id)
+{
+ return __q6asm_cmd(ac, cmd, stream_id);
+}
+
+static int __q6asm_cmd_nowait(struct audio_client *ac, int cmd,
+ uint32_t stream_id)
+{
+ struct apr_hdr hdr;
+ int rc;
+
+ if (!ac) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ q6asm_stream_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, 1);
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ q6asm_update_token(&hdr.token,
+ ac->session,
+ stream_id,
+ 0, /* Buffer index is NA */
+ 0, /* Direction flag is NA */
+ NO_WAIT_CMD);
+
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, hdr.token, stream_id, ac->session);
+ switch (cmd) {
+ case CMD_PAUSE:
+ pr_debug("%s: CMD_PAUSE\n", __func__);
+ hdr.opcode = ASM_SESSION_CMD_PAUSE;
+ break;
+ case CMD_EOS:
+ pr_debug("%s: CMD_EOS\n", __func__);
+ hdr.opcode = ASM_DATA_CMD_EOS;
+ break;
+ case CMD_CLOSE:
+ pr_debug("%s: CMD_CLOSE\n", __func__);
+ hdr.opcode = ASM_STREAM_CMD_CLOSE;
+ break;
+ default:
+ pr_err("%s: Invalid format[%d]\n", __func__, cmd);
+ goto fail_cmd;
+ }
+ pr_debug("%s: session[%d]opcode[0x%x]\n", __func__,
+ ac->session,
+ hdr.opcode);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("%s: Commmand 0x%x failed %d\n",
+ __func__, hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_cmd_nowait(struct audio_client *ac, int cmd)
+{
+ pr_debug("%s: stream_id: %d\n", __func__, ac->stream_id);
+ return __q6asm_cmd_nowait(ac, cmd, ac->stream_id);
+}
+
+int q6asm_stream_cmd_nowait(struct audio_client *ac, int cmd,
+ uint32_t stream_id)
+{
+ pr_debug("%s: stream_id: %d\n", __func__, stream_id);
+ return __q6asm_cmd_nowait(ac, cmd, stream_id);
+}
+
+int __q6asm_send_meta_data(struct audio_client *ac, uint32_t stream_id,
+ uint32_t initial_samples, uint32_t trailing_samples)
+{
+ struct asm_data_cmd_remove_silence silence;
+ int rc = 0;
+
+ if (!ac) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]\n", __func__, ac->session);
+ q6asm_stream_add_hdr_async(ac, &silence.hdr, sizeof(silence), TRUE,
+ stream_id);
+
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ q6asm_update_token(&silence.hdr.token,
+ ac->session,
+ stream_id,
+ 0, /* Buffer index is NA */
+ 0, /* Direction flag is NA */
+ NO_WAIT_CMD);
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, silence.hdr.token, stream_id, ac->session);
+
+ silence.hdr.opcode = ASM_DATA_CMD_REMOVE_INITIAL_SILENCE;
+ silence.num_samples_to_remove = initial_samples;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &silence);
+ if (rc < 0) {
+ pr_err("%s: Commmand silence failed[%d]", __func__, rc);
+
+ goto fail_cmd;
+ }
+
+ silence.hdr.opcode = ASM_DATA_CMD_REMOVE_TRAILING_SILENCE;
+ silence.num_samples_to_remove = trailing_samples;
+
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &silence);
+ if (rc < 0) {
+ pr_err("%s: Commmand silence failed[%d]", __func__, rc);
+ goto fail_cmd;
+ }
+
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_stream_send_meta_data(struct audio_client *ac, uint32_t stream_id,
+ uint32_t initial_samples, uint32_t trailing_samples)
+{
+ return __q6asm_send_meta_data(ac, stream_id, initial_samples,
+ trailing_samples);
+}
+
+int q6asm_send_meta_data(struct audio_client *ac, uint32_t initial_samples,
+ uint32_t trailing_samples)
+{
+ return __q6asm_send_meta_data(ac, ac->stream_id, initial_samples,
+ trailing_samples);
+}
+
+static void q6asm_reset_buf_state(struct audio_client *ac)
+{
+ int cnt = 0;
+ int loopcnt = 0;
+ int used;
+ struct audio_port_data *port = NULL;
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ used = (ac->io_mode & TUN_WRITE_IO_MODE ? 1 : 0);
+ mutex_lock(&ac->cmd_lock);
+ for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
+ port = &ac->port[loopcnt];
+ cnt = port->max_buf_cnt - 1;
+ port->dsp_buf = 0;
+ port->cpu_buf = 0;
+ while (cnt >= 0) {
+ if (!port->buf)
+ continue;
+ port->buf[cnt].used = used;
+ cnt--;
+ }
+ }
+ mutex_unlock(&ac->cmd_lock);
+ }
+}
+
+int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable)
+{
+ struct asm_session_cmd_regx_overflow tx_overflow;
+ int rc;
+
+ if (!ac) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]enable[%d]\n", __func__,
+ ac->session, enable);
+ q6asm_add_hdr(ac, &tx_overflow.hdr, sizeof(tx_overflow), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ tx_overflow.hdr.opcode = \
+ ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS;
+ /* tx overflow event: enable */
+ tx_overflow.enable_flag = enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &tx_overflow);
+ if (rc < 0) {
+ pr_err("%s: tx overflow op[0x%x]rc[%d]\n",
+ __func__, tx_overflow.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for tx overflow\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_reg_rx_underflow(struct audio_client *ac, uint16_t enable)
+{
+ struct asm_session_cmd_rgstr_rx_underflow rx_underflow;
+ int rc;
+
+ if (!ac) {
+ pr_err("%s: AC APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->apr == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]enable[%d]\n", __func__,
+ ac->session, enable);
+ q6asm_add_hdr_async(ac, &rx_underflow.hdr, sizeof(rx_underflow), FALSE);
+
+ rx_underflow.hdr.opcode =
+ ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS;
+ /* tx overflow event: enable */
+ rx_underflow.enable_flag = enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &rx_underflow);
+ if (rc < 0) {
+ pr_err("%s: tx overflow op[0x%x]rc[%d]\n",
+ __func__, rx_underflow.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_adjust_session_clock(struct audio_client *ac,
+ uint32_t adjust_time_lsw,
+ uint32_t adjust_time_msw)
+{
+ int rc = 0;
+ int sz = 0;
+ struct asm_session_cmd_adjust_session_clock_v2 adjust_clock;
+
+ pr_debug("%s: adjust_time_lsw is %x, adjust_time_msw is %x\n", __func__,
+ adjust_time_lsw, adjust_time_msw);
+
+ if (!ac) {
+ pr_err("%s: audio client handle is NULL\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (ac->apr == NULL) {
+ pr_err("%s: ac->apr is NULL", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ sz = sizeof(struct asm_session_cmd_adjust_session_clock_v2);
+ q6asm_add_hdr(ac, &adjust_clock.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ adjust_clock.hdr.opcode = ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2;
+
+ adjust_clock.adjustime_lsw = adjust_time_lsw;
+ adjust_clock.adjustime_msw = adjust_time_msw;
+
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &adjust_clock);
+ if (rc < 0) {
+ pr_err("%s: adjust_clock send failed paramid [0x%x]\n",
+ __func__, adjust_clock.hdr.opcode);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout, adjust_clock paramid[0x%x]\n",
+ __func__, adjust_clock.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+/*
+ * q6asm_get_path_delay() - get the path delay for an audio session
+ * @ac: audio client handle
+ *
+ * Retrieves the current audio DSP path delay for the given audio session.
+ *
+ * Return: 0 on success, error code otherwise
+ */
+int q6asm_get_path_delay(struct audio_client *ac)
+{
+ int rc = 0;
+ struct apr_hdr hdr;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("%s: invalid audio client\n", __func__);
+ return -EINVAL;
+ }
+
+ hdr.opcode = ASM_SESSION_CMD_GET_PATH_DELAY_V2;
+ q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("%s: Commmand 0x%x failed %d\n", __func__,
+ hdr.opcode, rc);
+ return rc;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5 * HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for response opcode[0x%x]\n",
+ __func__, hdr.opcode);
+ return -ETIMEDOUT;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ return rc;
+ }
+
+ return 0;
+}
+
+int q6asm_get_apr_service_id(int session_id)
+{
+ pr_debug("%s:\n", __func__);
+
+ if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) {
+ pr_err("%s: invalid session_id = %d\n", __func__, session_id);
+ return -EINVAL;
+ }
+
+ return ((struct apr_svc *)(session[session_id].ac)->apr)->id;
+}
+
+int q6asm_get_asm_topology(int session_id)
+{
+ int topology = -EINVAL;
+
+ if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) {
+ pr_err("%s: invalid session_id = %d\n", __func__, session_id);
+ goto done;
+ }
+ if (session[session_id].ac == NULL) {
+ pr_err("%s: session not created for session id = %d\n",
+ __func__, session_id);
+ goto done;
+ }
+ topology = (session[session_id].ac)->topology;
+done:
+ return topology;
+}
+
+int q6asm_get_asm_app_type(int session_id)
+{
+ int app_type = -EINVAL;
+
+ if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) {
+ pr_err("%s: invalid session_id = %d\n", __func__, session_id);
+ goto done;
+ }
+ if (session[session_id].ac == NULL) {
+ pr_err("%s: session not created for session id = %d\n",
+ __func__, session_id);
+ goto done;
+ }
+ app_type = (session[session_id].ac)->app_type;
+done:
+ return app_type;
+}
+
+static int q6asm_get_asm_topology_cal(void)
+{
+ int topology = DEFAULT_POPP_TOPOLOGY;
+ struct cal_block_data *cal_block = NULL;
+
+ if (cal_data[ASM_TOPOLOGY_CAL] == NULL)
+ goto done;
+
+ mutex_lock(&cal_data[ASM_TOPOLOGY_CAL]->lock);
+ cal_block = cal_utils_get_only_cal_block(cal_data[ASM_TOPOLOGY_CAL]);
+ if (cal_block == NULL)
+ goto unlock;
+
+ topology = ((struct audio_cal_info_asm_top *)
+ cal_block->cal_info)->topology;
+unlock:
+ mutex_unlock(&cal_data[ASM_TOPOLOGY_CAL]->lock);
+done:
+ pr_debug("%s: Using topology %d\n", __func__, topology);
+ return topology;
+}
+
+static int q6asm_get_asm_app_type_cal(void)
+{
+ int app_type = DEFAULT_APP_TYPE;
+ struct cal_block_data *cal_block = NULL;
+
+ if (cal_data[ASM_TOPOLOGY_CAL] == NULL)
+ goto done;
+
+ mutex_lock(&cal_data[ASM_TOPOLOGY_CAL]->lock);
+ cal_block = cal_utils_get_only_cal_block(cal_data[ASM_TOPOLOGY_CAL]);
+ if (cal_block == NULL)
+ goto unlock;
+
+ app_type = ((struct audio_cal_info_asm_top *)
+ cal_block->cal_info)->app_type;
+
+ if (app_type == 0)
+ app_type = DEFAULT_APP_TYPE;
+unlock:
+ mutex_unlock(&cal_data[ASM_TOPOLOGY_CAL]->lock);
+done:
+ pr_debug("%s: Using app_type %d\n", __func__, app_type);
+ return app_type;
+}
+
+int q6asm_send_cal(struct audio_client *ac)
+{
+ struct cal_block_data *cal_block = NULL;
+ struct mem_mapping_hdr mem_hdr = {0};
+ u32 payload_size = 0;
+ int rc = -EINVAL;
+ pr_debug("%s:\n", __func__);
+
+ if (!ac) {
+ pr_err("%s: Audio client is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (ac->io_mode & NT_MODE) {
+ pr_debug("%s: called for NT MODE, exiting\n", __func__);
+ goto done;
+ }
+
+ if (cal_data[ASM_AUDSTRM_CAL] == NULL)
+ goto done;
+
+ if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) {
+ rc = 0; /* no cal is required, not error case */
+ goto done;
+ }
+
+ mutex_lock(&cal_data[ASM_AUDSTRM_CAL]->lock);
+ cal_block = cal_utils_get_only_cal_block(cal_data[ASM_AUDSTRM_CAL]);
+ if (cal_block == NULL) {
+ pr_err("%s: cal_block is NULL\n",
+ __func__);
+ goto unlock;
+ }
+
+ if (cal_block->cal_data.size == 0) {
+ rc = 0; /* not error case */
+ pr_debug("%s: cal_data.size is 0, don't send cal data\n",
+ __func__);
+ goto unlock;
+ }
+
+ rc = remap_cal_data(ASM_AUDSTRM_CAL_TYPE, cal_block);
+ if (rc) {
+ pr_err("%s: Remap_cal_data failed for cal %d!\n",
+ __func__, ASM_AUDSTRM_CAL);
+ goto unlock;
+ }
+
+ mem_hdr.data_payload_addr_lsw =
+ lower_32_bits(cal_block->cal_data.paddr);
+ mem_hdr.data_payload_addr_msw =
+ msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+ mem_hdr.mem_map_handle = cal_block->map_data.q6map_handle;
+ payload_size = cal_block->cal_data.size;
+
+ pr_debug("%s: phyaddr lsw = %x msw = %x, maphdl = %x calsize = %d\n",
+ __func__, mem_hdr.data_payload_addr_lsw,
+ mem_hdr.data_payload_addr_msw, mem_hdr.mem_map_handle,
+ payload_size);
+
+ rc = q6asm_set_pp_params(ac, &mem_hdr, NULL, payload_size);
+ if (rc) {
+ pr_err("%s: audio audstrm cal send failed\n", __func__);
+ goto unlock;
+ }
+
+ rc = 0;
+
+unlock:
+ mutex_unlock(&cal_data[ASM_AUDSTRM_CAL]->lock);
+done:
+ return rc;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+ int ret = -EINVAL;
+
+ switch (cal_type) {
+ case ASM_TOPOLOGY_CAL_TYPE:
+ ret = ASM_TOPOLOGY_CAL;
+ break;
+ case ASM_CUST_TOPOLOGY_CAL_TYPE:
+ ret = ASM_CUSTOM_TOP_CAL;
+ break;
+ case ASM_AUDSTRM_CAL_TYPE:
+ ret = ASM_AUDSTRM_CAL;
+ break;
+ case ASM_RTAC_APR_CAL_TYPE:
+ ret = ASM_RTAC_APR_CAL;
+ break;
+ default:
+ pr_err("%s: invalid cal type %d!\n", __func__, cal_type);
+ }
+ return ret;
+}
+
+static int q6asm_alloc_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_alloc_cal(data_size, data,
+ cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int q6asm_dealloc_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_dealloc_cal(data_size, data,
+ cal_data[cal_index]);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int q6asm_set_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_set_cal(data_size, data,
+ cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (cal_index == ASM_CUSTOM_TOP_CAL) {
+ mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+ set_custom_topology = 1;
+ mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+ }
+done:
+ return ret;
+}
+
+static void q6asm_delete_cal_data(void)
+{
+ pr_debug("%s:\n", __func__);
+ cal_utils_destroy_cal_types(ASM_MAX_CAL_TYPES, cal_data);
+
+ return;
+}
+
+static int q6asm_init_cal_data(void)
+{
+ int ret = 0;
+ struct cal_type_info cal_type_info[] = {
+ {{ASM_TOPOLOGY_CAL_TYPE,
+ {NULL, NULL, NULL,
+ q6asm_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{ASM_CUST_TOPOLOGY_CAL_TYPE,
+ {q6asm_alloc_cal, q6asm_dealloc_cal, NULL,
+ q6asm_set_cal, NULL, NULL} },
+ {NULL, q6asm_unmap_cal_memory, cal_utils_match_buf_num} },
+
+ {{ASM_AUDSTRM_CAL_TYPE,
+ {q6asm_alloc_cal, q6asm_dealloc_cal, NULL,
+ q6asm_set_cal, NULL, NULL} },
+ {NULL, q6asm_unmap_cal_memory, cal_utils_match_buf_num} },
+
+ {{ASM_RTAC_APR_CAL_TYPE,
+ {NULL, NULL, NULL, NULL, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} }
+ };
+ pr_debug("%s\n", __func__);
+
+ ret = cal_utils_create_cal_types(ASM_MAX_CAL_TYPES, cal_data,
+ cal_type_info);
+ if (ret < 0) {
+ pr_err("%s: could not create cal type! %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return ret;
+err:
+ q6asm_delete_cal_data();
+ return ret;
+}
+
+static int q6asm_is_valid_session(struct apr_client_data *data, void *priv)
+{
+ struct audio_client *ac = (struct audio_client *)priv;
+ union asm_token_struct asm_token;
+
+ asm_token.token = data->token;
+ if (asm_token._token.session_id != ac->session) {
+ pr_err("%s: Invalid session[%d] rxed expected[%d]",
+ __func__, asm_token._token.session_id, ac->session);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int __init q6asm_init(void)
+{
+ int lcnt, ret;
+ pr_debug("%s:\n", __func__);
+
+ memset(session, 0, sizeof(struct audio_session) *
+ (ASM_ACTIVE_STREAMS_ALLOWED + 1));
+ for (lcnt = 0; lcnt <= ASM_ACTIVE_STREAMS_ALLOWED; lcnt++)
+ spin_lock_init(&(session[lcnt].session_lock));
+ set_custom_topology = 1;
+
+ /*setup common client used for cal mem map */
+ common_client.session = ASM_CONTROL_SESSION;
+ common_client.port[0].buf = &common_buf[0];
+ common_client.port[1].buf = &common_buf[1];
+ init_waitqueue_head(&common_client.cmd_wait);
+ init_waitqueue_head(&common_client.time_wait);
+ init_waitqueue_head(&common_client.mem_wait);
+ atomic_set(&common_client.time_flag, 1);
+ INIT_LIST_HEAD(&common_client.port[0].mem_map_handle);
+ INIT_LIST_HEAD(&common_client.port[1].mem_map_handle);
+ mutex_init(&common_client.cmd_lock);
+ for (lcnt = 0; lcnt <= OUT; lcnt++) {
+ mutex_init(&common_client.port[lcnt].lock);
+ spin_lock_init(&common_client.port[lcnt].dsp_lock);
+ }
+ atomic_set(&common_client.cmd_state, 0);
+ atomic_set(&common_client.mem_state, 0);
+
+ ret = q6asm_init_cal_data();
+ if (ret)
+ pr_err("%s: could not init cal data! ret %d\n",
+ __func__, ret);
+
+ config_debug_fs_init();
+
+ return 0;
+}
+
+static void __exit q6asm_exit(void)
+{
+ q6asm_delete_cal_data();
+}
+
+device_initcall(q6asm_init);
+__exitcall(q6asm_exit);
diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c
new file mode 100644
index 000000000000..9faf85e11ed7
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c
@@ -0,0 +1,962 @@
+/* Copyright (c) 2012-2018, 2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6audio-v2.h>
+
+int q6audio_get_port_index(u16 port_id)
+{
+ switch (port_id) {
+ case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX;
+ case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX;
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ return IDX_AFE_PORT_ID_PRIMARY_PCM_RX;
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ return IDX_AFE_PORT_ID_PRIMARY_PCM_TX;
+ case AFE_PORT_ID_SECONDARY_PCM_RX:
+ return IDX_AFE_PORT_ID_SECONDARY_PCM_RX;
+ case AFE_PORT_ID_SECONDARY_PCM_TX:
+ return IDX_AFE_PORT_ID_SECONDARY_PCM_TX;
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_PCM_RX;
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_PCM_TX;
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX;
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX;
+ case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX;
+ case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX;
+ case MI2S_RX: return IDX_MI2S_RX;
+ case MI2S_TX: return IDX_MI2S_TX;
+ case HDMI_RX: return IDX_HDMI_RX;
+ case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX;
+ case AFE_PORT_ID_SPDIF_RX: return IDX_SPDIF_RX;
+ case RSVD_2: return IDX_RSVD_2;
+ case RSVD_3: return IDX_RSVD_3;
+ case DIGI_MIC_TX: return IDX_DIGI_MIC_TX;
+ case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX;
+ case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX;
+ case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX;
+ case VOICE2_PLAYBACK_TX: return IDX_VOICE2_PLAYBACK_TX;
+ case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX;
+ case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX;
+ case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX;
+ case SLIMBUS_1_TX: return IDX_SLIMBUS_1_TX;
+ case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX;
+ case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX;
+ case SLIMBUS_3_RX: return IDX_SLIMBUS_3_RX;
+ case SLIMBUS_3_TX: return IDX_SLIMBUS_3_TX;
+ case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX;
+ case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX;
+ case SLIMBUS_5_RX: return IDX_SLIMBUS_5_RX;
+ case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX;
+ case SLIMBUS_6_RX: return IDX_SLIMBUS_6_RX;
+ case SLIMBUS_6_TX: return IDX_SLIMBUS_6_TX;
+ case SLIMBUS_7_RX: return IDX_SLIMBUS_7_RX;
+ case SLIMBUS_7_TX: return IDX_SLIMBUS_7_TX;
+ case SLIMBUS_8_RX: return IDX_SLIMBUS_8_RX;
+ case SLIMBUS_8_TX: return IDX_SLIMBUS_8_TX;
+ case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX;
+ case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX;
+ case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX;
+ case INT_FM_RX: return IDX_INT_FM_RX;
+ case INT_FM_TX: return IDX_INT_FM_TX;
+ case RT_PROXY_PORT_001_RX: return IDX_RT_PROXY_PORT_001_RX;
+ case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX;
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_PRIMARY_MI2S_RX;
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_PRIMARY_MI2S_TX;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX_1;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX_2;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX_3;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX_4;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX_1;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX_2;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX_3;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX_4;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX_1;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX_2;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX_3;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX_4;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_1;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_2;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_3;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_4;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX_1;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX_2;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX_3;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX_4;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX_1;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX_2;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX_3;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX_4;
+ case AUDIO_PORT_ID_I2S_RX:
+ return IDX_AUDIO_PORT_ID_I2S_RX;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+ return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_SD1;
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_0;
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_0;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_1;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_1;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_2;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_2;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_3;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_3;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_4;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_4;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_5;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_5;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_6;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_6;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_7;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_7;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_0;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_0;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_1;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_1;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_2;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_2;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_3;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_3;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_4;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_4;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_5;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_5;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_6;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_6;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_7;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_7;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_0;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_0;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_1;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_1;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_2;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_2;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_3;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_3;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_4;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_4;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_5;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_5;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_6;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_6;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_7;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_7;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_0;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_0;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_1;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_1;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_2;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_2;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_3;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_3;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_4;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_4;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_5;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_5;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_6;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7;
+ case AFE_PORT_ID_SENARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_SENARY_MI2S_TX;
+ case AFE_PORT_ID_USB_RX:
+ return IDX_AFE_PORT_ID_USB_RX;
+ case AFE_PORT_ID_USB_TX:
+ return IDX_AFE_PORT_ID_USB_TX;
+ case AFE_PORT_ID_INT0_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT0_MI2S_RX;
+ case AFE_PORT_ID_INT0_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT0_MI2S_TX;
+ case AFE_PORT_ID_INT1_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT1_MI2S_RX;
+ case AFE_PORT_ID_INT1_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT1_MI2S_TX;
+ case AFE_PORT_ID_INT2_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT2_MI2S_RX;
+ case AFE_PORT_ID_INT2_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT2_MI2S_TX;
+ case AFE_PORT_ID_INT3_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT3_MI2S_RX;
+ case AFE_PORT_ID_INT3_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT3_MI2S_TX;
+ case AFE_PORT_ID_INT4_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT4_MI2S_RX;
+ case AFE_PORT_ID_INT4_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT4_MI2S_TX;
+ case AFE_PORT_ID_INT5_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT5_MI2S_RX;
+ case AFE_PORT_ID_INT5_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT5_MI2S_TX;
+ case AFE_PORT_ID_INT6_MI2S_RX:
+ return IDX_AFE_PORT_ID_INT6_MI2S_RX;
+ case AFE_PORT_ID_INT6_MI2S_TX:
+ return IDX_AFE_PORT_ID_INT6_MI2S_TX;
+ case RT_PROXY_PORT_002_RX:
+ return IDX_RT_PROXY_PORT_002_RX;
+ case RT_PROXY_PORT_002_TX:
+ return IDX_RT_PROXY_PORT_002_TX;
+ default: return -EINVAL;
+ }
+}
+
+int q6audio_get_port_id(u16 port_id)
+{
+ switch (port_id) {
+ case PRIMARY_I2S_RX: return PRIMARY_I2S_RX;
+ case PRIMARY_I2S_TX: return PRIMARY_I2S_TX;
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ return AFE_PORT_ID_PRIMARY_PCM_RX;
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ return AFE_PORT_ID_PRIMARY_PCM_TX;
+ case AFE_PORT_ID_SECONDARY_PCM_RX:
+ return AFE_PORT_ID_SECONDARY_PCM_RX;
+ case AFE_PORT_ID_SECONDARY_PCM_TX:
+ return AFE_PORT_ID_SECONDARY_PCM_TX;
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ return AFE_PORT_ID_TERTIARY_PCM_RX;
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ return AFE_PORT_ID_TERTIARY_PCM_TX;
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ return AFE_PORT_ID_QUATERNARY_PCM_RX;
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ return AFE_PORT_ID_QUATERNARY_PCM_TX;
+ case SECONDARY_I2S_RX: return AFE_PORT_ID_SECONDARY_MI2S_RX;
+ case SECONDARY_I2S_TX: return AFE_PORT_ID_SECONDARY_MI2S_TX;
+ case MI2S_RX: return AFE_PORT_ID_PRIMARY_MI2S_RX;
+ case MI2S_TX: return AFE_PORT_ID_PRIMARY_MI2S_TX;
+ case HDMI_RX: return AFE_PORT_ID_MULTICHAN_HDMI_RX;
+ case DISPLAY_PORT_RX:
+ return AFE_PORT_ID_HDMI_OVER_DP_RX;
+ case AFE_PORT_ID_SPDIF_RX: return AFE_PORT_ID_SPDIF_RX;
+ case RSVD_2: return IDX_RSVD_2;
+ case RSVD_3: return IDX_RSVD_3;
+ case DIGI_MIC_TX: return AFE_PORT_ID_DIGITAL_MIC_TX;
+ case VOICE_RECORD_RX: return AFE_PORT_ID_VOICE_RECORD_RX;
+ case VOICE_RECORD_TX: return AFE_PORT_ID_VOICE_RECORD_TX;
+ case VOICE_PLAYBACK_TX: return AFE_PORT_ID_VOICE_PLAYBACK_TX;
+ case VOICE2_PLAYBACK_TX: return AFE_PORT_ID_VOICE2_PLAYBACK_TX;
+ case SLIMBUS_0_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX;
+ case SLIMBUS_0_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX;
+ case SLIMBUS_1_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX;
+ case SLIMBUS_1_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX;
+ case SLIMBUS_2_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX;
+ case SLIMBUS_2_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX;
+ case SLIMBUS_3_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX;
+ case SLIMBUS_3_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX;
+ case SLIMBUS_4_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX;
+ case SLIMBUS_4_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX;
+ case SLIMBUS_5_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX;
+ case SLIMBUS_5_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX;
+ case SLIMBUS_6_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX;
+ case SLIMBUS_6_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX;
+ case SLIMBUS_7_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_RX;
+ case SLIMBUS_7_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_TX;
+ case SLIMBUS_8_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_RX;
+ case SLIMBUS_8_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_TX;
+ case INT_BT_SCO_RX: return AFE_PORT_ID_INTERNAL_BT_SCO_RX;
+ case INT_BT_SCO_TX: return AFE_PORT_ID_INTERNAL_BT_SCO_TX;
+ case INT_BT_A2DP_RX: return AFE_PORT_ID_INTERNAL_BT_A2DP_RX;
+ case INT_FM_RX: return AFE_PORT_ID_INTERNAL_FM_RX;
+ case INT_FM_TX: return AFE_PORT_ID_INTERNAL_FM_TX;
+ case RT_PROXY_PORT_001_RX: return AFE_PORT_ID_RT_PROXY_PORT_001_RX;
+ case RT_PROXY_PORT_001_TX: return AFE_PORT_ID_RT_PROXY_PORT_001_TX;
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ return AFE_PORT_ID_PRIMARY_MI2S_RX;
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ return AFE_PORT_ID_PRIMARY_MI2S_TX;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ return AFE_PORT_ID_QUATERNARY_MI2S_RX;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ return AFE_PORT_ID_QUATERNARY_MI2S_TX;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX:
+ return AFE_PORT_ID_SECONDARY_MI2S_RX;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX:
+ return AFE_PORT_ID_SECONDARY_MI2S_TX;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ return AFE_PORT_ID_TERTIARY_MI2S_RX;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ return AFE_PORT_ID_TERTIARY_MI2S_TX;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ return AFE_PORT_ID_SECONDARY_MI2S_TX_1;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ return AFE_PORT_ID_SECONDARY_MI2S_TX_2;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ return AFE_PORT_ID_SECONDARY_MI2S_TX_3;
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ return AFE_PORT_ID_SECONDARY_MI2S_TX_4;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ return AFE_PORT_ID_TERTIARY_MI2S_TX_1;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ return AFE_PORT_ID_TERTIARY_MI2S_TX_2;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ return AFE_PORT_ID_TERTIARY_MI2S_TX_3;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ return AFE_PORT_ID_TERTIARY_MI2S_TX_4;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ return AFE_PORT_ID_QUATERNARY_MI2S_TX_1;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ return AFE_PORT_ID_QUATERNARY_MI2S_TX_2;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ return AFE_PORT_ID_QUATERNARY_MI2S_TX_3;
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ return AFE_PORT_ID_QUATERNARY_MI2S_TX_4;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ return AFE_PORT_ID_SECONDARY_MI2S_RX_1;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ return AFE_PORT_ID_SECONDARY_MI2S_RX_2;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ return AFE_PORT_ID_SECONDARY_MI2S_RX_3;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ return AFE_PORT_ID_SECONDARY_MI2S_RX_4;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ return AFE_PORT_ID_TERTIARY_MI2S_RX_1;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ return AFE_PORT_ID_TERTIARY_MI2S_RX_2;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ return AFE_PORT_ID_TERTIARY_MI2S_RX_3;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ return AFE_PORT_ID_TERTIARY_MI2S_RX_4;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ return AFE_PORT_ID_QUATERNARY_MI2S_RX_1;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ return AFE_PORT_ID_QUATERNARY_MI2S_RX_2;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ return AFE_PORT_ID_QUATERNARY_MI2S_RX_3;
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ return AFE_PORT_ID_QUATERNARY_MI2S_RX_4;
+ case AUDIO_PORT_ID_I2S_RX:
+ return AUDIO_PORT_ID_I2S_RX;
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+ return AFE_PORT_ID_SECONDARY_MI2S_RX_SD1;
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ return AFE_PORT_ID_PRIMARY_TDM_RX;
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ return AFE_PORT_ID_PRIMARY_TDM_TX;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ return AFE_PORT_ID_PRIMARY_TDM_RX_1;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ return AFE_PORT_ID_PRIMARY_TDM_TX_1;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ return AFE_PORT_ID_PRIMARY_TDM_RX_2;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ return AFE_PORT_ID_PRIMARY_TDM_TX_2;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ return AFE_PORT_ID_PRIMARY_TDM_RX_3;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ return AFE_PORT_ID_PRIMARY_TDM_TX_3;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ return AFE_PORT_ID_PRIMARY_TDM_RX_4;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ return AFE_PORT_ID_PRIMARY_TDM_TX_4;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ return AFE_PORT_ID_PRIMARY_TDM_RX_5;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ return AFE_PORT_ID_PRIMARY_TDM_TX_5;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ return AFE_PORT_ID_PRIMARY_TDM_RX_6;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ return AFE_PORT_ID_PRIMARY_TDM_TX_6;
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ return AFE_PORT_ID_PRIMARY_TDM_RX_7;
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ return AFE_PORT_ID_PRIMARY_TDM_TX_7;
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ return AFE_PORT_ID_SECONDARY_TDM_RX;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ return AFE_PORT_ID_SECONDARY_TDM_TX;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ return AFE_PORT_ID_SECONDARY_TDM_RX_1;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ return AFE_PORT_ID_SECONDARY_TDM_TX_1;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ return AFE_PORT_ID_SECONDARY_TDM_RX_2;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ return AFE_PORT_ID_SECONDARY_TDM_TX_2;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ return AFE_PORT_ID_SECONDARY_TDM_RX_3;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ return AFE_PORT_ID_SECONDARY_TDM_TX_3;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ return AFE_PORT_ID_SECONDARY_TDM_RX_4;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ return AFE_PORT_ID_SECONDARY_TDM_TX_4;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ return AFE_PORT_ID_SECONDARY_TDM_RX_5;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ return AFE_PORT_ID_SECONDARY_TDM_TX_5;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ return AFE_PORT_ID_SECONDARY_TDM_RX_6;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ return AFE_PORT_ID_SECONDARY_TDM_TX_6;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ return AFE_PORT_ID_SECONDARY_TDM_RX_7;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ return AFE_PORT_ID_SECONDARY_TDM_TX_7;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ return AFE_PORT_ID_TERTIARY_TDM_RX;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ return AFE_PORT_ID_TERTIARY_TDM_TX;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ return AFE_PORT_ID_TERTIARY_TDM_RX_1;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ return AFE_PORT_ID_TERTIARY_TDM_TX_1;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ return AFE_PORT_ID_TERTIARY_TDM_RX_2;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ return AFE_PORT_ID_TERTIARY_TDM_TX_2;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ return AFE_PORT_ID_TERTIARY_TDM_RX_3;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ return AFE_PORT_ID_TERTIARY_TDM_TX_3;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ return AFE_PORT_ID_TERTIARY_TDM_RX_4;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ return AFE_PORT_ID_TERTIARY_TDM_TX_4;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ return AFE_PORT_ID_TERTIARY_TDM_RX_5;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ return AFE_PORT_ID_TERTIARY_TDM_TX_5;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ return AFE_PORT_ID_TERTIARY_TDM_RX_6;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ return AFE_PORT_ID_TERTIARY_TDM_TX_6;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ return AFE_PORT_ID_TERTIARY_TDM_RX_7;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ return AFE_PORT_ID_TERTIARY_TDM_TX_7;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ return AFE_PORT_ID_QUATERNARY_TDM_RX;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ return AFE_PORT_ID_QUATERNARY_TDM_TX;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ return AFE_PORT_ID_QUATERNARY_TDM_RX_1;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ return AFE_PORT_ID_QUATERNARY_TDM_TX_1;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ return AFE_PORT_ID_QUATERNARY_TDM_RX_2;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ return AFE_PORT_ID_QUATERNARY_TDM_TX_2;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ return AFE_PORT_ID_QUATERNARY_TDM_RX_3;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ return AFE_PORT_ID_QUATERNARY_TDM_TX_3;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ return AFE_PORT_ID_QUATERNARY_TDM_RX_4;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ return AFE_PORT_ID_QUATERNARY_TDM_TX_4;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ return AFE_PORT_ID_QUATERNARY_TDM_RX_5;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ return AFE_PORT_ID_QUATERNARY_TDM_TX_5;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ return AFE_PORT_ID_QUATERNARY_TDM_RX_6;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ return AFE_PORT_ID_QUATERNARY_TDM_TX_6;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ return AFE_PORT_ID_QUATERNARY_TDM_RX_7;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ return AFE_PORT_ID_QUATERNARY_TDM_TX_7;
+ case AFE_PORT_ID_SENARY_MI2S_TX:
+ return AFE_PORT_ID_SENARY_MI2S_TX;
+ case AFE_PORT_ID_USB_RX:
+ return AFE_PORT_ID_USB_RX;
+ case AFE_PORT_ID_USB_TX:
+ return AFE_PORT_ID_USB_TX;
+ case AFE_PORT_ID_INT0_MI2S_RX:
+ return AFE_PORT_ID_INT0_MI2S_RX;
+ case AFE_PORT_ID_INT0_MI2S_TX:
+ return AFE_PORT_ID_INT0_MI2S_TX;
+ case AFE_PORT_ID_INT1_MI2S_RX:
+ return AFE_PORT_ID_INT1_MI2S_RX;
+ case AFE_PORT_ID_INT1_MI2S_TX:
+ return AFE_PORT_ID_INT1_MI2S_TX;
+ case AFE_PORT_ID_INT2_MI2S_RX:
+ return AFE_PORT_ID_INT2_MI2S_RX;
+ case AFE_PORT_ID_INT2_MI2S_TX:
+ return AFE_PORT_ID_INT2_MI2S_TX;
+ case AFE_PORT_ID_INT3_MI2S_RX:
+ return AFE_PORT_ID_INT3_MI2S_RX;
+ case AFE_PORT_ID_INT3_MI2S_TX:
+ return AFE_PORT_ID_INT3_MI2S_TX;
+ case AFE_PORT_ID_INT4_MI2S_RX:
+ return AFE_PORT_ID_INT4_MI2S_RX;
+ case AFE_PORT_ID_INT4_MI2S_TX:
+ return AFE_PORT_ID_INT4_MI2S_TX;
+ case AFE_PORT_ID_INT5_MI2S_RX:
+ return AFE_PORT_ID_INT5_MI2S_RX;
+ case AFE_PORT_ID_INT5_MI2S_TX:
+ return AFE_PORT_ID_INT5_MI2S_TX;
+ case AFE_PORT_ID_INT6_MI2S_RX:
+ return AFE_PORT_ID_INT6_MI2S_RX;
+ case AFE_PORT_ID_INT6_MI2S_TX:
+ return AFE_PORT_ID_INT6_MI2S_TX;
+ case RT_PROXY_PORT_002_RX:
+ return RT_PROXY_PORT_002_RX;
+ case RT_PROXY_PORT_002_TX:
+ return RT_PROXY_PORT_002_TX;
+ default:
+ pr_warn("%s: Invalid port_id %d\n", __func__, port_id);
+ return -EINVAL;
+ }
+}
+int q6audio_convert_virtual_to_portid(u16 port_id)
+{
+ int ret;
+
+ /* if port_id is virtual, convert to physical..
+ * if port_id is already physical, return physical
+ */
+ if (q6audio_validate_port(port_id) < 0) {
+ if (port_id == RT_PROXY_DAI_001_RX ||
+ port_id == RT_PROXY_DAI_001_TX ||
+ port_id == RT_PROXY_DAI_002_RX ||
+ port_id == RT_PROXY_DAI_002_TX)
+ ret = VIRTUAL_ID_TO_PORTID(port_id);
+ else
+ ret = -EINVAL;
+ } else
+ ret = port_id;
+
+ return ret;
+}
+
+int q6audio_is_digital_pcm_interface(u16 port_id)
+{
+ int ret = 0;
+
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ case AFE_PORT_ID_SECONDARY_PCM_RX:
+ case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case MI2S_RX:
+ case MI2S_TX:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX:
+ case AUDIO_PORT_ID_I2S_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ case AFE_PORT_ID_SENARY_MI2S_TX:
+ case AFE_PORT_ID_INT0_MI2S_RX:
+ case AFE_PORT_ID_INT0_MI2S_TX:
+ case AFE_PORT_ID_INT1_MI2S_RX:
+ case AFE_PORT_ID_INT1_MI2S_TX:
+ case AFE_PORT_ID_INT2_MI2S_RX:
+ case AFE_PORT_ID_INT2_MI2S_TX:
+ case AFE_PORT_ID_INT3_MI2S_RX:
+ case AFE_PORT_ID_INT3_MI2S_TX:
+ case AFE_PORT_ID_INT4_MI2S_RX:
+ case AFE_PORT_ID_INT4_MI2S_TX:
+ case AFE_PORT_ID_INT5_MI2S_RX:
+ case AFE_PORT_ID_INT5_MI2S_TX:
+ case AFE_PORT_ID_INT6_MI2S_RX:
+ case AFE_PORT_ID_INT6_MI2S_TX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int q6audio_validate_port(u16 port_id)
+{
+ int ret;
+
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ case AFE_PORT_ID_SECONDARY_PCM_RX:
+ case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case MI2S_RX:
+ case MI2S_TX:
+ case HDMI_RX:
+ case DISPLAY_PORT_RX:
+ case RSVD_2:
+ case RSVD_3:
+ case DIGI_MIC_TX:
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ case VOICE_PLAYBACK_TX:
+ case VOICE2_PLAYBACK_TX:
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_RX:
+ case SLIMBUS_3_TX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_4_TX:
+ case SLIMBUS_5_RX:
+ case SLIMBUS_5_TX:
+ case SLIMBUS_6_RX:
+ case SLIMBUS_6_TX:
+ case SLIMBUS_7_RX:
+ case SLIMBUS_7_TX:
+ case SLIMBUS_8_RX:
+ case SLIMBUS_8_TX:
+ case INT_BT_SCO_RX:
+ case INT_BT_SCO_TX:
+ case INT_BT_A2DP_RX:
+ case INT_FM_RX:
+ case INT_FM_TX:
+ case RT_PROXY_PORT_001_RX:
+ case RT_PROXY_PORT_001_TX:
+ case AFE_PORT_ID_PRIMARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_MI2S_TX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX:
+ case AFE_PORT_ID_SPDIF_RX:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+ case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+ case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ case AFE_PORT_ID_SENARY_MI2S_TX:
+ case AFE_PORT_ID_USB_RX:
+ case AFE_PORT_ID_USB_TX:
+ case AFE_PORT_ID_INT0_MI2S_RX:
+ case AFE_PORT_ID_INT0_MI2S_TX:
+ case AFE_PORT_ID_INT1_MI2S_RX:
+ case AFE_PORT_ID_INT1_MI2S_TX:
+ case AFE_PORT_ID_INT2_MI2S_RX:
+ case AFE_PORT_ID_INT2_MI2S_TX:
+ case AFE_PORT_ID_INT3_MI2S_RX:
+ case AFE_PORT_ID_INT3_MI2S_TX:
+ case AFE_PORT_ID_INT4_MI2S_RX:
+ case AFE_PORT_ID_INT4_MI2S_TX:
+ case AFE_PORT_ID_INT5_MI2S_RX:
+ case AFE_PORT_ID_INT5_MI2S_TX:
+ case AFE_PORT_ID_INT6_MI2S_RX:
+ case AFE_PORT_ID_INT6_MI2S_TX:
+ case AFE_PORT_ID_MULTICHAN_HDMI_RX:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_RX_4:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_1:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_2:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_3:
+ case AFE_PORT_ID_SECONDARY_MI2S_TX_4:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX_4:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_1:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_2:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_3:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX_4:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX_4:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_1:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_2:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_3:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX_4:
+ case RT_PROXY_PORT_002_RX:
+ case RT_PROXY_PORT_002_TX:
+ {
+ ret = 0;
+ break;
+ }
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
diff --git a/sound/soc/msm/qdsp6v2/q6common.c b/sound/soc/msm/qdsp6v2/q6common.c
new file mode 100644
index 000000000000..88e9af1cb86b
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6common.c
@@ -0,0 +1,85 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <sound/q6common.h>
+
+struct q6common_ctl {
+ bool instance_id_supported;
+};
+
+static struct q6common_ctl common;
+
+void q6common_update_instance_id_support(bool supported)
+{
+ common.instance_id_supported = supported;
+}
+EXPORT_SYMBOL(q6common_update_instance_id_support);
+
+bool q6common_is_instance_id_supported(void)
+{
+ return common.instance_id_supported;
+}
+EXPORT_SYMBOL(q6common_is_instance_id_supported);
+
+int q6common_pack_pp_params(u8 *dest, struct param_hdr_v3 *v3_hdr,
+ u8 *param_data, u32 *total_size)
+{
+ struct param_hdr_v1 *v1_hdr = NULL;
+ u32 packed_size = 0;
+ u32 param_size = 0;
+ bool iid_supported = q6common_is_instance_id_supported();
+
+ if (dest == NULL) {
+ pr_err("%s: Received NULL pointer for destination\n", __func__);
+ return -EINVAL;
+ } else if (v3_hdr == NULL) {
+ pr_err("%s: Received NULL pointer for header\n", __func__);
+ return -EINVAL;
+ } else if (total_size == NULL) {
+ pr_err("%s: Received NULL pointer for total size\n", __func__);
+ return -EINVAL;
+ }
+
+ param_size = v3_hdr->param_size;
+ packed_size = iid_supported ? sizeof(struct param_hdr_v3) :
+ sizeof(struct param_hdr_v1);
+
+ if (iid_supported) {
+ memcpy(dest, v3_hdr, packed_size);
+ } else {
+ v1_hdr = (struct param_hdr_v1 *) dest;
+ v1_hdr->module_id = v3_hdr->module_id;
+ v1_hdr->param_id = v3_hdr->param_id;
+
+ if (param_size > U16_MAX) {
+ pr_err("%s: Invalid param size for V1 %d\n", __func__,
+ param_size);
+ return -EINVAL;
+ }
+ v1_hdr->param_size = param_size;
+ v1_hdr->reserved = 0;
+ }
+
+ /*
+ * Make param_data optional for cases when there is no data
+ * present as in some set cases and all get cases.
+ */
+ if (param_data != NULL) {
+ memcpy(dest + packed_size, param_data, param_size);
+ packed_size += param_size;
+ }
+
+ *total_size = packed_size;
+
+ return 0;
+}
+EXPORT_SYMBOL(q6common_pack_pp_params);
diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/sound/soc/msm/qdsp6v2/q6core.c
new file mode 100644
index 000000000000..7ac3dcf6281a
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6core.c
@@ -0,0 +1,1277 @@
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/qdsp6v2/apr.h>
+#include <soc/qcom/smd.h>
+#include <sound/q6core.h>
+#include <sound/audio_cal_utils.h>
+#include <sound/adsp_err.h>
+
+#define TIMEOUT_MS 1000
+/*
+ * AVS bring up in the modem is optimitized for the new
+ * Sub System Restart design and 100 milliseconds timeout
+ * is sufficient to make sure the Q6 will be ready.
+ */
+#define Q6_READY_TIMEOUT_MS 100
+
+enum {
+ META_CAL,
+ CUST_TOP_CAL,
+ CORE_MAX_CAL
+};
+
+enum ver_query_status {
+ VER_QUERY_UNATTEMPTED,
+ VER_QUERY_UNSUPPORTED,
+ VER_QUERY_SUPPORTED
+};
+
+struct q6core_avcs_ver_info {
+ enum ver_query_status status;
+ struct avcs_fwk_ver_info *ver_info;
+};
+
+struct q6core_str {
+ struct apr_svc *core_handle_q;
+ wait_queue_head_t bus_bw_req_wait;
+ wait_queue_head_t cmd_req_wait;
+ wait_queue_head_t avcs_fwk_ver_req_wait;
+ u32 bus_bw_resp_received;
+ enum cmd_flags {
+ FLAG_NONE,
+ FLAG_CMDRSP_LICENSE_RESULT,
+ FLAG_AVCS_GET_VERSIONS_RESULT,
+ } cmd_resp_received_flag;
+ u32 avcs_fwk_ver_resp_received;
+ struct mutex cmd_lock;
+ struct mutex ver_lock;
+ union {
+ struct avcs_cmdrsp_get_license_validation_result
+ cmdrsp_license_result;
+ } cmd_resp_payload;
+ u32 param;
+ struct cal_type_data *cal_data[CORE_MAX_CAL];
+ uint32_t mem_map_cal_handle;
+ int32_t adsp_status;
+ struct q6core_avcs_ver_info q6core_avcs_ver_info;
+ u32 q6_core_avs_version;
+};
+
+static struct q6core_str q6core_lcl;
+
+struct generic_get_data_ {
+ int valid;
+ int size_in_ints;
+ int ints[];
+};
+static struct generic_get_data_ *generic_get_data;
+
+static int parse_fwk_version_info(uint32_t *payload, uint16_t payload_size)
+{
+ size_t ver_size;
+ int num_services;
+
+ pr_debug("%s: Payload info num services %d\n",
+ __func__, payload[4]);
+
+ /*
+ * payload1[4] is the number of services running on DSP
+ * Based on this info, we copy the payload into core
+ * avcs version info structure.
+ */
+ if (payload_size < 5 * sizeof(uint32_t)) {
+ pr_err("%s: payload has invalid size %d\n",
+ __func__, payload_size);
+ return -EINVAL;
+ }
+ num_services = payload[4];
+ if (num_services > VSS_MAX_AVCS_NUM_SERVICES) {
+ pr_err("%s: num_services: %d greater than max services: %d\n",
+ __func__, num_services, VSS_MAX_AVCS_NUM_SERVICES);
+ return -EINVAL;
+ }
+
+ /*
+ * Dynamically allocate memory for all
+ * the services based on num_services
+ */
+ ver_size = sizeof(struct avcs_get_fwk_version) +
+ num_services * sizeof(struct avs_svc_api_info);
+
+ if (payload_size < ver_size) {
+ pr_err("%s: payload has invalid size %d, expected size %zu\n",
+ __func__, payload_size, ver_size);
+ return -EINVAL;
+ }
+ q6core_lcl.q6core_avcs_ver_info.ver_info =
+ kzalloc(ver_size, GFP_ATOMIC);
+ if (q6core_lcl.q6core_avcs_ver_info.ver_info == NULL)
+ return -ENOMEM;
+
+ memcpy(q6core_lcl.q6core_avcs_ver_info.ver_info, (uint8_t *) payload,
+ ver_size);
+ return 0;
+}
+
+static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv)
+{
+ uint32_t *payload1;
+ int ret = 0;
+
+ if (data == NULL) {
+ pr_err("%s: data argument is null\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: core msg: payload len = %u, apr resp opcode = 0x%x\n",
+ __func__,
+ data->payload_size, data->opcode);
+
+ switch (data->opcode) {
+
+ case APR_BASIC_RSP_RESULT:{
+
+ if (data->payload_size == 0) {
+ pr_err("%s: APR_BASIC_RSP_RESULT No Payload ",
+ __func__);
+ return 0;
+ }
+
+ payload1 = data->payload;
+
+ if (data->payload_size < 2 * sizeof(uint32_t)) {
+ pr_err("%s: payload has invalid size %d\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
+
+ switch (payload1[0]) {
+
+ case AVCS_CMD_SHARED_MEM_UNMAP_REGIONS:
+ pr_debug("%s: Cmd = AVCS_CMD_SHARED_MEM_UNMAP_REGIONS status[0x%x]\n",
+ __func__, payload1[1]);
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
+ case AVCS_CMD_SHARED_MEM_MAP_REGIONS:
+ pr_debug("%s: Cmd = AVCS_CMD_SHARED_MEM_MAP_REGIONS status[0x%x]\n",
+ __func__, payload1[1]);
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
+ case AVCS_CMD_REGISTER_TOPOLOGIES:
+ pr_debug("%s: Cmd = AVCS_CMD_REGISTER_TOPOLOGIES status[0x%x]\n",
+ __func__, payload1[1]);
+ /* -ADSP status to match Linux error standard */
+ q6core_lcl.adsp_status = -payload1[1];
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
+ case AVCS_CMD_DEREGISTER_TOPOLOGIES:
+ pr_debug("%s: Cmd = AVCS_CMD_DEREGISTER_TOPOLOGIES status[0x%x]\n",
+ __func__, payload1[1]);
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
+ case AVCS_CMD_ADD_POOL_PAGES:
+ pr_debug("%s: Cmd = AVCS_CMD_ADD_POOL_PAGES status[0x%x]\n",
+ __func__, payload1[1]);
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
+ case AVCS_CMD_REMOVE_POOL_PAGES:
+ pr_debug("%s: Cmd = AVCS_CMD_REMOVE_POOL_PAGES status[0x%x]\n",
+ __func__, payload1[1]);
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
+ case AVCS_CMD_GET_FWK_VERSION:
+ pr_debug("%s: Cmd = AVCS_CMD_GET_FWK_VERSION status[%s]\n",
+ __func__, adsp_err_get_err_str(payload1[1]));
+ /* ADSP status to match Linux error standard */
+ q6core_lcl.adsp_status = -payload1[1];
+ if (payload1[1] == ADSP_EUNSUPPORTED)
+ q6core_lcl.q6core_avcs_ver_info.status =
+ VER_QUERY_UNSUPPORTED;
+ q6core_lcl.avcs_fwk_ver_resp_received = 1;
+ wake_up(&q6core_lcl.avcs_fwk_ver_req_wait);
+ break;
+ default:
+ pr_err("%s: Invalid cmd rsp[0x%x][0x%x] opcode %d\n",
+ __func__,
+ payload1[0], payload1[1], data->opcode);
+ break;
+ }
+ break;
+ }
+
+ case RESET_EVENTS:{
+ pr_debug("%s: Reset event received in Core service\n",
+ __func__);
+ apr_reset(q6core_lcl.core_handle_q);
+ q6core_lcl.core_handle_q = NULL;
+ break;
+ }
+ case AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS:
+ if (data->payload_size < sizeof(uint32_t)) {
+ pr_err("%s: payload has invalid size %d\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
+ payload1 = data->payload;
+ pr_debug("%s: AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS handle %d\n",
+ __func__, payload1[0]);
+ q6core_lcl.mem_map_cal_handle = payload1[0];
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
+ case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
+ if (data->payload_size < sizeof(uint32_t)) {
+ pr_err("%s: payload has invalid size %d\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
+ payload1 = data->payload;
+ q6core_lcl.param = payload1[0];
+ pr_debug("%s: Received ADSP get state response 0x%x\n",
+ __func__, q6core_lcl.param);
+ /* ensure .param is updated prior to .bus_bw_resp_received */
+ wmb();
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
+ case AVCS_GET_VERSIONS_RSP:
+ if (data->payload_size < 4 * sizeof(uint32_t)) {
+ pr_err("%s: payload has invalid size %d\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
+ payload1 = data->payload;
+ pr_debug("%s: Received ADSP version response[3]0x%x\n",
+ __func__, payload1[3]);
+ q6core_lcl.cmd_resp_received_flag =
+ FLAG_AVCS_GET_VERSIONS_RESULT;
+ if (payload1[3] == AVCS_CMDRSP_Q6_ID_2_6) {
+ q6core_lcl.q6_core_avs_version = Q6_SUBSYS_AVS2_6;
+ pr_debug("%s: Received ADSP version as 2.6\n",
+ __func__);
+ } else if (payload1[3] == AVCS_CMDRSP_Q6_ID_2_7) {
+ q6core_lcl.q6_core_avs_version = Q6_SUBSYS_AVS2_7;
+ pr_debug("%s: Received ADSP version as 2.7\n",
+ __func__);
+ } else if (payload1[3] == AVCS_CMDRSP_Q6_ID_2_8) {
+ q6core_lcl.q6_core_avs_version = Q6_SUBSYS_AVS2_8;
+ pr_info("%s: Received ADSP version as 2.8\n",
+ __func__);
+ } else {
+ pr_err("%s: ADSP version is neither 2.6 nor 2.7\n",
+ __func__);
+ q6core_lcl.q6_core_avs_version = Q6_SUBSYS_INVALID;
+ }
+ wake_up(&q6core_lcl.cmd_req_wait);
+ break;
+
+ case AVCS_CMDRSP_GET_LICENSE_VALIDATION_RESULT:
+ if (data->payload_size < sizeof(uint32_t)) {
+ pr_err("%s: payload has invalid size %d\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
+ payload1 = data->payload;
+ pr_debug("%s: cmd = LICENSE_VALIDATION_RESULT, result = 0x%x\n",
+ __func__, payload1[0]);
+ q6core_lcl.cmd_resp_payload.cmdrsp_license_result.result
+ = payload1[0];
+ q6core_lcl.cmd_resp_received_flag = FLAG_CMDRSP_LICENSE_RESULT;
+ wake_up(&q6core_lcl.cmd_req_wait);
+ break;
+ case AVCS_CMDRSP_GET_FWK_VERSION:
+ pr_debug("%s: Received AVCS_CMDRSP_GET_FWK_VERSION\n",
+ __func__);
+ payload1 = data->payload;
+ ret = parse_fwk_version_info(payload1, data->payload_size);
+ if (ret < 0) {
+ q6core_lcl.adsp_status = ret;
+ pr_err("%s: Failed to parse payload:%d\n",
+ __func__, ret);
+ } else {
+ q6core_lcl.q6core_avcs_ver_info.status =
+ VER_QUERY_SUPPORTED;
+ }
+ q6core_lcl.avcs_fwk_ver_resp_received = 1;
+ wake_up(&q6core_lcl.avcs_fwk_ver_req_wait);
+ break;
+ default:
+ pr_err("%s: Message id from adsp core svc: 0x%x\n",
+ __func__, data->opcode);
+ if (generic_get_data) {
+ generic_get_data->valid = 1;
+ generic_get_data->size_in_ints =
+ data->payload_size/sizeof(int);
+ pr_debug("callback size = %i\n",
+ data->payload_size);
+ memcpy(generic_get_data->ints, data->payload,
+ data->payload_size);
+ q6core_lcl.bus_bw_resp_received = 1;
+ wake_up(&q6core_lcl.bus_bw_req_wait);
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+void ocm_core_open(void)
+{
+ if (q6core_lcl.core_handle_q == NULL)
+ q6core_lcl.core_handle_q = apr_register("ADSP", "CORE",
+ aprv2_core_fn_q, 0xFFFFFFFF, NULL);
+ pr_debug("%s: Open_q %pK\n", __func__, q6core_lcl.core_handle_q);
+ if (q6core_lcl.core_handle_q == NULL)
+ pr_err("%s: Unable to register CORE\n", __func__);
+}
+
+struct cal_block_data *cal_utils_get_cal_block_by_key(
+ struct cal_type_data *cal_type, uint32_t key)
+{
+ struct list_head *ptr, *next;
+ struct cal_block_data *cal_block = NULL;
+ struct audio_cal_info_metainfo *metainfo;
+
+ list_for_each_safe(ptr, next,
+ &cal_type->cal_blocks) {
+
+ cal_block = list_entry(ptr,
+ struct cal_block_data, list);
+ metainfo = (struct audio_cal_info_metainfo *)
+ cal_block->cal_info;
+ if (metainfo->nKey != key) {
+ pr_debug("%s: metainfo key mismatch!!! found:%x, needed:%x\n",
+ __func__, metainfo->nKey, key);
+ } else {
+ pr_debug("%s: metainfo key match found", __func__);
+ return cal_block;
+ }
+ }
+ return NULL;
+}
+
+static int q6core_send_get_avcs_fwk_ver_cmd(void)
+{
+ struct apr_hdr avcs_ver_cmd;
+ int ret;
+
+ avcs_ver_cmd.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ avcs_ver_cmd.pkt_size = sizeof(struct apr_hdr);
+ avcs_ver_cmd.src_port = 0;
+ avcs_ver_cmd.dest_port = 0;
+ avcs_ver_cmd.token = 0;
+ avcs_ver_cmd.opcode = AVCS_CMD_GET_FWK_VERSION;
+
+ q6core_lcl.adsp_status = 0;
+ q6core_lcl.avcs_fwk_ver_resp_received = 0;
+
+ ret = apr_send_pkt(q6core_lcl.core_handle_q,
+ (uint32_t *) &avcs_ver_cmd);
+ if (ret < 0) {
+ pr_err("%s: failed to send apr packet, ret=%d\n", __func__,
+ ret);
+ goto done;
+ }
+
+ ret = wait_event_timeout(q6core_lcl.avcs_fwk_ver_req_wait,
+ (q6core_lcl.avcs_fwk_ver_resp_received == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout for AVCS fwk version info\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (q6core_lcl.adsp_status < 0) {
+ /*
+ * adsp_err_get_err_str expects a positive value but we store
+ * the DSP error as negative to match the Linux error standard.
+ * Pass in the negated value so adsp_err_get_err_str returns
+ * the correct string.
+ */
+ pr_err("%s: DSP returned error[%s]\n", __func__,
+ adsp_err_get_err_str(-q6core_lcl.adsp_status));
+ ret = adsp_err_get_lnx_err_code(q6core_lcl.adsp_status);
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ return ret;
+}
+
+int q6core_get_service_version(uint32_t service_id,
+ struct avcs_fwk_ver_info *ver_info,
+ size_t size)
+{
+ struct avcs_fwk_ver_info *cached_ver_info = NULL;
+ int i;
+ uint32_t num_services;
+ size_t ver_size;
+ int ret;
+
+ if (ver_info == NULL) {
+ pr_err("%s: ver_info is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = q6core_get_fwk_version_size(service_id);
+ if (ret < 0) {
+ pr_err("%s: Failed to get service size for service id %d with error %d\n",
+ __func__, service_id, ret);
+ return ret;
+ }
+
+ ver_size = ret;
+ if (ver_size != size) {
+ pr_err("%s: Expected size %zu and provided size %zu do not match\n",
+ __func__, ver_size, size);
+ return -EINVAL;
+ }
+
+ cached_ver_info = q6core_lcl.q6core_avcs_ver_info.ver_info;
+ num_services = cached_ver_info->avcs_fwk_version.num_services;
+
+ if (service_id == AVCS_SERVICE_ID_ALL) {
+ memcpy(ver_info, cached_ver_info, ver_size);
+ return 0;
+ }
+
+ ver_info->avcs_fwk_version = cached_ver_info->avcs_fwk_version;
+ for (i = 0; i < num_services; i++) {
+ if (cached_ver_info->services[i].service_id == service_id) {
+ ver_info->services[0] = cached_ver_info->services[i];
+ return 0;
+ }
+ }
+ pr_err("%s: No service matching service ID %d\n", __func__, service_id);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(q6core_get_service_version);
+
+size_t q6core_get_fwk_version_size(uint32_t service_id)
+{
+ int ret = 0;
+ uint32_t num_services;
+
+ mutex_lock(&(q6core_lcl.ver_lock));
+ pr_debug("%s: q6core_avcs_ver_info.status(%d)\n", __func__,
+ q6core_lcl.q6core_avcs_ver_info.status);
+
+ switch (q6core_lcl.q6core_avcs_ver_info.status) {
+ case VER_QUERY_SUPPORTED:
+ pr_debug("%s: AVCS FWK version query already attempted\n",
+ __func__);
+ break;
+ case VER_QUERY_UNSUPPORTED:
+ ret = -EOPNOTSUPP;
+ break;
+ case VER_QUERY_UNATTEMPTED:
+ pr_debug("%s: Attempting AVCS FWK version query\n", __func__);
+ if (q6core_is_adsp_ready()) {
+ ret = q6core_send_get_avcs_fwk_ver_cmd();
+ } else {
+ pr_err("%s: ADSP is not ready to query version\n",
+ __func__);
+ ret = -ENODEV;
+ }
+ break;
+ default:
+ pr_err("%s: Invalid version query status %d\n", __func__,
+ q6core_lcl.q6core_avcs_ver_info.status);
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&(q6core_lcl.ver_lock));
+
+ if (ret)
+ goto done;
+
+ if (q6core_lcl.q6core_avcs_ver_info.ver_info != NULL) {
+ num_services = q6core_lcl.q6core_avcs_ver_info.ver_info
+ ->avcs_fwk_version.num_services;
+ } else {
+ pr_err("%s: ver_info is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = sizeof(struct avcs_get_fwk_version);
+ if (service_id == AVCS_SERVICE_ID_ALL)
+ ret += num_services * sizeof(struct avs_svc_api_info);
+ else
+ ret += sizeof(struct avs_svc_api_info);
+done:
+ return ret;
+}
+EXPORT_SYMBOL(q6core_get_fwk_version_size);
+
+int32_t core_set_license(uint32_t key, uint32_t module_id)
+{
+ struct avcs_cmd_set_license *cmd_setl = NULL;
+ struct cal_block_data *cal_block = NULL;
+ int rc = 0, packet_size = 0;
+
+ pr_debug("%s: key:0x%x, id:0x%x\n", __func__, key, module_id);
+
+ mutex_lock(&(q6core_lcl.cmd_lock));
+ if (q6core_lcl.cal_data[META_CAL] == NULL) {
+ pr_err("%s: cal_data not initialized yet!!\n", __func__);
+ rc = -EINVAL;
+ goto cmd_unlock;
+ }
+
+ mutex_lock(&((q6core_lcl.cal_data[META_CAL])->lock));
+ cal_block = cal_utils_get_cal_block_by_key(
+ q6core_lcl.cal_data[META_CAL], key);
+ if (cal_block == NULL ||
+ cal_block->cal_data.kvaddr == NULL ||
+ cal_block->cal_data.size <= 0) {
+ pr_err("%s: Invalid cal block to send", __func__);
+ rc = -EINVAL;
+ goto cal_data_unlock;
+ }
+
+ packet_size = sizeof(struct avcs_cmd_set_license) +
+ cal_block->cal_data.size;
+ /*round up total packet_size to next 4 byte boundary*/
+ packet_size = ((packet_size + 0x3)>>2)<<2;
+
+ cmd_setl = kzalloc(packet_size, GFP_KERNEL);
+ if (cmd_setl == NULL) {
+ pr_err("%s: kzalloc for cmd_set_license failed for size %d\n",
+ __func__, packet_size);
+ rc = -ENOMEM;
+ goto cal_data_unlock;
+ }
+
+ ocm_core_open();
+ if (q6core_lcl.core_handle_q == NULL) {
+ pr_err("%s: apr registration for CORE failed\n", __func__);
+ rc = -ENODEV;
+ goto fail_cmd;
+ }
+
+ cmd_setl->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cmd_setl->hdr.pkt_size = packet_size;
+ cmd_setl->hdr.src_port = 0;
+ cmd_setl->hdr.dest_port = 0;
+ cmd_setl->hdr.token = 0;
+ cmd_setl->hdr.opcode = AVCS_CMD_SET_LICENSE;
+ cmd_setl->id = module_id;
+ cmd_setl->overwrite = 1;
+ cmd_setl->size = cal_block->cal_data.size;
+ memcpy((uint8_t *)cmd_setl + sizeof(struct avcs_cmd_set_license),
+ cal_block->cal_data.kvaddr,
+ cal_block->cal_data.size);
+ pr_info("%s: Set license opcode=0x%x, id =0x%x, size = %d\n",
+ __func__, cmd_setl->hdr.opcode,
+ cmd_setl->id, cmd_setl->size);
+ rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)cmd_setl);
+ if (rc < 0)
+ pr_err("%s: SET_LICENSE failed op[0x%x]rc[%d]\n",
+ __func__, cmd_setl->hdr.opcode, rc);
+
+fail_cmd:
+ kfree(cmd_setl);
+cal_data_unlock:
+ mutex_unlock(&((q6core_lcl.cal_data[META_CAL])->lock));
+cmd_unlock:
+ mutex_unlock(&(q6core_lcl.cmd_lock));
+
+ return rc;
+}
+
+int core_get_adsp_ver(void)
+{
+ struct avcs_cmd_get_version_result get_aver_cmd;
+ int ret = 0;
+
+ mutex_lock(&(q6core_lcl.cmd_lock));
+ ocm_core_open();
+ if (q6core_lcl.core_handle_q == NULL) {
+ pr_err("%s: apr registration for CORE failed\n", __func__);
+ ret = -ENODEV;
+ goto fail_cmd;
+ }
+
+ get_aver_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ get_aver_cmd.hdr.pkt_size = sizeof(get_aver_cmd);
+ get_aver_cmd.hdr.src_port = 0;
+ get_aver_cmd.hdr.dest_port = 0;
+ get_aver_cmd.hdr.token = 0;
+ get_aver_cmd.hdr.opcode = AVCS_GET_VERSIONS;
+
+ ret = apr_send_pkt(q6core_lcl.core_handle_q,
+ (uint32_t *) &get_aver_cmd);
+ if (ret < 0) {
+ pr_err("%s: Core get DSP version request failed, err %d\n",
+ __func__, ret);
+ ret = -EREMOTE;
+ goto fail_cmd;
+ }
+
+ q6core_lcl.cmd_resp_received_flag &= ~(FLAG_AVCS_GET_VERSIONS_RESULT);
+ mutex_unlock(&(q6core_lcl.cmd_lock));
+ ret = wait_event_timeout(q6core_lcl.cmd_req_wait,
+ (q6core_lcl.cmd_resp_received_flag ==
+ FLAG_AVCS_GET_VERSIONS_RESULT),
+ msecs_to_jiffies(TIMEOUT_MS));
+ mutex_lock(&(q6core_lcl.cmd_lock));
+ if (!ret) {
+ pr_err("%s: wait_event timeout for AVCS_GET_VERSIONS_RESULT\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ q6core_lcl.cmd_resp_received_flag &= ~(FLAG_AVCS_GET_VERSIONS_RESULT);
+
+fail_cmd:
+ if (ret < 0)
+ q6core_lcl.q6_core_avs_version = Q6_SUBSYS_INVALID;
+ mutex_unlock(&(q6core_lcl.cmd_lock));
+ return ret;
+}
+
+enum q6_subsys_image q6core_get_avs_version(void)
+{
+ if (q6core_lcl.q6_core_avs_version == 0)
+ core_get_adsp_ver();
+ return q6core_lcl.q6_core_avs_version;
+}
+
+int32_t core_get_license_status(uint32_t module_id)
+{
+ struct avcs_cmd_get_license_validation_result get_lvr_cmd;
+ int ret = 0;
+
+ pr_debug("%s: module_id 0x%x", __func__, module_id);
+
+ mutex_lock(&(q6core_lcl.cmd_lock));
+ ocm_core_open();
+ if (q6core_lcl.core_handle_q == NULL) {
+ pr_err("%s: apr registration for CORE failed\n", __func__);
+ ret = -ENODEV;
+ goto fail_cmd;
+ }
+
+ get_lvr_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ get_lvr_cmd.hdr.pkt_size =
+ sizeof(struct avcs_cmd_get_license_validation_result);
+
+ get_lvr_cmd.hdr.src_port = 0;
+ get_lvr_cmd.hdr.dest_port = 0;
+ get_lvr_cmd.hdr.token = 0;
+ get_lvr_cmd.hdr.opcode = AVCS_CMD_GET_LICENSE_VALIDATION_RESULT;
+ get_lvr_cmd.id = module_id;
+
+
+ ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) &get_lvr_cmd);
+ if (ret < 0) {
+ pr_err("%s: license_validation request failed, err %d\n",
+ __func__, ret);
+ ret = -EREMOTE;
+ goto fail_cmd;
+ }
+
+ q6core_lcl.cmd_resp_received_flag &= ~(FLAG_CMDRSP_LICENSE_RESULT);
+ mutex_unlock(&(q6core_lcl.cmd_lock));
+ ret = wait_event_timeout(q6core_lcl.cmd_req_wait,
+ (q6core_lcl.cmd_resp_received_flag ==
+ FLAG_CMDRSP_LICENSE_RESULT),
+ msecs_to_jiffies(TIMEOUT_MS));
+ mutex_lock(&(q6core_lcl.cmd_lock));
+ if (!ret) {
+ pr_err("%s: wait_event timeout for CMDRSP_LICENSE_RESULT\n",
+ __func__);
+ ret = -ETIME;
+ goto fail_cmd;
+ }
+ q6core_lcl.cmd_resp_received_flag &= ~(FLAG_CMDRSP_LICENSE_RESULT);
+ ret = q6core_lcl.cmd_resp_payload.cmdrsp_license_result.result;
+
+fail_cmd:
+ mutex_unlock(&(q6core_lcl.cmd_lock));
+ pr_info("%s: cmdrsp_license_result.result = 0x%x for module 0x%x\n",
+ __func__, ret, module_id);
+ return ret;
+}
+
+uint32_t core_set_dolby_manufacturer_id(int manufacturer_id)
+{
+ struct adsp_dolby_manufacturer_id payload;
+ int rc = 0;
+ pr_debug("%s: manufacturer_id :%d\n", __func__, manufacturer_id);
+ mutex_lock(&(q6core_lcl.cmd_lock));
+ ocm_core_open();
+ if (q6core_lcl.core_handle_q) {
+ payload.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ payload.hdr.pkt_size =
+ sizeof(struct adsp_dolby_manufacturer_id);
+ payload.hdr.src_port = 0;
+ payload.hdr.dest_port = 0;
+ payload.hdr.token = 0;
+ payload.hdr.opcode = ADSP_CMD_SET_DOLBY_MANUFACTURER_ID;
+ payload.manufacturer_id = manufacturer_id;
+ pr_debug("%s: Send Dolby security opcode=0x%x manufacturer ID = %d\n",
+ __func__,
+ payload.hdr.opcode, payload.manufacturer_id);
+ rc = apr_send_pkt(q6core_lcl.core_handle_q,
+ (uint32_t *)&payload);
+ if (rc < 0)
+ pr_err("%s: SET_DOLBY_MANUFACTURER_ID failed op[0x%x]rc[%d]\n",
+ __func__, payload.hdr.opcode, rc);
+ }
+ mutex_unlock(&(q6core_lcl.cmd_lock));
+ return rc;
+}
+
+int q6core_is_adsp_ready(void)
+{
+ int rc = 0;
+ int ret = false;
+ struct apr_hdr hdr;
+
+ pr_debug("%s: enter\n", __func__);
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0);
+ hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE;
+
+ mutex_lock(&(q6core_lcl.cmd_lock));
+ ocm_core_open();
+ if (q6core_lcl.core_handle_q) {
+ q6core_lcl.bus_bw_resp_received = 0;
+ rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&hdr);
+ if (rc < 0) {
+ pr_err("%s: Get ADSP state APR packet send event %d\n",
+ __func__, rc);
+ goto bail;
+ }
+
+ rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+ (q6core_lcl.bus_bw_resp_received == 1),
+ msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
+ if (rc > 0 && q6core_lcl.bus_bw_resp_received) {
+ /* ensure to read updated param by callback thread */
+ rmb();
+ ret = q6core_lcl.param;
+ }
+ }
+bail:
+ pr_debug("%s: leave, rc %d, adsp ready %d\n", __func__, rc, ret);
+ mutex_unlock(&(q6core_lcl.cmd_lock));
+ return ret;
+}
+
+
+static int q6core_map_memory_regions(phys_addr_t *buf_add, uint32_t mempool_id,
+ uint32_t *bufsz, uint32_t bufcnt, uint32_t *map_handle)
+{
+ struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+ struct avs_shared_map_region_payload *mregions = NULL;
+ void *mmap_region_cmd = NULL;
+ void *payload = NULL;
+ int ret = 0;
+ int i = 0;
+ int cmd_size = 0;
+
+ cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions)
+ + sizeof(struct avs_shared_map_region_payload)
+ * bufcnt;
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (mmap_region_cmd == NULL)
+ return -ENOMEM;
+
+ mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
+ mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mmap_regions->hdr.pkt_size = cmd_size;
+ mmap_regions->hdr.src_port = 0;
+ mmap_regions->hdr.dest_port = 0;
+ mmap_regions->hdr.token = 0;
+ mmap_regions->hdr.opcode = AVCS_CMD_SHARED_MEM_MAP_REGIONS;
+ mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL & 0x00ff;
+ mmap_regions->num_regions = bufcnt & 0x00ff;
+ mmap_regions->property_flag = 0x00;
+
+ payload = ((u8 *) mmap_region_cmd +
+ sizeof(struct avs_cmd_shared_mem_map_regions));
+ mregions = (struct avs_shared_map_region_payload *)payload;
+
+ for (i = 0; i < bufcnt; i++) {
+ mregions->shm_addr_lsw = lower_32_bits(buf_add[i]);
+ mregions->shm_addr_msw =
+ msm_audio_populate_upper_32_bits(buf_add[i]);
+ mregions->mem_size_bytes = bufsz[i];
+ ++mregions;
+ }
+
+ pr_debug("%s: sending memory map, addr %pK, size %d, bufcnt = %d\n",
+ __func__, buf_add, bufsz[0], mmap_regions->num_regions);
+
+ *map_handle = 0;
+ q6core_lcl.bus_bw_resp_received = 0;
+ ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)
+ mmap_regions);
+ if (ret < 0) {
+ pr_err("%s: mmap regions failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+ (q6core_lcl.bus_bw_resp_received == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: timeout. waited for memory map\n", __func__);
+ ret = -ETIME;
+ goto done;
+ }
+
+ *map_handle = q6core_lcl.mem_map_cal_handle;
+done:
+ kfree(mmap_region_cmd);
+ return ret;
+}
+
+static int q6core_memory_unmap_regions(uint32_t mem_map_handle)
+{
+ struct avs_cmd_shared_mem_unmap_regions unmap_regions;
+ int ret = 0;
+
+ memset(&unmap_regions, 0, sizeof(unmap_regions));
+ unmap_regions.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ unmap_regions.hdr.pkt_size = sizeof(unmap_regions);
+ unmap_regions.hdr.src_svc = APR_SVC_ADSP_CORE;
+ unmap_regions.hdr.src_domain = APR_DOMAIN_APPS;
+ unmap_regions.hdr.src_port = 0;
+ unmap_regions.hdr.dest_svc = APR_SVC_ADSP_CORE;
+ unmap_regions.hdr.dest_domain = APR_DOMAIN_ADSP;
+ unmap_regions.hdr.dest_port = 0;
+ unmap_regions.hdr.token = 0;
+ unmap_regions.hdr.opcode = AVCS_CMD_SHARED_MEM_UNMAP_REGIONS;
+ unmap_regions.mem_map_handle = mem_map_handle;
+
+ q6core_lcl.bus_bw_resp_received = 0;
+
+ pr_debug("%s: unmap regions map handle %d\n",
+ __func__, mem_map_handle);
+
+ ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)
+ &unmap_regions);
+ if (ret < 0) {
+ pr_err("%s: unmap regions failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+ (q6core_lcl.bus_bw_resp_received == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: timeout. waited for memory_unmap\n",
+ __func__);
+ ret = -ETIME;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+int q6core_add_remove_pool_pages(ion_phys_addr_t buf_add, uint32_t bufsz,
+ uint32_t mempool_id, bool add_pages)
+{
+ struct avs_mem_assign_region mem_pool;
+ int ret = 0, sz;
+
+ if (add_pages)
+ mem_pool.hdr.opcode = AVCS_CMD_ADD_POOL_PAGES;
+ else
+ mem_pool.hdr.opcode = AVCS_CMD_REMOVE_POOL_PAGES;
+
+ /* get payload length */
+ sz = sizeof(struct avs_mem_assign_region);
+ mem_pool.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(sizeof(struct apr_hdr)),
+ APR_PKT_VER);
+ mem_pool.hdr.src_port = 0;
+ mem_pool.hdr.dest_port = 0;
+ mem_pool.hdr.token = 0;
+ mem_pool.hdr.pkt_size = sz;
+ mem_pool.pool_id = mempool_id;
+ mem_pool.size = bufsz;
+ mem_pool.addr_lsw = lower_32_bits(buf_add);
+ mem_pool.addr_msw = msm_audio_populate_upper_32_bits(buf_add);
+ pr_debug("%s: sending memory map, size %d\n",
+ __func__, bufsz);
+
+ q6core_lcl.bus_bw_resp_received = 0;
+ ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&mem_pool);
+ if (ret < 0) {
+ pr_err("%s: library map region failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+ (q6core_lcl.bus_bw_resp_received == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: timeout. waited for library memory map\n",
+ __func__);
+ ret = -ETIME;
+ goto done;
+ }
+ ret = 0;
+done:
+ return ret;
+}
+
+static int q6core_dereg_all_custom_topologies(void)
+{
+ int ret = 0;
+ struct avcs_cmd_deregister_topologies dereg_top;
+
+ memset(&dereg_top, 0, sizeof(dereg_top));
+ dereg_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ dereg_top.hdr.pkt_size = sizeof(dereg_top);
+ dereg_top.hdr.src_svc = APR_SVC_ADSP_CORE;
+ dereg_top.hdr.src_domain = APR_DOMAIN_APPS;
+ dereg_top.hdr.src_port = 0;
+ dereg_top.hdr.dest_svc = APR_SVC_ADSP_CORE;
+ dereg_top.hdr.dest_domain = APR_DOMAIN_ADSP;
+ dereg_top.hdr.dest_port = 0;
+ dereg_top.hdr.token = 0;
+ dereg_top.hdr.opcode = AVCS_CMD_DEREGISTER_TOPOLOGIES;
+ dereg_top.payload_addr_lsw = 0;
+ dereg_top.payload_addr_msw = 0;
+ dereg_top.mem_map_handle = 0;
+ dereg_top.payload_size = 0;
+ dereg_top.mode = AVCS_MODE_DEREGISTER_ALL_CUSTOM_TOPOLOGIES;
+
+ q6core_lcl.bus_bw_resp_received = 0;
+
+ pr_debug("%s: Deregister topologies mode %d\n",
+ __func__, dereg_top.mode);
+
+ ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) &dereg_top);
+ if (ret < 0) {
+ pr_err("%s: Deregister topologies failed %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+ (q6core_lcl.bus_bw_resp_received == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout for Deregister topologies\n",
+ __func__);
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int q6core_send_custom_topologies(void)
+{
+ int ret = 0;
+ int ret2 = 0;
+ struct cal_block_data *cal_block = NULL;
+ struct avcs_cmd_register_topologies reg_top;
+
+ if (!q6core_is_adsp_ready()) {
+ pr_err("%s: ADSP is not ready!\n", __func__);
+ return -ENODEV;
+ }
+
+ memset(&reg_top, 0, sizeof(reg_top));
+ mutex_lock(&q6core_lcl.cal_data[CUST_TOP_CAL]->lock);
+ mutex_lock(&q6core_lcl.cmd_lock);
+
+ cal_block = cal_utils_get_only_cal_block(
+ q6core_lcl.cal_data[CUST_TOP_CAL]);
+ if (cal_block == NULL) {
+ pr_debug("%s: cal block is NULL!\n", __func__);
+ goto unlock;
+ }
+ if (cal_block->cal_data.size <= 0) {
+ pr_debug("%s: cal size is %zd not sending\n",
+ __func__, cal_block->cal_data.size);
+ goto unlock;
+ }
+
+ q6core_dereg_all_custom_topologies();
+
+ ret = q6core_map_memory_regions(&cal_block->cal_data.paddr, 0,
+ (uint32_t *)&cal_block->map_data.map_size, 1,
+ &cal_block->map_data.q6map_handle);
+ if (!ret) {
+ pr_err("%s: q6core_map_memory_regions failed\n", __func__);
+ goto unlock;
+ }
+
+ reg_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ reg_top.hdr.pkt_size = sizeof(reg_top);
+ reg_top.hdr.src_svc = APR_SVC_ADSP_CORE;
+ reg_top.hdr.src_domain = APR_DOMAIN_APPS;
+ reg_top.hdr.src_port = 0;
+ reg_top.hdr.dest_svc = APR_SVC_ADSP_CORE;
+ reg_top.hdr.dest_domain = APR_DOMAIN_ADSP;
+ reg_top.hdr.dest_port = 0;
+ reg_top.hdr.token = 0;
+ reg_top.hdr.opcode = AVCS_CMD_REGISTER_TOPOLOGIES;
+ reg_top.payload_addr_lsw =
+ lower_32_bits(cal_block->cal_data.paddr);
+ reg_top.payload_addr_msw =
+ msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+ reg_top.mem_map_handle = cal_block->map_data.q6map_handle;
+ reg_top.payload_size = cal_block->cal_data.size;
+
+ q6core_lcl.adsp_status = 0;
+ q6core_lcl.bus_bw_resp_received = 0;
+
+ pr_debug("%s: Register topologies addr %pK, size %zd, map handle %d\n",
+ __func__, &cal_block->cal_data.paddr, cal_block->cal_data.size,
+ cal_block->map_data.q6map_handle);
+
+ ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) &reg_top);
+ if (ret < 0) {
+ pr_err("%s: Register topologies failed %d\n",
+ __func__, ret);
+ goto unmap;
+ }
+
+ ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+ (q6core_lcl.bus_bw_resp_received == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout for Register topologies\n",
+ __func__);
+ goto unmap;
+ }
+
+ if (q6core_lcl.adsp_status < 0)
+ ret = q6core_lcl.adsp_status;
+unmap:
+ ret2 = q6core_memory_unmap_regions(cal_block->map_data.q6map_handle);
+ if (!ret2) {
+ pr_err("%s: q6core_memory_unmap_regions failed for map handle %d\n",
+ __func__, cal_block->map_data.q6map_handle);
+ ret = ret2;
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&q6core_lcl.cmd_lock);
+ mutex_unlock(&q6core_lcl.cal_data[CUST_TOP_CAL]->lock);
+
+ return ret;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+ int ret = -EINVAL;
+
+ switch (cal_type) {
+ case AUDIO_CORE_METAINFO_CAL_TYPE:
+ ret = META_CAL;
+ break;
+ case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE:
+ ret = CUST_TOP_CAL;
+ break;
+ default:
+ pr_err("%s: invalid cal type %d!\n", __func__, cal_type);
+ }
+ return ret;
+}
+
+static int q6core_alloc_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+
+ ret = cal_utils_alloc_cal(data_size, data,
+ q6core_lcl.cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int q6core_dealloc_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+
+ ret = cal_utils_dealloc_cal(data_size, data,
+ q6core_lcl.cal_data[cal_index]);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int q6core_set_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+
+ ret = cal_utils_set_cal(data_size, data,
+ q6core_lcl.cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ goto done;
+ }
+
+ if (cal_index == CUST_TOP_CAL)
+ ret = q6core_send_custom_topologies();
+done:
+ return ret;
+}
+
+static void q6core_delete_cal_data(void)
+{
+ pr_debug("%s:\n", __func__);
+
+ cal_utils_destroy_cal_types(CORE_MAX_CAL, q6core_lcl.cal_data);
+ return;
+}
+
+
+static int q6core_init_cal_data(void)
+{
+ int ret = 0;
+ struct cal_type_info cal_type_info[] = {
+ {{AUDIO_CORE_METAINFO_CAL_TYPE,
+ {q6core_alloc_cal, q6core_dealloc_cal, NULL,
+ q6core_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{CORE_CUSTOM_TOPOLOGIES_CAL_TYPE,
+ {q6core_alloc_cal, q6core_dealloc_cal, NULL,
+ q6core_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} }
+ };
+ pr_debug("%s:\n", __func__);
+
+ ret = cal_utils_create_cal_types(CORE_MAX_CAL,
+ q6core_lcl.cal_data, cal_type_info);
+ if (ret < 0) {
+ pr_err("%s: could not create cal type!\n",
+ __func__);
+ goto err;
+ }
+
+ return ret;
+err:
+ q6core_delete_cal_data();
+ return ret;
+}
+
+static int __init core_init(void)
+{
+ memset(&q6core_lcl, 0, sizeof(struct q6core_str));
+ init_waitqueue_head(&q6core_lcl.bus_bw_req_wait);
+ init_waitqueue_head(&q6core_lcl.cmd_req_wait);
+ init_waitqueue_head(&q6core_lcl.avcs_fwk_ver_req_wait);
+ q6core_lcl.cmd_resp_received_flag = FLAG_NONE;
+ mutex_init(&q6core_lcl.cmd_lock);
+ mutex_init(&q6core_lcl.ver_lock);
+
+ q6core_init_cal_data();
+
+ return 0;
+}
+module_init(core_init);
+
+static void __exit core_exit(void)
+{
+ mutex_destroy(&q6core_lcl.cmd_lock);
+ mutex_destroy(&q6core_lcl.ver_lock);
+ q6core_delete_cal_data();
+}
+module_exit(core_exit);
+MODULE_DESCRIPTION("ADSP core driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/msm/qdsp6v2/q6lsm.c b/sound/soc/msm/qdsp6v2/q6lsm.c
new file mode 100644
index 000000000000..a19f0447c4b4
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6lsm.c
@@ -0,0 +1,2139 @@
+/*
+ * Copyright (c) 2013-2020, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/time.h>
+#include <linux/atomic.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/lsm_params.h>
+#include <sound/q6core.h>
+#include <sound/q6common.h>
+#include <sound/q6lsm.h>
+#include <asm/ioctls.h>
+#include <linux/memory.h>
+#include <linux/msm_audio_ion.h>
+#include <sound/q6afe-v2.h>
+#include <sound/audio_cal_utils.h>
+#include <sound/adsp_err.h>
+
+#define APR_TIMEOUT (5 * HZ)
+#define LSM_ALIGN_BOUNDARY 512
+#define LSM_SAMPLE_RATE 16000
+#define QLSM_PARAM_ID_MINOR_VERSION 1
+#define QLSM_PARAM_ID_MINOR_VERSION_2 2
+
+static int lsm_afe_port;
+
+enum {
+ LSM_CUSTOM_TOP_IDX,
+ LSM_TOP_IDX,
+ LSM_CAL_IDX,
+ LSM_MAX_CAL_IDX
+};
+
+enum {
+ CMD_STATE_CLEARED = 0,
+ CMD_STATE_WAIT_RESP = 1,
+};
+
+enum {
+ LSM_INVALID_SESSION_ID = 0,
+ LSM_MIN_SESSION_ID = 1,
+ LSM_MAX_SESSION_ID = 8,
+ LSM_CONTROL_SESSION = 0x0F,
+};
+
+#define CHECK_SESSION(x) (x < LSM_MIN_SESSION_ID || x > LSM_MAX_SESSION_ID)
+struct lsm_common {
+ void *apr;
+ atomic_t apr_users;
+ struct lsm_client common_client[LSM_MAX_SESSION_ID + 1];
+
+ int set_custom_topology;
+ struct cal_type_data *cal_data[LSM_MAX_CAL_IDX];
+
+ struct mutex apr_lock;
+};
+
+static struct lsm_common lsm_common;
+/*
+ * mmap_handle_p can point either client->sound_model.mem_map_handle or
+ * lsm_common.mmap_handle_for_cal.
+ * mmap_lock must be held while accessing this.
+ */
+static spinlock_t mmap_lock;
+static uint32_t *mmap_handle_p;
+
+static spinlock_t lsm_session_lock;
+static struct lsm_client *lsm_session[LSM_MAX_SESSION_ID + 1];
+
+static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv);
+static int q6lsm_send_cal(struct lsm_client *client, u32 set_params_opcode);
+static int q6lsm_memory_map_regions(struct lsm_client *client,
+ dma_addr_t dma_addr_p, uint32_t dma_buf_sz,
+ uint32_t *mmap_p);
+static int q6lsm_memory_unmap_regions(struct lsm_client *client,
+ uint32_t handle);
+
+static int q6lsm_callback(struct apr_client_data *data, void *priv)
+{
+ struct lsm_client *client = (struct lsm_client *)priv;
+ uint32_t token;
+ uint32_t *payload;
+
+ if (!client || !data) {
+ pr_err("%s: client %pK data %pK\n",
+ __func__, client, data);
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("%s: SSR event received 0x%x, event 0x%x, proc 0x%x\n",
+ __func__, data->opcode, data->reset_event,
+ data->reset_proc);
+
+ cal_utils_clear_cal_block_q6maps(LSM_MAX_CAL_IDX,
+ lsm_common.cal_data);
+ mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+ lsm_common.set_custom_topology = 1;
+ mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+ return 0;
+ }
+
+ payload = data->payload;
+ pr_debug("%s: Session %d opcode 0x%x token 0x%x payload size %d\n"
+ "payload [0] = 0x%x\n", __func__, client->session,
+ data->opcode, data->token, data->payload_size, payload[0]);
+ if (data->opcode == LSM_DATA_EVENT_READ_DONE) {
+ struct lsm_cmd_read_done read_done;
+ token = data->token;
+ if (data->payload_size > sizeof(read_done) ||
+ data->payload_size < 6 * sizeof(payload[0])) {
+ pr_err("%s: read done error payload size %d expected size %zd\n",
+ __func__, data->payload_size,
+ sizeof(read_done));
+ return -EINVAL;
+ }
+ pr_debug("%s: opcode %x status %x lsw %x msw %x mem_map handle %x\n",
+ __func__, data->opcode, payload[0], payload[1],
+ payload[2], payload[3]);
+ read_done.status = payload[0];
+ read_done.buf_addr_lsw = payload[1];
+ read_done.buf_addr_msw = payload[2];
+ read_done.mem_map_handle = payload[3];
+ read_done.total_size = payload[4];
+ read_done.offset = payload[5];
+ if (client->cb)
+ client->cb(data->opcode, data->token,
+ (void *)&read_done,
+ sizeof(read_done),
+ client->priv);
+ return 0;
+ } else if (data->opcode == APR_BASIC_RSP_RESULT) {
+ token = data->token;
+ switch (payload[0]) {
+ case LSM_SESSION_CMD_START:
+ case LSM_SESSION_CMD_STOP:
+ case LSM_SESSION_CMD_SET_PARAMS:
+ case LSM_SESSION_CMD_OPEN_TX:
+ case LSM_SESSION_CMD_CLOSE_TX:
+ case LSM_SESSION_CMD_REGISTER_SOUND_MODEL:
+ case LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL:
+ case LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS:
+ case LSM_SESSION_CMD_EOB:
+ case LSM_SESSION_CMD_READ:
+ case LSM_SESSION_CMD_OPEN_TX_V2:
+ case LSM_CMD_ADD_TOPOLOGIES:
+ case LSM_SESSION_CMD_SET_PARAMS_V2:
+ case LSM_SESSION_CMD_SET_PARAMS_V3:
+ if (token != client->session &&
+ payload[0] !=
+ LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL) {
+ pr_err("%s: Invalid session %d receivced expected %d\n",
+ __func__, token, client->session);
+ return -EINVAL;
+ }
+ if (data->payload_size < 2 * sizeof(payload[0])) {
+ pr_err("%s: payload has invalid size[%d]\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
+ client->cmd_err_code = payload[1];
+ if (client->cmd_err_code)
+ pr_err("%s: cmd 0x%x failed status %d\n",
+ __func__, payload[0], client->cmd_err_code);
+ if (atomic_cmpxchg(&client->cmd_state,
+ CMD_STATE_WAIT_RESP,
+ CMD_STATE_CLEARED) ==
+ CMD_STATE_WAIT_RESP)
+ wake_up(&client->cmd_wait);
+ break;
+ default:
+ pr_debug("%s: Unknown command 0x%x\n",
+ __func__, payload[0]);
+ break;
+ }
+ return 0;
+ }
+
+ if (client->cb)
+ client->cb(data->opcode, data->token, data->payload,
+ data->payload_size, client->priv);
+
+ return 0;
+}
+
+static int q6lsm_session_alloc(struct lsm_client *client)
+{
+ unsigned long flags;
+ int n, ret = -ENOMEM;
+
+ spin_lock_irqsave(&lsm_session_lock, flags);
+ for (n = LSM_MIN_SESSION_ID; n <= LSM_MAX_SESSION_ID; n++) {
+ if (!lsm_session[n]) {
+ lsm_session[n] = client;
+ ret = n;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&lsm_session_lock, flags);
+ pr_debug("%s: Alloc Session %d", __func__, n);
+ return ret;
+}
+
+static void q6lsm_session_free(struct lsm_client *client)
+{
+ unsigned long flags;
+ pr_debug("%s: Freeing session ID %d\n", __func__, client->session);
+ spin_lock_irqsave(&lsm_session_lock, flags);
+ lsm_session[client->session] = NULL;
+ spin_unlock_irqrestore(&lsm_session_lock, flags);
+ client->session = LSM_INVALID_SESSION_ID;
+}
+
+static void *q6lsm_mmap_apr_reg(void)
+{
+ if (atomic_inc_return(&lsm_common.apr_users) == 1) {
+ lsm_common.apr =
+ apr_register("ADSP", "LSM", q6lsm_mmapcallback,
+ 0x0FFFFFFFF, &lsm_common);
+ if (!lsm_common.apr) {
+ pr_debug("%s: Unable to register APR LSM common port\n",
+ __func__);
+ atomic_dec(&lsm_common.apr_users);
+ }
+ }
+ return lsm_common.apr;
+}
+
+static int q6lsm_mmap_apr_dereg(void)
+{
+ if (atomic_read(&lsm_common.apr_users) <= 0) {
+ WARN("%s: APR common port already closed\n", __func__);
+ } else {
+ if (atomic_dec_return(&lsm_common.apr_users) == 0) {
+ apr_deregister(lsm_common.apr);
+ pr_debug("%s: APR De-Register common port\n", __func__);
+ }
+ }
+ return 0;
+}
+
+struct lsm_client *q6lsm_client_alloc(lsm_app_cb cb, void *priv)
+{
+ struct lsm_client *client;
+ int n;
+
+ client = kzalloc(sizeof(struct lsm_client), GFP_KERNEL);
+ if (!client)
+ return NULL;
+ n = q6lsm_session_alloc(client);
+ if (n <= 0) {
+ kfree(client);
+ return NULL;
+ }
+ client->session = n;
+ client->cb = cb;
+ client->priv = priv;
+ if (CHECK_SESSION(client->session)) {
+ pr_err("%s: Client session %d\n",
+ __func__, client->session);
+ kfree(client);
+ return NULL;
+ }
+ pr_debug("%s: Client Session %d\n", __func__, client->session);
+ client->apr = apr_register("ADSP", "LSM", q6lsm_callback,
+ ((client->session) << 8 | client->session),
+ client);
+
+ if (client->apr == NULL) {
+ pr_err("%s: Registration with APR failed\n", __func__);
+ goto fail;
+ }
+
+ pr_debug("%s: Registering the common port with APR\n", __func__);
+ client->mmap_apr = q6lsm_mmap_apr_reg();
+ if (!client->mmap_apr) {
+ pr_err("%s: APR registration failed\n", __func__);
+ goto fail;
+ }
+
+ init_waitqueue_head(&client->cmd_wait);
+ mutex_init(&client->cmd_lock);
+ atomic_set(&client->cmd_state, CMD_STATE_CLEARED);
+ pr_debug("%s: New client allocated\n", __func__);
+ return client;
+fail:
+ q6lsm_client_free(client);
+ return NULL;
+}
+
+void q6lsm_client_free(struct lsm_client *client)
+{
+ if (!client)
+ return;
+ if (CHECK_SESSION(client->session)) {
+ pr_err("%s: Invalid Session %d\n", __func__, client->session);
+ return;
+ }
+ apr_deregister(client->apr);
+ client->mmap_apr = NULL;
+ q6lsm_session_free(client);
+ q6lsm_mmap_apr_dereg();
+ mutex_destroy(&client->cmd_lock);
+ kfree(client);
+ client = NULL;
+}
+
+/*
+ * q6lsm_apr_send_pkt : If wait == true, hold mutex to prevent from preempting
+ * other thread's wait.
+ * If mmap_handle_p != NULL, disable irq and spin lock to
+ * protect mmap_handle_p
+ */
+static int q6lsm_apr_send_pkt(struct lsm_client *client, void *handle,
+ void *data, bool wait, uint32_t *mmap_p)
+{
+ int ret;
+ unsigned long flags = 0;
+ struct apr_hdr *msg_hdr = (struct apr_hdr *) data;
+
+ pr_debug("%s: enter wait %d\n", __func__, wait);
+ if (wait)
+ mutex_lock(&lsm_common.apr_lock);
+ if (mmap_p) {
+ WARN_ON(!wait);
+ spin_lock_irqsave(&mmap_lock, flags);
+ mmap_handle_p = mmap_p;
+ }
+ atomic_set(&client->cmd_state, CMD_STATE_WAIT_RESP);
+ client->cmd_err_code = 0;
+ ret = apr_send_pkt(handle, data);
+ if (mmap_p)
+ spin_unlock_irqrestore(&mmap_lock, flags);
+
+ if (ret < 0) {
+ pr_err("%s: apr_send_pkt failed %d\n", __func__, ret);
+ } else if (wait) {
+ ret = wait_event_timeout(client->cmd_wait,
+ (atomic_read(&client->cmd_state) ==
+ CMD_STATE_CLEARED),
+ APR_TIMEOUT);
+ if (likely(ret)) {
+ /* q6 returned error */
+ if (client->cmd_err_code) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ client->cmd_err_code));
+ ret = adsp_err_get_lnx_err_code(
+ client->cmd_err_code);
+ } else {
+ ret = 0;
+ }
+ } else {
+ pr_err("%s: wait timedout, apr_opcode = 0x%x, size = %d\n",
+ __func__, msg_hdr->opcode, msg_hdr->pkt_size);
+ /* ret = 0 means wait timed out */
+ ret = -ETIMEDOUT;
+ }
+ } else {
+ ret = 0;
+ }
+ if (wait)
+ mutex_unlock(&lsm_common.apr_lock);
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+}
+
+static void q6lsm_add_hdr(struct lsm_client *client, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg)
+{
+ pr_debug("%s: pkt_size %d cmd_flg %d session %d\n", __func__,
+ pkt_size, cmd_flg, client->session);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(sizeof(struct apr_hdr)),
+ APR_PKT_VER);
+ hdr->src_svc = APR_SVC_LSM;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_LSM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = ((client->session << 8) & 0xFF00) | client->session;
+ hdr->dest_port = ((client->session << 8) & 0xFF00) | client->session;
+ hdr->pkt_size = pkt_size;
+ if (cmd_flg)
+ hdr->token = client->session;
+}
+
+/*
+ * LSM still supports 3 versions of commands so it cannot use the common
+ * Q6Common packing function. No need to check parameter pointers as it
+ * is static and should only be called internally.
+ */
+static int q6lsm_pack_params(u8 *dest, struct param_hdr_v3 *param_info,
+ u8 *param_data, size_t *final_length,
+ u32 set_param_opcode)
+{
+ bool iid_supported = q6common_is_instance_id_supported();
+ union param_hdrs *param_hdr = NULL;
+ u32 param_size = param_info->param_size;
+ size_t hdr_size;
+ size_t provided_size = *final_length;
+
+ hdr_size = iid_supported ? sizeof(struct param_hdr_v3) :
+ sizeof(struct param_hdr_v2);
+ if (provided_size < hdr_size) {
+ pr_err("%s: Provided size %zu is not large enough, need %zu\n",
+ __func__, provided_size, hdr_size);
+ return -EINVAL;
+ }
+
+ if (iid_supported) {
+ memcpy(dest, param_info, hdr_size);
+ } else {
+ /* MID, PID and structure size are the same in V1 and V2 */
+ param_hdr = (union param_hdrs *) dest;
+ param_hdr->v2.module_id = param_info->module_id;
+ param_hdr->v2.param_id = param_info->param_id;
+
+ switch (set_param_opcode) {
+ case LSM_SESSION_CMD_SET_PARAMS_V2:
+ param_hdr->v2.param_size = param_size;
+ break;
+ case LSM_SESSION_CMD_SET_PARAMS:
+ default:
+ if (param_size > U16_MAX) {
+ pr_err("%s: Invalid param size %d\n", __func__,
+ param_size);
+ return -EINVAL;
+ }
+
+ param_hdr->v1.param_size = param_size;
+ param_hdr->v1.reserved = 0;
+ break;
+ }
+ }
+
+ *final_length = hdr_size;
+
+ if (param_data != NULL) {
+ if (provided_size < hdr_size + param_size) {
+ pr_err("%s: Provided size %zu is not large enough, need %zu\n",
+ __func__, provided_size, hdr_size + param_size);
+ return -EINVAL;
+ }
+ memcpy(dest + hdr_size, param_data, param_size);
+ *final_length += param_size;
+ }
+ return 0;
+}
+
+static int q6lsm_set_params_v2(struct lsm_client *client,
+ struct mem_mapping_hdr *mem_hdr,
+ uint8_t *param_data, uint32_t param_size,
+ uint32_t set_param_opcode)
+{
+ struct lsm_session_cmd_set_params_v2 *lsm_set_param = NULL;
+ uint32_t pkt_size = 0;
+ int ret;
+
+ pkt_size = sizeof(struct lsm_session_cmd_set_params_v2);
+ /* Only include param size in packet size when inband */
+ if (param_data != NULL)
+ pkt_size += param_size;
+
+ lsm_set_param = kzalloc(pkt_size, GFP_KERNEL);
+ if (!lsm_set_param)
+ return -ENOMEM;
+
+ q6lsm_add_hdr(client, &lsm_set_param->apr_hdr, pkt_size, true);
+ lsm_set_param->apr_hdr.opcode = set_param_opcode;
+ lsm_set_param->payload_size = param_size;
+
+ if (mem_hdr != NULL) {
+ lsm_set_param->mem_hdr = *mem_hdr;
+ } else if (param_data != NULL) {
+ memcpy(lsm_set_param->param_data, param_data, param_size);
+ } else {
+ pr_err("%s: Received NULL pointers for both memory header and data\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = q6lsm_apr_send_pkt(client, client->apr, lsm_set_param, true,
+ NULL);
+done:
+ kfree(lsm_set_param);
+ return ret;
+}
+
+static int q6lsm_set_params_v3(struct lsm_client *client,
+ struct mem_mapping_hdr *mem_hdr,
+ uint8_t *param_data, uint32_t param_size)
+{
+ struct lsm_session_cmd_set_params_v3 *lsm_set_param = NULL;
+ uint16_t pkt_size = 0;
+ int ret = 0;
+
+ pkt_size = sizeof(struct lsm_session_cmd_set_params_v3);
+ /* Only include param size in packet size when inband */
+ if (param_data != NULL)
+ pkt_size += param_size;
+
+ lsm_set_param = kzalloc(pkt_size, GFP_KERNEL);
+ if (!lsm_set_param)
+ return -ENOMEM;
+
+ q6lsm_add_hdr(client, &lsm_set_param->apr_hdr, pkt_size, true);
+ lsm_set_param->apr_hdr.opcode = LSM_SESSION_CMD_SET_PARAMS_V3;
+ lsm_set_param->payload_size = param_size;
+
+ if (mem_hdr != NULL) {
+ lsm_set_param->mem_hdr = *mem_hdr;
+ } else if (param_data != NULL) {
+ memcpy(lsm_set_param->param_data, param_data, param_size);
+ } else {
+ pr_err("%s: Received NULL pointers for both memory header and data\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = q6lsm_apr_send_pkt(client, client->apr, lsm_set_param, true,
+ NULL);
+done:
+ kfree(lsm_set_param);
+ return ret;
+}
+
+static int q6lsm_set_params(struct lsm_client *client,
+ struct mem_mapping_hdr *mem_hdr,
+ uint8_t *param_data, uint32_t param_size,
+ uint32_t set_param_opcode)
+
+{
+ if (q6common_is_instance_id_supported())
+ return q6lsm_set_params_v3(client, mem_hdr, param_data,
+ param_size);
+ else
+ return q6lsm_set_params_v2(client, mem_hdr, param_data,
+ param_size, set_param_opcode);
+}
+
+static int q6lsm_pack_and_set_params(struct lsm_client *client,
+ struct param_hdr_v3 *param_info,
+ uint8_t *param_data,
+ uint32_t set_param_opcode)
+
+{
+ u8 *packed_data = NULL;
+ size_t total_size = 0;
+ int ret = 0;
+
+ total_size = sizeof(union param_hdrs) + param_info->param_size;
+ packed_data = kzalloc(total_size, GFP_KERNEL);
+ if (!packed_data)
+ return -ENOMEM;
+
+ ret = q6lsm_pack_params(packed_data, param_info, param_data,
+ &total_size, set_param_opcode);
+ if (ret)
+ goto done;
+
+ ret = q6lsm_set_params(client, NULL, packed_data, total_size,
+ set_param_opcode);
+
+done:
+ kfree(packed_data);
+ return ret;
+}
+
+static int q6lsm_send_custom_topologies(struct lsm_client *client)
+{
+ int rc;
+ struct cal_block_data *cal_block = NULL;
+ struct lsm_custom_topologies cstm_top;
+
+ if (lsm_common.cal_data[LSM_CUSTOM_TOP_IDX] == NULL) {
+ pr_err("%s: LSM_CUSTOM_TOP_IDX invalid\n", __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ lsm_common.set_custom_topology = 0;
+
+ mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+ cal_block = cal_utils_get_only_cal_block(
+ lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]);
+ if (!cal_block) {
+ pr_err("%s: Cal block for LSM_CUSTOM_TOP_IDX not found\n",
+ __func__);
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ if (cal_block->cal_data.size <= 0) {
+ pr_err("%s: Invalid size for LSM_CUSTOM_TOP %zd\n",
+ __func__, cal_block->cal_data.size);
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ memset(&cstm_top, 0, sizeof(cstm_top));
+ /* Map the memory for out-of-band data */
+ rc = q6lsm_memory_map_regions(client, cal_block->cal_data.paddr,
+ cal_block->map_data.map_size,
+ &cal_block->map_data.q6map_handle);
+ if (rc < 0) {
+ pr_err("%s: Failed to map custom topologied, err = %d\n",
+ __func__, rc);
+ goto unlock;
+ }
+
+ q6lsm_add_hdr(client, &cstm_top.hdr,
+ sizeof(cstm_top), true);
+ cstm_top.hdr.opcode = LSM_CMD_ADD_TOPOLOGIES;
+
+ /*
+ * For ADD_TOPOLOGIES, the dest_port should be 0
+ * Note that source port cannot be zero as it is used
+ * to route the response to a specific client registered
+ * on APR
+ */
+ cstm_top.hdr.dest_port = 0;
+
+ cstm_top.data_payload_addr_lsw =
+ lower_32_bits(cal_block->cal_data.paddr);
+ cstm_top.data_payload_addr_msw =
+ msm_audio_populate_upper_32_bits(
+ cal_block->cal_data.paddr);
+ cstm_top.mem_map_handle = cal_block->map_data.q6map_handle;
+ cstm_top.buffer_size = cal_block->cal_data.size;
+
+ rc = q6lsm_apr_send_pkt(client, client->apr,
+ &cstm_top, true, NULL);
+ if (rc)
+ pr_err("%s: Failed to add custom top, err = %d\n",
+ __func__, rc);
+ /* go ahead and unmap even if custom top failed */
+ rc = q6lsm_memory_unmap_regions(client,
+ cal_block->map_data.q6map_handle);
+ if (rc) {
+ pr_err("%s: Failed to unmap, err = %d\n",
+ __func__, rc);
+ /* Even if mem unmap failed, treat the cmd as success */
+ rc = 0;
+ }
+
+unlock:
+ mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+done:
+ return rc;
+}
+
+static int q6lsm_do_open_v2(struct lsm_client *client,
+ uint16_t app_id)
+{
+ int rc;
+ struct cal_block_data *cal_block = NULL;
+ struct audio_cal_info_lsm_top *lsm_top;
+ struct lsm_stream_cmd_open_tx_v2 open_v2;
+
+ if (lsm_common.cal_data[LSM_TOP_IDX] == NULL) {
+ pr_err("%s: LSM_TOP_IDX invalid\n", __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&lsm_common.cal_data[LSM_TOP_IDX]->lock);
+ cal_block = cal_utils_get_only_cal_block(
+ lsm_common.cal_data[LSM_TOP_IDX]);
+ if (!cal_block) {
+ pr_err("%s: Cal block for LSM_TOP_IDX not found\n",
+ __func__);
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ lsm_top = (struct audio_cal_info_lsm_top *)
+ cal_block->cal_info;
+ if (!lsm_top) {
+ pr_err("%s: cal_info for LSM_TOP_IDX not found\n",
+ __func__);
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ pr_debug("%s: topology_id = 0x%x, acdb_id = 0x%x, app_type = 0x%x\n",
+ __func__, lsm_top->topology, lsm_top->acdb_id,
+ lsm_top->app_type);
+
+ if (lsm_top->topology == 0) {
+ pr_err("%s: toplogy id not sent for app_type 0x%x\n",
+ __func__, lsm_top->app_type);
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ client->app_id = lsm_top->app_type;
+ memset(&open_v2, 0, sizeof(open_v2));
+ q6lsm_add_hdr(client, &open_v2.hdr,
+ sizeof(open_v2), true);
+ open_v2.topology_id = lsm_top->topology;
+ open_v2.hdr.opcode = LSM_SESSION_CMD_OPEN_TX_V2;
+
+ rc = q6lsm_apr_send_pkt(client, client->apr,
+ &open_v2, true, NULL);
+ if (rc)
+ pr_err("%s: open_v2 failed, err = %d\n",
+ __func__, rc);
+ else
+ client->use_topology = true;
+unlock:
+ mutex_unlock(&lsm_common.cal_data[LSM_TOP_IDX]->lock);
+done:
+ return rc;
+
+}
+
+void q6lsm_sm_set_param_data(struct lsm_client *client,
+ struct lsm_params_info *p_info,
+ size_t *offset)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ param_hdr.module_id = p_info->module_id;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = p_info->param_id;
+ param_hdr.param_size = client->sound_model.size;
+ *offset = sizeof(struct param_hdr_v3);
+
+ ret = q6lsm_pack_params((u8 *) client->sound_model.data, &param_hdr,
+ NULL, offset, LSM_SESSION_CMD_SET_PARAMS_V2);
+ if (ret)
+ pr_err("%s: Failed to pack params, error %d\n", __func__, ret);
+}
+
+int q6lsm_open(struct lsm_client *client, uint16_t app_id)
+{
+ int rc = 0;
+ struct lsm_stream_cmd_open_tx open;
+
+ /* Add Custom topologies if needed */
+ if (lsm_common.set_custom_topology) {
+ rc = q6lsm_send_custom_topologies(client);
+ if (rc)
+ pr_err("%s: Failed to send cust_top, err = %d\n",
+ __func__, rc);
+ }
+
+ /* Try to open with topology first */
+ rc = q6lsm_do_open_v2(client, app_id);
+ if (!rc)
+ /* open_v2 was successful */
+ goto done;
+
+ pr_debug("%s: try without topology\n",
+ __func__);
+
+ memset(&open, 0, sizeof(open));
+ q6lsm_add_hdr(client, &open.hdr, sizeof(open), true);
+ switch (client->app_id) {
+ case LSM_VOICE_WAKEUP_APP_ID_V2:
+ open.app_id = client->app_id;
+ break;
+ default:
+ pr_err("%s: default err 0x%x\n", __func__, client->app_id);
+ rc = -EINVAL;
+ break;
+ }
+
+ open.sampling_rate = LSM_SAMPLE_RATE;
+ open.hdr.opcode = LSM_SESSION_CMD_OPEN_TX;
+ rc = q6lsm_apr_send_pkt(client, client->apr,
+ &open, true, NULL);
+ if (rc)
+ pr_err("%s: Open failed opcode 0x%x, rc %d\n",
+ __func__, open.hdr.opcode, rc);
+ else
+ client->use_topology = false;
+done:
+ pr_debug("%s: leave %d\n", __func__, rc);
+ return rc;
+}
+
+static int q6lsm_send_confidence_levels(struct lsm_client *client,
+ struct param_hdr_v3 *param_info,
+ uint32_t set_param_opcode)
+{
+ struct lsm_param_confidence_levels *conf_levels = NULL;
+ uint32_t num_conf_levels = client->num_confidence_levels;
+ uint8_t i = 0;
+ uint8_t padd_size = 0;
+ uint32_t param_size = 0;
+ int rc = 0;
+
+ /* Data must be 4 byte alligned so add any necessary padding. */
+ padd_size = (4 - (num_conf_levels % 4)) - 1;
+ param_size = (sizeof(uint8_t) + num_conf_levels + padd_size) *
+ sizeof(uint8_t);
+ param_info->param_size = param_size;
+ pr_debug("%s: Set Conf Levels PARAM SIZE = %d\n", __func__, param_size);
+
+ conf_levels = kzalloc(param_size, GFP_KERNEL);
+ if (!conf_levels)
+ return -ENOMEM;
+
+ conf_levels->num_confidence_levels = num_conf_levels;
+ pr_debug("%s: Num conf_level = %d\n", __func__, num_conf_levels);
+
+ memcpy(conf_levels->confidence_levels, client->confidence_levels,
+ num_conf_levels);
+ for (i = 0; i < num_conf_levels; i++)
+ pr_debug("%s: Confidence_level[%d] = %d\n", __func__, i,
+ conf_levels->confidence_levels[i]);
+
+ rc = q6lsm_pack_and_set_params(client, param_info,
+ (uint8_t *) conf_levels,
+ set_param_opcode);
+ if (rc)
+ pr_err("%s: Send confidence_levels cmd failed, err = %d\n",
+ __func__, rc);
+ kfree(conf_levels);
+ return rc;
+}
+
+static int q6lsm_send_param_opmode(struct lsm_client *client,
+ struct param_hdr_v3 *param_info,
+ u32 set_param_opcode)
+{
+ struct lsm_param_op_mode op_mode = {0};
+ int rc = 0;
+
+ param_info->param_size = sizeof(op_mode);
+
+ op_mode.minor_version = QLSM_PARAM_ID_MINOR_VERSION;
+ op_mode.mode = client->mode;
+ pr_debug("%s: mode = 0x%x", __func__, op_mode.mode);
+
+ rc = q6lsm_pack_and_set_params(client, param_info, (uint8_t *) &op_mode,
+ set_param_opcode);
+ if (rc)
+ pr_err("%s: Failed set_params, rc %d\n", __func__, rc);
+
+ pr_debug("%s: leave %d\n", __func__, rc);
+ return rc;
+}
+
+void set_lsm_port(int lsm_port)
+{
+ lsm_afe_port = lsm_port;
+}
+
+int get_lsm_port(void)
+{
+ return lsm_afe_port;
+}
+
+int q6lsm_set_port_connected(struct lsm_client *client)
+{
+ struct lsm_param_connect_to_port connect_port = {0};
+ struct param_hdr_v3 connectport_hdr = {0};
+ u32 set_param_opcode = 0;
+ int rc = 0;
+
+ if (client->use_topology) {
+ set_param_opcode = LSM_SESSION_CMD_SET_PARAMS_V2;
+ connectport_hdr.module_id = LSM_MODULE_ID_FRAMEWORK;
+ } else {
+ set_param_opcode = LSM_SESSION_CMD_SET_PARAMS;
+ connectport_hdr.module_id = LSM_MODULE_ID_VOICE_WAKEUP;
+ }
+ connectport_hdr.instance_id = INSTANCE_ID_0;
+ connectport_hdr.param_id = LSM_PARAM_ID_CONNECT_TO_PORT;
+ connectport_hdr.param_size = sizeof(connect_port);
+
+ client->connect_to_port = get_lsm_port();
+ connect_port.minor_version = QLSM_PARAM_ID_MINOR_VERSION;
+ connect_port.port_id = client->connect_to_port;
+
+ rc = q6lsm_pack_and_set_params(client, &connectport_hdr,
+ (uint8_t *) &connect_port,
+ set_param_opcode);
+ if (rc)
+ pr_err("%s: Failed set_params, rc %d\n", __func__, rc);
+ return rc;
+}
+
+static int q6lsm_send_param_polling_enable(struct lsm_client *client,
+ bool poll_en,
+ struct param_hdr_v3 *param_info,
+ u32 set_param_opcode)
+{
+ struct lsm_param_poll_enable polling_enable = {0};
+ int rc = 0;
+
+ param_info->param_size = sizeof(polling_enable);
+
+ polling_enable.minor_version = QLSM_PARAM_ID_MINOR_VERSION;
+ polling_enable.polling_enable = (poll_en) ? 1 : 0;
+
+ rc = q6lsm_pack_and_set_params(client, param_info,
+ (uint8_t *) &polling_enable,
+ set_param_opcode);
+ if (rc)
+ pr_err("%s: Failed set_params, rc %d\n", __func__, rc);
+ return rc;
+}
+
+int q6lsm_set_fwk_mode_cfg(struct lsm_client *client,
+ uint32_t event_mode)
+{
+ struct lsm_param_fwk_mode_cfg fwk_mode_cfg = {0};
+ struct param_hdr_v3 fwk_mode_cfg_hdr = {0};
+ int rc = 0;
+
+ if (!client->use_topology) {
+ pr_debug("%s: Ignore sending event mode\n", __func__);
+ return rc;
+ }
+
+ fwk_mode_cfg_hdr.module_id = LSM_MODULE_ID_FRAMEWORK;
+ fwk_mode_cfg_hdr.instance_id = INSTANCE_ID_0;
+ fwk_mode_cfg_hdr.param_id = LSM_PARAM_ID_FWK_MODE_CONFIG;
+ fwk_mode_cfg_hdr.param_size = sizeof(fwk_mode_cfg);
+
+ fwk_mode_cfg.minor_version = QLSM_PARAM_ID_MINOR_VERSION;
+ fwk_mode_cfg.mode = event_mode;
+ pr_debug("%s: mode = %d\n", __func__, fwk_mode_cfg.mode);
+
+ rc = q6lsm_pack_and_set_params(client, &fwk_mode_cfg_hdr,
+ (uint8_t *) &fwk_mode_cfg,
+ LSM_SESSION_CMD_SET_PARAMS_V2);
+ if (rc)
+ pr_err("%s: Failed set_params, rc %d\n", __func__, rc);
+ return rc;
+}
+
+static int q6lsm_arrange_mch_map(struct lsm_param_media_fmt *media_fmt,
+ int channel_count)
+{
+ int rc = 0;
+
+ memset(media_fmt->channel_mapping, 0, LSM_MAX_NUM_CHANNELS);
+
+ switch (channel_count) {
+ case 1:
+ media_fmt->channel_mapping[0] = PCM_CHANNEL_FC;
+ break;
+ case 2:
+ media_fmt->channel_mapping[0] = PCM_CHANNEL_FL;
+ media_fmt->channel_mapping[1] = PCM_CHANNEL_FR;
+ break;
+ case 3:
+ media_fmt->channel_mapping[0] = PCM_CHANNEL_FL;
+ media_fmt->channel_mapping[1] = PCM_CHANNEL_FR;
+ media_fmt->channel_mapping[2] = PCM_CHANNEL_FC;
+ break;
+ case 4:
+ media_fmt->channel_mapping[0] = PCM_CHANNEL_FL;
+ media_fmt->channel_mapping[1] = PCM_CHANNEL_FR;
+ media_fmt->channel_mapping[2] = PCM_CHANNEL_LS;
+ media_fmt->channel_mapping[3] = PCM_CHANNEL_RS;
+ break;
+ default:
+ pr_err("%s: invalid num_chan %d\n", __func__, channel_count);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+int q6lsm_set_media_fmt_params(struct lsm_client *client)
+{
+ struct lsm_param_media_fmt media_fmt = {0};
+ struct lsm_hw_params param = client->hw_params;
+ struct param_hdr_v3 media_fmt_hdr = {0};
+ int rc = 0;
+
+ if (!client->use_topology) {
+ pr_debug("%s: Ignore sending media format\n", __func__);
+ goto err_ret;
+ }
+
+ media_fmt_hdr.module_id = LSM_MODULE_ID_FRAMEWORK;
+ media_fmt_hdr.instance_id = INSTANCE_ID_0;
+ media_fmt_hdr.param_id = LSM_PARAM_ID_MEDIA_FMT;
+ media_fmt_hdr.param_size = sizeof(media_fmt);
+
+ media_fmt.minor_version = QLSM_PARAM_ID_MINOR_VERSION_2;
+ media_fmt.sample_rate = param.sample_rate;
+ media_fmt.num_channels = param.num_chs;
+ media_fmt.bit_width = param.sample_size;
+ rc = q6lsm_arrange_mch_map(&media_fmt, media_fmt.num_channels);
+ if (rc)
+ goto err_ret;
+
+ pr_debug("%s: sample rate= %d, channels %d bit width %d\n", __func__,
+ media_fmt.sample_rate, media_fmt.num_channels,
+ media_fmt.bit_width);
+
+ rc = q6lsm_pack_and_set_params(client, &media_fmt_hdr,
+ (uint8_t *) &media_fmt,
+ LSM_SESSION_CMD_SET_PARAMS_V2);
+ if (rc)
+ pr_err("%s: Failed set_params, rc %d\n", __func__, rc);
+err_ret:
+ return rc;
+}
+
+int q6lsm_set_data(struct lsm_client *client,
+ enum lsm_detection_mode mode,
+ bool detectfailure)
+{
+ struct param_hdr_v3 param_hdr = {0};
+ int rc = 0;
+
+ if (!client->confidence_levels) {
+ /*
+ * It is possible that confidence levels are
+ * not provided. This is not a error condition.
+ * Return gracefully without any error
+ */
+ pr_debug("%s: no conf levels to set\n",
+ __func__);
+ return rc;
+ }
+
+ if (mode == LSM_MODE_KEYWORD_ONLY_DETECTION) {
+ client->mode = 0x01;
+ } else if (mode == LSM_MODE_USER_KEYWORD_DETECTION) {
+ client->mode = 0x03;
+ } else {
+ pr_err("%s: Incorrect detection mode %d\n", __func__, mode);
+ rc = -EINVAL;
+ goto err_ret;
+ }
+ client->mode |= detectfailure << 2;
+
+ param_hdr.module_id = LSM_MODULE_ID_VOICE_WAKEUP;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = LSM_PARAM_ID_OPERATION_MODE;
+ rc = q6lsm_send_param_opmode(client, &param_hdr,
+ LSM_SESSION_CMD_SET_PARAMS);
+ if (rc) {
+ pr_err("%s: Failed to set lsm config params %d\n",
+ __func__, rc);
+ goto err_ret;
+ }
+
+ param_hdr.param_id = LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS;
+ rc = q6lsm_send_confidence_levels(client, &param_hdr,
+ LSM_SESSION_CMD_SET_PARAMS);
+ if (rc) {
+ pr_err("%s: Failed to send conf_levels, err = %d\n",
+ __func__, rc);
+ goto err_ret;
+ }
+
+ rc = q6lsm_send_cal(client, LSM_SESSION_CMD_SET_PARAMS);
+ if (rc) {
+ pr_err("%s: Failed to send calibration data %d\n",
+ __func__, rc);
+ goto err_ret;
+ }
+
+err_ret:
+ return rc;
+}
+
+int q6lsm_register_sound_model(struct lsm_client *client,
+ enum lsm_detection_mode mode,
+ bool detectfailure)
+{
+ int rc;
+ struct lsm_cmd_reg_snd_model cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ rc = q6lsm_set_data(client, mode, detectfailure);
+ if (rc) {
+ pr_err("%s: Failed to set lsm data, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd), true);
+ cmd.hdr.opcode = LSM_SESSION_CMD_REGISTER_SOUND_MODEL;
+ cmd.model_addr_lsw = lower_32_bits(client->sound_model.phys);
+ cmd.model_addr_msw = msm_audio_populate_upper_32_bits(
+ client->sound_model.phys);
+ cmd.model_size = client->sound_model.size;
+ /* read updated mem_map_handle by q6lsm_mmapcallback */
+ rmb();
+ cmd.mem_map_handle = client->sound_model.mem_map_handle;
+
+ pr_debug("%s: addr %pK, size %d, handle 0x%x\n", __func__,
+ &client->sound_model.phys, cmd.model_size, cmd.mem_map_handle);
+ rc = q6lsm_apr_send_pkt(client, client->apr, &cmd, true, NULL);
+ if (rc)
+ pr_err("%s: Failed cmd op[0x%x]rc[%d]\n", __func__,
+ cmd.hdr.opcode, rc);
+ else
+ pr_debug("%s: Register sound model succeeded\n", __func__);
+
+ return rc;
+}
+
+int q6lsm_deregister_sound_model(struct lsm_client *client)
+{
+ int rc;
+ struct lsm_cmd_reg_snd_model cmd;
+
+ if (!client) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ if (!client->apr) {
+ pr_err("APR client handle NULL\n");
+ return -EINVAL;
+ }
+
+ if (CHECK_SESSION(client->session)) {
+ pr_err("%s: session[%d]", __func__, client->session);
+ return -EINVAL;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd.hdr), false);
+ cmd.hdr.opcode = LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL;
+
+ rc = q6lsm_apr_send_pkt(client, client->apr, &cmd.hdr, true, NULL);
+ if (rc) {
+ pr_err("%s: Failed cmd opcode 0x%x, rc %d\n", __func__,
+ cmd.hdr.opcode, rc);
+ } else {
+ pr_debug("%s: Deregister sound model succeeded\n", __func__);
+ }
+
+ q6lsm_snd_model_buf_free(client);
+
+ return rc;
+}
+
+static void q6lsm_add_mmaphdr(struct lsm_client *client, struct apr_hdr *hdr,
+ u32 pkt_size, u32 cmd_flg, u32 token)
+{
+ pr_debug("%s: pkt size=%d cmd_flg=%d session=%d\n", __func__, pkt_size,
+ cmd_flg, client->session);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ hdr->src_port = 0x00;
+ hdr->dest_port = client->session;
+ if (cmd_flg)
+ hdr->token = token;
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+static int q6lsm_memory_map_regions(struct lsm_client *client,
+ dma_addr_t dma_addr_p, uint32_t dma_buf_sz,
+ uint32_t *mmap_p)
+{
+ struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+ struct avs_shared_map_region_payload *mregions = NULL;
+ void *mmap_region_cmd = NULL;
+ void *payload = NULL;
+ int rc;
+ int cmd_size = 0;
+
+ pr_debug("%s: dma_addr_p 0x%pK, dma_buf_sz %d, mmap_p 0x%pK, session %d\n",
+ __func__, &dma_addr_p, dma_buf_sz, mmap_p,
+ client->session);
+ if (CHECK_SESSION(client->session)) {
+ pr_err("%s: session[%d]", __func__, client->session);
+ return -EINVAL;
+ }
+ cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) +
+ sizeof(struct avs_shared_map_region_payload);
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (!mmap_region_cmd) {
+ pr_err("%s: memory allocation failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
+ q6lsm_add_mmaphdr(client, &mmap_regions->hdr, cmd_size, true,
+ (client->session << 8));
+
+ mmap_regions->hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS;
+ mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+ mmap_regions->num_regions = 1;
+ mmap_regions->property_flag = 0x00;
+ payload = ((u8 *)mmap_region_cmd +
+ sizeof(struct avs_cmd_shared_mem_map_regions));
+ mregions = (struct avs_shared_map_region_payload *)payload;
+
+ mregions->shm_addr_lsw = lower_32_bits(dma_addr_p);
+ mregions->shm_addr_msw = msm_audio_populate_upper_32_bits(dma_addr_p);
+ mregions->mem_size_bytes = dma_buf_sz;
+
+ rc = q6lsm_apr_send_pkt(client, client->mmap_apr, mmap_region_cmd,
+ true, mmap_p);
+ if (rc)
+ pr_err("%s: Failed mmap_regions opcode 0x%x, rc %d\n",
+ __func__, mmap_regions->hdr.opcode, rc);
+
+ pr_debug("%s: leave %d\n", __func__, rc);
+ kfree(mmap_region_cmd);
+ return rc;
+}
+
+static int q6lsm_memory_unmap_regions(struct lsm_client *client,
+ uint32_t handle)
+{
+ struct avs_cmd_shared_mem_unmap_regions unmap;
+ int rc = 0;
+ int cmd_size = 0;
+ if (CHECK_SESSION(client->session)) {
+ pr_err("%s: session[%d]", __func__, client->session);
+ return -EINVAL;
+ }
+ cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions);
+ q6lsm_add_mmaphdr(client, &unmap.hdr, cmd_size,
+ true, (client->session << 8));
+ unmap.hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS;
+ unmap.mem_map_handle = handle;
+
+ pr_debug("%s: unmap handle 0x%x\n", __func__, unmap.mem_map_handle);
+ rc = q6lsm_apr_send_pkt(client, client->mmap_apr, &unmap, true,
+ NULL);
+ if (rc)
+ pr_err("%s: Failed mmap_regions opcode 0x%x rc %d\n",
+ __func__, unmap.hdr.opcode, rc);
+
+ return rc;
+}
+
+static int q6lsm_send_cal(struct lsm_client *client,
+ u32 set_params_opcode)
+{
+ int rc = 0;
+ struct mem_mapping_hdr mem_hdr = {0};
+ struct cal_block_data *cal_block = NULL;
+
+ pr_debug("%s: Session id %d\n", __func__, client->session);
+ if (CHECK_SESSION(client->session)) {
+ pr_err("%s: session[%d]", __func__, client->session);
+ return -EINVAL;
+ }
+
+ if (lsm_common.cal_data[LSM_CAL_IDX] == NULL)
+ goto done;
+
+ mutex_lock(&lsm_common.cal_data[LSM_CAL_IDX]->lock);
+ cal_block = cal_utils_get_only_cal_block(
+ lsm_common.cal_data[LSM_CAL_IDX]);
+
+ if (!cal_block || cal_block->cal_data.size <= 0) {
+ pr_debug("%s: No cal to send!\n", __func__);
+ goto unlock;
+ }
+
+ if (cal_block->cal_data.size != client->lsm_cal_size) {
+ pr_err("%s: Cal size %zd doesn't match lsm cal size %d\n",
+ __func__, cal_block->cal_data.size,
+ client->lsm_cal_size);
+ rc = -EINVAL;
+ goto unlock;
+ }
+ /* Cache mmap address, only map once or if new addr */
+ lsm_common.common_client[client->session].session = client->session;
+ mem_hdr.data_payload_addr_lsw = lower_32_bits(client->lsm_cal_phy_addr);
+ mem_hdr.data_payload_addr_msw =
+ msm_audio_populate_upper_32_bits(client->lsm_cal_phy_addr);
+ mem_hdr.mem_map_handle = client->sound_model.mem_map_handle;
+
+ pr_debug("%s: Cal Size = %zd", __func__, cal_block->cal_data.size);
+ rc = q6lsm_set_params(client, &mem_hdr, NULL, cal_block->cal_data.size,
+ set_params_opcode);
+ if (rc)
+ pr_err("%s: Failed set_params, rc %d\n", __func__, rc);
+unlock:
+ mutex_unlock(&lsm_common.cal_data[LSM_CAL_IDX]->lock);
+done:
+ return rc;
+}
+
+
+int q6lsm_snd_model_buf_free(struct lsm_client *client)
+{
+ int rc;
+
+ pr_debug("%s: Session id %d\n", __func__, client->session);
+ if (CHECK_SESSION(client->session)) {
+ pr_err("%s: session[%d]", __func__, client->session);
+ return -EINVAL;
+ }
+
+ mutex_lock(&client->cmd_lock);
+ rc = q6lsm_memory_unmap_regions(client,
+ client->sound_model.mem_map_handle);
+ if (rc)
+ pr_err("%s: CMD Memory_unmap_regions failed %d\n",
+ __func__, rc);
+
+ if (client->sound_model.data) {
+ msm_audio_ion_free(client->sound_model.client,
+ client->sound_model.handle);
+ client->sound_model.client = NULL;
+ client->sound_model.handle = NULL;
+ client->sound_model.data = NULL;
+ client->sound_model.phys = 0;
+ client->lsm_cal_phy_addr = 0;
+ client->lsm_cal_size = 0;
+ }
+ mutex_unlock(&client->cmd_lock);
+ return rc;
+}
+
+static struct lsm_client *q6lsm_get_lsm_client(int session_id)
+{
+ unsigned long flags;
+ struct lsm_client *client = NULL;
+
+ spin_lock_irqsave(&lsm_session_lock, flags);
+ if (session_id < LSM_MIN_SESSION_ID || session_id > LSM_MAX_SESSION_ID)
+ pr_err("%s: Invalid session %d\n", __func__, session_id);
+ else if (!lsm_session[session_id])
+ pr_err("%s: Not an active session %d\n", __func__, session_id);
+ else
+ client = lsm_session[session_id];
+ spin_unlock_irqrestore(&lsm_session_lock, flags);
+ return client;
+}
+
+/*
+ * q6lsm_mmapcallback : atomic context
+ */
+static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv)
+{
+ unsigned long flags;
+ uint32_t command;
+ uint32_t retcode;
+ uint32_t sid;
+ const uint32_t *payload = data->payload;
+ struct lsm_client *client = NULL;
+
+ if (data->opcode == RESET_EVENTS) {
+ sid = (data->token >> 8) & 0x0F;
+ pr_debug("%s: SSR event received 0x%x, event 0x%x,\n"
+ "proc 0x%x SID 0x%x\n", __func__, data->opcode,
+ data->reset_event, data->reset_proc, sid);
+ if (sid < LSM_MIN_SESSION_ID || sid > LSM_MAX_SESSION_ID)
+ pr_err("%s: Invalid session %d\n", __func__, sid);
+ lsm_common.common_client[sid].lsm_cal_phy_addr = 0;
+ cal_utils_clear_cal_block_q6maps(LSM_MAX_CAL_IDX,
+ lsm_common.cal_data);
+ lsm_common.set_custom_topology = 1;
+ return 0;
+ }
+
+ command = payload[0];
+ retcode = payload[1];
+ sid = (data->token >> 8) & 0x0F;
+ pr_debug("%s: opcode 0x%x command 0x%x return code 0x%x SID 0x%x\n",
+ __func__, data->opcode, command, retcode, sid);
+ client = q6lsm_get_lsm_client(sid);
+ if (!client) {
+ pr_debug("%s: Session %d already freed\n", __func__, sid);
+ return 0;
+ }
+
+ switch (data->opcode) {
+ case LSM_SESSION_CMDRSP_SHARED_MEM_MAP_REGIONS:
+ if (atomic_read(&client->cmd_state) == CMD_STATE_WAIT_RESP) {
+ spin_lock_irqsave(&mmap_lock, flags);
+ *mmap_handle_p = command;
+ /* spin_unlock_irqrestore implies barrier */
+ spin_unlock_irqrestore(&mmap_lock, flags);
+ atomic_set(&client->cmd_state, CMD_STATE_CLEARED);
+ wake_up(&client->cmd_wait);
+ }
+ break;
+ case APR_BASIC_RSP_RESULT:
+ switch (command) {
+ case LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS:
+ atomic_set(&client->cmd_state, CMD_STATE_CLEARED);
+ wake_up(&client->cmd_wait);
+ break;
+ case LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS:
+ if (retcode != 0) {
+ /* error state, signal to stop waiting */
+ if (atomic_read(&client->cmd_state) ==
+ CMD_STATE_WAIT_RESP) {
+ spin_lock_irqsave(&mmap_lock, flags);
+ /* implies barrier */
+ spin_unlock_irqrestore(&mmap_lock,
+ flags);
+ atomic_set(&client->cmd_state,
+ CMD_STATE_CLEARED);
+ wake_up(&client->cmd_wait);
+ }
+ }
+ break;
+ default:
+ pr_warn("%s: Unexpected command 0x%x\n", __func__,
+ command);
+ }
+ default:
+ pr_debug("%s: command 0x%x return code 0x%x opcode 0x%x\n",
+ __func__, command, retcode, data->opcode);
+ break;
+ }
+ if (client->cb)
+ client->cb(data->opcode, data->token,
+ data->payload, data->payload_size,
+ client->priv);
+ return 0;
+}
+
+int q6lsm_snd_model_buf_alloc(struct lsm_client *client, size_t len,
+ bool allocate_module_data)
+{
+ int rc = -EINVAL;
+ struct cal_block_data *cal_block = NULL;
+
+ size_t pad_zero = 0, total_mem = 0;
+
+ if (!client || len <= LSM_ALIGN_BOUNDARY)
+ return rc;
+
+ mutex_lock(&client->cmd_lock);
+
+ mutex_lock(&lsm_common.cal_data[LSM_CAL_IDX]->lock);
+ cal_block = cal_utils_get_only_cal_block(
+ lsm_common.cal_data[LSM_CAL_IDX]);
+ if (cal_block == NULL)
+ goto fail;
+
+ pr_debug("%s:Snd Model len = %zd cal size %zd phys addr %pK", __func__,
+ len, cal_block->cal_data.size,
+ &cal_block->cal_data.paddr);
+ if (!cal_block->cal_data.paddr) {
+ pr_err("%s: No LSM calibration set for session", __func__);
+ rc = -EINVAL;
+ goto fail;
+ }
+ if (!client->sound_model.data) {
+
+ /*
+ * if sound module is sent as set_param
+ * Then memory needs to be allocated for
+ * set_param payload as well.
+ */
+ if (allocate_module_data)
+ len += sizeof(union param_hdrs);
+
+ client->sound_model.size = len;
+ pad_zero = (LSM_ALIGN_BOUNDARY -
+ (len % LSM_ALIGN_BOUNDARY));
+ if ((len > SIZE_MAX - pad_zero) ||
+ (len + pad_zero >
+ SIZE_MAX - cal_block->cal_data.size)) {
+ pr_err("%s: invalid allocation size, len = %zd, pad_zero =%zd, cal_size = %zd\n",
+ __func__, len, pad_zero,
+ cal_block->cal_data.size);
+ rc = -EINVAL;
+ goto fail;
+ }
+
+ total_mem = PAGE_ALIGN(pad_zero + len +
+ cal_block->cal_data.size);
+ pr_debug("%s: Pad zeros sound model %zd Total mem %zd\n",
+ __func__, pad_zero, total_mem);
+ rc = msm_audio_ion_alloc("lsm_client",
+ &client->sound_model.client,
+ &client->sound_model.handle,
+ total_mem,
+ &client->sound_model.phys,
+ &len,
+ &client->sound_model.data);
+ if (rc) {
+ pr_err("%s: Audio ION alloc is failed, rc = %d\n",
+ __func__, rc);
+ goto fail;
+ }
+ pr_debug("%s: Length = %zd\n", __func__, len);
+ client->lsm_cal_phy_addr = (pad_zero +
+ client->sound_model.phys +
+ client->sound_model.size);
+ client->lsm_cal_size = cal_block->cal_data.size;
+ memcpy((client->sound_model.data + pad_zero +
+ client->sound_model.size),
+ (uint32_t *)cal_block->cal_data.kvaddr, client->lsm_cal_size);
+ pr_debug("%s: Copy cal start virt_addr %pK phy_addr %pK\n"
+ "Offset cal virtual Addr %pK\n", __func__,
+ client->sound_model.data, &client->sound_model.phys,
+ (pad_zero + client->sound_model.data +
+ client->sound_model.size));
+ } else {
+ pr_err("%s: sound model busy\n", __func__);
+ rc = -EBUSY;
+ goto fail;
+ }
+ mutex_unlock(&lsm_common.cal_data[LSM_CAL_IDX]->lock);
+ mutex_unlock(&client->cmd_lock);
+
+ rc = q6lsm_memory_map_regions(client, client->sound_model.phys,
+ len,
+ &client->sound_model.mem_map_handle);
+ if (rc) {
+ pr_err("%s: CMD Memory_map_regions failed %d\n", __func__, rc);
+ goto exit;
+ }
+
+ return 0;
+fail:
+ mutex_unlock(&lsm_common.cal_data[LSM_CAL_IDX]->lock);
+ mutex_unlock(&client->cmd_lock);
+exit:
+ q6lsm_snd_model_buf_free(client);
+ return rc;
+}
+
+static int q6lsm_cmd(struct lsm_client *client, int opcode, bool wait)
+{
+ struct apr_hdr hdr;
+ int rc;
+
+ pr_debug("%s: enter opcode %x wait %d\n", __func__, opcode, wait);
+ q6lsm_add_hdr(client, &hdr, sizeof(hdr), true);
+ switch (opcode) {
+ case LSM_SESSION_CMD_START:
+ case LSM_SESSION_CMD_STOP:
+ case LSM_SESSION_CMD_CLOSE_TX:
+ case LSM_SESSION_CMD_EOB:
+ hdr.opcode = opcode;
+ break;
+ default:
+ pr_err("%s: Invalid opcode 0x%x\n", __func__, opcode);
+ return -EINVAL;
+ }
+ rc = q6lsm_apr_send_pkt(client, client->apr, &hdr, wait, NULL);
+ if (rc)
+ pr_err("%s: Failed commmand 0x%x\n", __func__, hdr.opcode);
+
+ pr_debug("%s: leave %d\n", __func__, rc);
+ return rc;
+}
+
+static int q6lsm_send_param_epd_thres(struct lsm_client *client, void *data,
+ struct param_hdr_v3 *param_info)
+{
+ struct snd_lsm_ep_det_thres *ep_det_data = NULL;
+ struct lsm_param_epd_thres epd_thres = {0};
+ int rc = 0;
+
+ param_info->param_size = sizeof(epd_thres);
+
+ ep_det_data = (struct snd_lsm_ep_det_thres *) data;
+ epd_thres.minor_version = QLSM_PARAM_ID_MINOR_VERSION;
+ epd_thres.epd_begin = ep_det_data->epd_begin;
+ epd_thres.epd_end = ep_det_data->epd_end;
+
+ rc = q6lsm_pack_and_set_params(client, param_info,
+ (uint8_t *) &epd_thres,
+ LSM_SESSION_CMD_SET_PARAMS_V2);
+ if (unlikely(rc))
+ pr_err("%s: EPD_THRESHOLD failed, rc %d\n", __func__, rc);
+ return rc;
+}
+
+static int q6lsm_send_param_gain(struct lsm_client *client, u16 gain,
+ struct param_hdr_v3 *param_info)
+{
+ struct lsm_param_gain lsm_gain = {0};
+ int rc = 0;
+
+ param_info->param_size = sizeof(lsm_gain);
+
+ lsm_gain.minor_version = QLSM_PARAM_ID_MINOR_VERSION;
+ lsm_gain.gain = gain;
+
+ rc = q6lsm_pack_and_set_params(client, param_info,
+ (uint8_t *) &lsm_gain,
+ LSM_SESSION_CMD_SET_PARAMS_V2);
+ if (unlikely(rc))
+ pr_err("%s: LSM_GAIN CMD send failed, rc %d\n", __func__, rc);
+ return rc;
+}
+
+int q6lsm_set_one_param(struct lsm_client *client,
+ struct lsm_params_info *p_info, void *data,
+ uint32_t param_type)
+{
+ struct param_hdr_v3 param_info = {0};
+ int rc = 0;
+
+ switch (param_type) {
+ case LSM_ENDPOINT_DETECT_THRESHOLD: {
+ param_info.module_id = p_info->module_id;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = p_info->param_id;
+ rc = q6lsm_send_param_epd_thres(client, data, &param_info);
+ if (rc)
+ pr_err("%s: LSM_ENDPOINT_DETECT_THRESHOLD failed, rc %d\n",
+ __func__, rc);
+ break;
+ }
+
+ case LSM_OPERATION_MODE: {
+ struct snd_lsm_detect_mode *det_mode = data;
+
+ if (det_mode->mode == LSM_MODE_KEYWORD_ONLY_DETECTION) {
+ client->mode = 0x01;
+ } else if (det_mode->mode == LSM_MODE_USER_KEYWORD_DETECTION) {
+ client->mode = 0x03;
+ } else {
+ pr_err("%s: Incorrect detection mode %d\n",
+ __func__, det_mode->mode);
+ return -EINVAL;
+ }
+
+ client->mode |= det_mode->detect_failure << 2;
+
+ param_info.module_id = p_info->module_id;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = p_info->param_id;
+
+ rc = q6lsm_send_param_opmode(client, &param_info,
+ LSM_SESSION_CMD_SET_PARAMS_V2);
+ if (rc)
+ pr_err("%s: OPERATION_MODE failed, rc %d\n",
+ __func__, rc);
+ break;
+ }
+
+ case LSM_GAIN: {
+ struct snd_lsm_gain *lsm_gain = (struct snd_lsm_gain *) data;
+ param_info.module_id = p_info->module_id;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = p_info->param_id;
+ rc = q6lsm_send_param_gain(client, lsm_gain->gain, &param_info);
+ if (rc)
+ pr_err("%s: LSM_GAIN command failed, rc %d\n",
+ __func__, rc);
+ break;
+ }
+
+ case LSM_MIN_CONFIDENCE_LEVELS:
+ param_info.module_id = p_info->module_id;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = p_info->param_id;
+ rc = q6lsm_send_confidence_levels(
+ client, &param_info, LSM_SESSION_CMD_SET_PARAMS_V2);
+ if (rc)
+ pr_err("%s: CONFIDENCE_LEVELS cmd failed, rc %d\n",
+ __func__, rc);
+ break;
+ case LSM_POLLING_ENABLE: {
+ struct snd_lsm_poll_enable *lsm_poll_enable =
+ (struct snd_lsm_poll_enable *) data;
+ param_info.module_id = p_info->module_id;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = p_info->param_id;
+ rc = q6lsm_send_param_polling_enable(
+ client, lsm_poll_enable->poll_en, &param_info,
+ LSM_SESSION_CMD_SET_PARAMS_V2);
+ if (rc)
+ pr_err("%s: POLLING ENABLE cmd failed, rc %d\n",
+ __func__, rc);
+ break;
+ }
+
+ case LSM_REG_SND_MODEL: {
+ struct mem_mapping_hdr mem_hdr = {0};
+ u32 payload_size;
+
+ if (q6common_is_instance_id_supported())
+ payload_size = p_info->param_size +
+ sizeof(struct param_hdr_v3);
+ else
+ payload_size = p_info->param_size +
+ sizeof(struct param_hdr_v2);
+
+ mem_hdr.data_payload_addr_lsw =
+ lower_32_bits(client->sound_model.phys);
+ mem_hdr.data_payload_addr_msw =
+ msm_audio_populate_upper_32_bits(
+ client->sound_model.phys),
+ mem_hdr.mem_map_handle = client->sound_model.mem_map_handle;
+
+ rc = q6lsm_set_params(client, &mem_hdr, NULL, payload_size,
+ LSM_SESSION_CMD_SET_PARAMS_V2);
+ if (rc) {
+ pr_err("%s: REG_SND_MODEL failed, rc %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = q6lsm_send_cal(client, LSM_SESSION_CMD_SET_PARAMS);
+ if (rc)
+ pr_err("%s: Failed to send lsm cal, err = %d\n",
+ __func__, rc);
+ break;
+ }
+
+ case LSM_DEREG_SND_MODEL: {
+ param_info.module_id = p_info->module_id;
+ param_info.instance_id = INSTANCE_ID_0;
+ param_info.param_id = p_info->param_id;
+ param_info.param_size = 0;
+ rc = q6lsm_pack_and_set_params(client, &param_info, NULL,
+ LSM_SESSION_CMD_SET_PARAMS_V2);
+ if (rc)
+ pr_err("%s: DEREG_SND_MODEL failed, rc %d\n",
+ __func__, rc);
+ break;
+ }
+
+ case LSM_CUSTOM_PARAMS: {
+ u32 param_size = p_info->param_size;
+
+ /* Check minimum size, V2 structure is smaller than V3 */
+ if (param_size < sizeof(struct param_hdr_v2)) {
+ pr_err("%s: Invalid param_size %d\n", __func__,
+ param_size);
+ return -EINVAL;
+ }
+
+ rc = q6lsm_set_params(client, NULL, data, param_size,
+ LSM_SESSION_CMD_SET_PARAMS_V2);
+ if (rc)
+ pr_err("%s: CUSTOM_PARAMS failed, rc %d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: wrong param_type 0x%x\n",
+ __func__, p_info->param_type);
+ }
+
+ return rc;
+}
+
+
+int q6lsm_start(struct lsm_client *client, bool wait)
+{
+ return q6lsm_cmd(client, LSM_SESSION_CMD_START, wait);
+}
+
+int q6lsm_stop(struct lsm_client *client, bool wait)
+{
+ return q6lsm_cmd(client, LSM_SESSION_CMD_STOP, wait);
+}
+
+int q6lsm_close(struct lsm_client *client)
+{
+ return q6lsm_cmd(client, LSM_SESSION_CMD_CLOSE_TX, true);
+}
+
+int q6lsm_lab_control(struct lsm_client *client, u32 enable)
+{
+ struct lsm_param_lab_enable lab_enable = {0};
+ struct param_hdr_v3 lab_enable_hdr = {0};
+ struct lsm_param_lab_config lab_config = {0};
+ struct param_hdr_v3 lab_config_hdr = {0};
+ int rc = 0;
+
+ if (!client) {
+ pr_err("%s: invalid param client %pK\n", __func__, client);
+ return -EINVAL;
+ }
+
+ /* enable/disable lab on dsp */
+ lab_enable_hdr.module_id = LSM_MODULE_ID_LAB;
+ lab_enable_hdr.instance_id = INSTANCE_ID_0;
+ lab_enable_hdr.param_id = LSM_PARAM_ID_LAB_ENABLE;
+ lab_enable_hdr.param_size = sizeof(lab_enable);
+ lab_enable.enable = (enable) ? 1 : 0;
+ rc = q6lsm_pack_and_set_params(client, &lab_enable_hdr,
+ (uint8_t *) &lab_enable,
+ LSM_SESSION_CMD_SET_PARAMS);
+ if (rc) {
+ pr_err("%s: Lab enable failed rc %d\n", __func__, rc);
+ return rc;
+ }
+ if (!enable)
+ goto exit;
+
+ /* lab session is being enabled set the config values */
+ lab_config_hdr.module_id = LSM_MODULE_ID_LAB;
+ lab_config_hdr.instance_id = INSTANCE_ID_0;
+ lab_config_hdr.param_id = LSM_PARAM_ID_LAB_CONFIG;
+ lab_config_hdr.param_size = sizeof(lab_config);
+ lab_config.minor_version = 1;
+ lab_config.wake_up_latency_ms = 250;
+ rc = q6lsm_pack_and_set_params(client, &lab_config_hdr,
+ (uint8_t *) &lab_config,
+ LSM_SESSION_CMD_SET_PARAMS);
+ if (rc) {
+ pr_err("%s: Lab config failed rc %d disable lab\n",
+ __func__, rc);
+ /* Lab config failed disable lab */
+ lab_enable.enable = 0;
+ if (q6lsm_pack_and_set_params(client, &lab_enable_hdr,
+ (uint8_t *) &lab_enable,
+ LSM_SESSION_CMD_SET_PARAMS))
+ pr_err("%s: Lab disable failed\n", __func__);
+ }
+exit:
+ return rc;
+}
+
+int q6lsm_stop_lab(struct lsm_client *client)
+{
+ int rc = 0;
+ if (!client) {
+ pr_err("%s: invalid param client %pK\n", __func__, client);
+ return -EINVAL;
+ }
+ rc = q6lsm_cmd(client, LSM_SESSION_CMD_EOB, true);
+ if (rc)
+ pr_err("%s: Lab stop failed %d\n", __func__, rc);
+ return rc;
+}
+
+int q6lsm_read(struct lsm_client *client, struct lsm_cmd_read *read)
+{
+ int rc = 0;
+ if (!client || !read) {
+ pr_err("%s: Invalid params client %pK read %pK\n", __func__,
+ client, read);
+ return -EINVAL;
+ }
+ pr_debug("%s: read call memmap handle %x address %x%x size %d\n",
+ __func__, read->mem_map_handle, read->buf_addr_msw,
+ read->buf_addr_lsw, read->buf_size);
+ q6lsm_add_hdr(client, &read->hdr, sizeof(struct lsm_cmd_read), true);
+ read->hdr.opcode = LSM_SESSION_CMD_READ;
+ rc = q6lsm_apr_send_pkt(client, client->apr, read, false, NULL);
+ if (rc)
+ pr_err("%s: read buffer call failed rc %d\n", __func__, rc);
+ return rc;
+}
+
+int q6lsm_lab_buffer_alloc(struct lsm_client *client, bool alloc)
+{
+ int ret = 0, i = 0;
+ size_t allocate_size = 0, len = 0;
+ if (!client) {
+ pr_err("%s: invalid client\n", __func__);
+ return -EINVAL;
+ }
+ if (alloc) {
+ if (client->lab_buffer) {
+ pr_err("%s: buffers are allocated period count %d period size %d\n",
+ __func__,
+ client->hw_params.period_count,
+ client->hw_params.buf_sz);
+ return -EINVAL;
+ }
+ allocate_size = client->hw_params.period_count *
+ client->hw_params.buf_sz;
+ allocate_size = PAGE_ALIGN(allocate_size);
+ client->lab_buffer =
+ kzalloc(sizeof(struct lsm_lab_buffer) *
+ client->hw_params.period_count, GFP_KERNEL);
+ if (!client->lab_buffer) {
+ pr_err("%s: memory allocation for lab buffer failed count %d\n"
+ , __func__,
+ client->hw_params.period_count);
+ return -ENOMEM;
+ }
+ ret = msm_audio_ion_alloc("lsm_lab",
+ &client->lab_buffer[0].client,
+ &client->lab_buffer[0].handle,
+ allocate_size, &client->lab_buffer[0].phys,
+ &len,
+ &client->lab_buffer[0].data);
+ if (ret)
+ pr_err("%s: ion alloc failed ret %d size %zd\n",
+ __func__, ret, allocate_size);
+ else {
+ ret = q6lsm_memory_map_regions(client,
+ client->lab_buffer[0].phys, len,
+ &client->lab_buffer[0].mem_map_handle);
+ if (ret) {
+ pr_err("%s: memory map filed ret %d size %zd\n",
+ __func__, ret, len);
+ msm_audio_ion_free(
+ client->lab_buffer[0].client,
+ client->lab_buffer[0].handle);
+ }
+ }
+ if (ret) {
+ pr_err("%s: alloc lab buffer failed ret %d\n",
+ __func__, ret);
+ kfree(client->lab_buffer);
+ client->lab_buffer = NULL;
+ } else {
+ pr_debug("%s: Memory map handle %x phys %pK size %d\n",
+ __func__,
+ client->lab_buffer[0].mem_map_handle,
+ &client->lab_buffer[0].phys,
+ client->hw_params.buf_sz);
+ for (i = 0; i < client->hw_params.period_count; i++) {
+ client->lab_buffer[i].phys =
+ client->lab_buffer[0].phys +
+ (i * client->hw_params.buf_sz);
+ client->lab_buffer[i].size =
+ client->hw_params.buf_sz;
+ client->lab_buffer[i].data =
+ (u8 *)(client->lab_buffer[0].data) +
+ (i * client->hw_params.buf_sz);
+ client->lab_buffer[i].mem_map_handle =
+ client->lab_buffer[0].mem_map_handle;
+ }
+ }
+ } else {
+ ret = q6lsm_memory_unmap_regions(client,
+ client->lab_buffer[0].mem_map_handle);
+ if (!ret)
+ msm_audio_ion_free(
+ client->lab_buffer[0].client,
+ client->lab_buffer[0].handle);
+ else
+ pr_err("%s: unmap failed not freeing memory\n",
+ __func__);
+ kfree(client->lab_buffer);
+ client->lab_buffer = NULL;
+ }
+ return ret;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+ int ret = -EINVAL;
+
+ switch (cal_type) {
+ case LSM_CUST_TOPOLOGY_CAL_TYPE:
+ ret = LSM_CUSTOM_TOP_IDX;
+ break;
+ case LSM_TOPOLOGY_CAL_TYPE:
+ ret = LSM_TOP_IDX;
+ break;
+ case LSM_CAL_TYPE:
+ ret = LSM_CAL_IDX;
+ break;
+ default:
+ pr_err("%s: invalid cal type %d!\n", __func__, cal_type);
+ }
+ return ret;
+}
+
+static int q6lsm_alloc_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_alloc_cal(data_size, data,
+ lsm_common.cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int q6lsm_dealloc_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_dealloc_cal(data_size, data,
+ lsm_common.cal_data[cal_index]);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int q6lsm_set_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s:\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_set_cal(data_size, data,
+ lsm_common.cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (cal_index == LSM_CUSTOM_TOP_IDX) {
+ mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+ lsm_common.set_custom_topology = 1;
+ mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+ }
+
+done:
+ return ret;
+}
+
+static void lsm_delete_cal_data(void)
+{
+ pr_debug("%s:\n", __func__);
+
+ cal_utils_destroy_cal_types(LSM_MAX_CAL_IDX, lsm_common.cal_data);
+ return;
+}
+
+static int q6lsm_init_cal_data(void)
+{
+ int ret = 0;
+ struct cal_type_info cal_type_info[] = {
+ {{LSM_CUST_TOPOLOGY_CAL_TYPE,
+ {q6lsm_alloc_cal, q6lsm_dealloc_cal, NULL,
+ q6lsm_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{LSM_TOPOLOGY_CAL_TYPE,
+ {NULL, NULL, NULL,
+ q6lsm_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{LSM_CAL_TYPE,
+ {q6lsm_alloc_cal, q6lsm_dealloc_cal, NULL,
+ q6lsm_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} }
+ };
+ pr_debug("%s:\n", __func__);
+
+ ret = cal_utils_create_cal_types(LSM_MAX_CAL_IDX,
+ lsm_common.cal_data, cal_type_info);
+ if (ret < 0) {
+ pr_err("%s: could not create cal type!\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return ret;
+err:
+ lsm_delete_cal_data();
+ return ret;
+}
+
+static int __init q6lsm_init(void)
+{
+ int i = 0;
+ pr_debug("%s:\n", __func__);
+
+ memset(&lsm_common, 0, sizeof(struct lsm_common));
+ spin_lock_init(&lsm_session_lock);
+ spin_lock_init(&mmap_lock);
+ mutex_init(&lsm_common.apr_lock);
+ for (; i <= LSM_MAX_SESSION_ID; i++) {
+ lsm_common.common_client[i].session = LSM_CONTROL_SESSION;
+ init_waitqueue_head(&lsm_common.common_client[i].cmd_wait);
+ mutex_init(&lsm_common.common_client[i].cmd_lock);
+ atomic_set(&lsm_common.common_client[i].cmd_state,
+ CMD_STATE_CLEARED);
+ }
+
+ if (q6lsm_init_cal_data())
+ pr_err("%s: could not init cal data!\n", __func__);
+
+ return 0;
+}
+
+static void __exit q6lsm_exit(void)
+{
+ lsm_delete_cal_data();
+}
+
+device_initcall(q6lsm_init);
+__exitcall(q6lsm_exit);
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
new file mode 100644
index 000000000000..e437a1c7985f
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -0,0 +1,8828 @@
+/* Copyright (c) 2012-2018, 2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/msm_audio_ion.h>
+
+#include <soc/qcom/socinfo.h>
+#include <linux/qdsp6v2/apr_tal.h>
+
+#include "sound/q6audio-v2.h"
+#include "sound/apr_audio-v2.h"
+#include "sound/q6afe-v2.h"
+#include <sound/q6common.h>
+#include <sound/audio_cal_utils.h>
+#include "q6voice.h"
+#include <sound/adsp_err.h>
+
+#define TIMEOUT_MS 300
+
+
+#define CMD_STATUS_SUCCESS 0
+#define CMD_STATUS_FAIL 1
+
+enum {
+ VOC_TOKEN_NONE,
+ VOIP_MEM_MAP_TOKEN,
+ VOC_CAL_MEM_MAP_TOKEN,
+ VOC_VOICE_HOST_PCM_MAP_TOKEN,
+ VOC_RTAC_MEM_MAP_TOKEN,
+ VOC_SOURCE_TRACKING_MEM_MAP_TOKEN
+};
+
+struct cvd_version_table cvd_version_table_mapping[CVD_INT_VERSION_MAX] = {
+ {CVD_VERSION_DEFAULT, CVD_INT_VERSION_DEFAULT},
+ {CVD_VERSION_0_0, CVD_INT_VERSION_0_0},
+ {CVD_VERSION_2_1, CVD_INT_VERSION_2_1},
+ {CVD_VERSION_2_2, CVD_INT_VERSION_2_2},
+ {CVD_VERSION_2_3, CVD_INT_VERSION_2_3},
+};
+
+static struct common_data common;
+static bool module_initialized;
+
+static int voice_send_enable_vocproc_cmd(struct voice_data *v);
+static int voice_send_netid_timing_cmd(struct voice_data *v);
+static int voice_send_attach_vocproc_cmd(struct voice_data *v);
+static int voice_send_set_device_cmd(struct voice_data *v);
+static int voice_send_vol_step_cmd(struct voice_data *v);
+static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v,
+ uint32_t mem_handle);
+static int voice_send_mvm_cal_network_cmd(struct voice_data *v);
+static int voice_send_mvm_media_type_cmd(struct voice_data *v);
+static int voice_send_mvm_cvd_version_cmd(struct voice_data *v);
+static int voice_send_cvs_data_exchange_mode_cmd(struct voice_data *v);
+static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v);
+static int voice_set_packet_exchange_mode_and_config(uint32_t session_id,
+ uint32_t mode);
+
+static int voice_send_cvs_register_cal_cmd(struct voice_data *v);
+static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_create_cmd(struct voice_data *v);
+static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v);
+static int voice_send_cvp_register_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_media_fmt_info_cmd(struct voice_data *v);
+static int voice_send_cvp_device_channels_cmd(struct voice_data *v);
+static int voice_send_cvp_media_format_cmd(struct voice_data *v,
+ uint32_t param_type);
+static int voice_send_cvp_topology_commit_cmd(struct voice_data *v);
+
+static int voice_cvs_stop_playback(struct voice_data *v);
+static int voice_cvs_start_playback(struct voice_data *v);
+static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode);
+static int voice_cvs_stop_record(struct voice_data *v);
+
+static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv);
+static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv);
+static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv);
+
+static int voice_send_set_pp_enable_cmd(
+ struct voice_data *v, struct module_instance_info mod_inst_info,
+ int enable);
+static int is_cal_memory_allocated(void);
+static bool is_cvd_version_queried(void);
+static int is_voip_memory_allocated(void);
+static int voice_get_cvd_int_version(char *cvd_ver_string);
+static int voice_alloc_cal_mem_map_table(void);
+static int voice_alloc_rtac_mem_map_table(void);
+static int voice_alloc_oob_shared_mem(void);
+static int voice_free_oob_shared_mem(void);
+static int voice_alloc_oob_mem_table(void);
+static int voice_alloc_and_map_oob_mem(struct voice_data *v);
+static void voice_vote_powerstate_to_bms(struct voice_data *v, bool state);
+
+static struct voice_data *voice_get_session_by_idx(int idx);
+
+static int remap_cal_data(struct cal_block_data *cal_block,
+ uint32_t session_id);
+static int voice_unmap_cal_memory(int32_t cal_type,
+ struct cal_block_data *cal_block);
+
+static int is_source_tracking_shared_memomry_allocated(void);
+static int voice_alloc_source_tracking_shared_memory(void);
+static int voice_alloc_and_map_source_tracking_shared_memory(
+ struct voice_data *v);
+static int voice_unmap_and_free_source_tracking_shared_memory(
+ struct voice_data *v);
+static int voice_send_set_sound_focus_cmd(struct voice_data *v,
+ struct sound_focus_param soundFocusData);
+static int voice_send_get_sound_focus_cmd(struct voice_data *v,
+ struct sound_focus_param *soundFocusData);
+static int voice_send_get_source_tracking_cmd(struct voice_data *v,
+ struct source_tracking_param *sourceTrackingData);
+static int voice_pack_and_set_cvp_param(struct voice_data *v,
+ struct param_hdr_v3 param_hdr,
+ u8 *param_data);
+static int voice_pack_and_set_cvs_ui_property(struct voice_data *v,
+ struct param_hdr_v3 param_hdr,
+ u8 *param_data);
+
+static void voice_itr_init(struct voice_session_itr *itr,
+ u32 session_id)
+{
+ if (itr == NULL)
+ return;
+ itr->session_idx = voice_get_idx_for_session(session_id);
+ if (session_id == ALL_SESSION_VSID)
+ itr->cur_idx = 0;
+ else
+ itr->cur_idx = itr->session_idx;
+
+}
+
+static bool voice_itr_get_next_session(struct voice_session_itr *itr,
+ struct voice_data **voice)
+{
+ bool ret = false;
+
+ if (itr == NULL)
+ return false;
+ pr_debug("%s : cur idx = %d session idx = %d\n",
+ __func__, itr->cur_idx, itr->session_idx);
+
+ if (itr->cur_idx <= itr->session_idx) {
+ ret = true;
+ *voice = voice_get_session_by_idx(itr->cur_idx);
+ itr->cur_idx++;
+ } else {
+ *voice = NULL;
+ }
+
+ return ret;
+}
+
+static bool voice_is_valid_session_id(uint32_t session_id)
+{
+ bool ret = false;
+
+ switch (session_id) {
+ case VOICE_SESSION_VSID:
+ case VOICE2_SESSION_VSID:
+ case VOLTE_SESSION_VSID:
+ case VOIP_SESSION_VSID:
+ case QCHAT_SESSION_VSID:
+ case VOWLAN_SESSION_VSID:
+ case VOICEMMODE1_VSID:
+ case VOICEMMODE2_VSID:
+ case ALL_SESSION_VSID:
+ ret = true;
+ break;
+ default:
+ pr_err("%s: Invalid session_id : %x\n", __func__, session_id);
+
+ break;
+ }
+
+ return ret;
+}
+
+static u16 voice_get_mvm_handle(struct voice_data *v)
+{
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return 0;
+ }
+
+ pr_debug("%s: mvm_handle %d\n", __func__, v->mvm_handle);
+
+ return v->mvm_handle;
+}
+
+static void voice_set_mvm_handle(struct voice_data *v, u16 mvm_handle)
+{
+ pr_debug("%s: mvm_handle %d\n", __func__, mvm_handle);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return;
+ }
+
+ v->mvm_handle = mvm_handle;
+}
+
+static u16 voice_get_cvs_handle(struct voice_data *v)
+{
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return 0;
+ }
+
+ pr_debug("%s: cvs_handle %d\n", __func__, v->cvs_handle);
+
+ return v->cvs_handle;
+}
+
+static void voice_set_cvs_handle(struct voice_data *v, u16 cvs_handle)
+{
+ pr_debug("%s: cvs_handle %d\n", __func__, cvs_handle);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return;
+ }
+
+ v->cvs_handle = cvs_handle;
+}
+
+static u16 voice_get_cvp_handle(struct voice_data *v)
+{
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return 0;
+ }
+
+ pr_debug("%s: cvp_handle %d\n", __func__, v->cvp_handle);
+
+ return v->cvp_handle;
+}
+
+static void voice_set_cvp_handle(struct voice_data *v, u16 cvp_handle)
+{
+ pr_debug("%s: cvp_handle %d\n", __func__, cvp_handle);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return;
+ }
+
+ v->cvp_handle = cvp_handle;
+}
+
+char *voc_get_session_name(u32 session_id)
+{
+ char *session_name = NULL;
+
+ if (session_id == common.voice[VOC_PATH_PASSIVE].session_id) {
+ session_name = VOICE_SESSION_NAME;
+ } else if (session_id ==
+ common.voice[VOC_PATH_VOLTE_PASSIVE].session_id) {
+ session_name = VOLTE_SESSION_NAME;
+ } else if (session_id ==
+ common.voice[VOC_PATH_QCHAT_PASSIVE].session_id) {
+ session_name = QCHAT_SESSION_NAME;
+ } else if (session_id ==
+ common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id) {
+ session_name = VOWLAN_SESSION_NAME;
+ } else if (session_id ==
+ common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id) {
+ session_name = VOICEMMODE1_NAME;
+ } else if (session_id ==
+ common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id) {
+ session_name = VOICEMMODE2_NAME;
+ } else if (session_id == common.voice[VOC_PATH_FULL].session_id) {
+ session_name = VOIP_SESSION_NAME;
+ }
+ return session_name;
+}
+
+uint32_t voc_get_session_id(char *name)
+{
+ u32 session_id = 0;
+
+ if (name != NULL) {
+ if (!strncmp(name, "Voice session", 13))
+ session_id = common.voice[VOC_PATH_PASSIVE].session_id;
+ else if (!strncmp(name, "Voice2 session", 14))
+ session_id =
+ common.voice[VOC_PATH_VOICE2_PASSIVE].session_id;
+ else if (!strncmp(name, "VoLTE session", 13))
+ session_id =
+ common.voice[VOC_PATH_VOLTE_PASSIVE].session_id;
+ else if (!strncmp(name, "QCHAT session", 13))
+ session_id =
+ common.voice[VOC_PATH_QCHAT_PASSIVE].session_id;
+ else if (!strncmp(name, "VoWLAN session", 14))
+ session_id =
+ common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id;
+ else if (!strcmp(name, "VoiceMMode1"))
+ session_id =
+ common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id;
+ else if (!strcmp(name, "VoiceMMode2"))
+ session_id =
+ common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id;
+ else
+ session_id = common.voice[VOC_PATH_FULL].session_id;
+
+ pr_debug("%s: %s has session id 0x%x\n", __func__, name,
+ session_id);
+ }
+
+ return session_id;
+}
+
+static struct voice_data *voice_get_session(u32 session_id)
+{
+ struct voice_data *v = NULL;
+
+ switch (session_id) {
+ case VOICE_SESSION_VSID:
+ v = &common.voice[VOC_PATH_PASSIVE];
+ break;
+
+ case VOICE2_SESSION_VSID:
+ v = &common.voice[VOC_PATH_VOICE2_PASSIVE];
+ break;
+
+ case VOLTE_SESSION_VSID:
+ v = &common.voice[VOC_PATH_VOLTE_PASSIVE];
+ break;
+
+ case VOIP_SESSION_VSID:
+ v = &common.voice[VOC_PATH_FULL];
+ break;
+
+ case QCHAT_SESSION_VSID:
+ v = &common.voice[VOC_PATH_QCHAT_PASSIVE];
+ break;
+
+ case VOWLAN_SESSION_VSID:
+ v = &common.voice[VOC_PATH_VOWLAN_PASSIVE];
+ break;
+
+ case VOICEMMODE1_VSID:
+ v = &common.voice[VOC_PATH_VOICEMMODE1_PASSIVE];
+ break;
+
+ case VOICEMMODE2_VSID:
+ v = &common.voice[VOC_PATH_VOICEMMODE2_PASSIVE];
+ break;
+
+ case ALL_SESSION_VSID:
+ break;
+
+ default:
+ pr_err("%s: Invalid session_id : %x\n", __func__, session_id);
+
+ break;
+ }
+
+ pr_debug("%s:session_id 0x%x session handle %pK\n",
+ __func__, session_id, v);
+
+ return v;
+}
+
+int voice_get_idx_for_session(u32 session_id)
+{
+ int idx = 0;
+
+ switch (session_id) {
+ case VOICE_SESSION_VSID:
+ idx = VOC_PATH_PASSIVE;
+ break;
+
+ case VOICE2_SESSION_VSID:
+ idx = VOC_PATH_VOICE2_PASSIVE;
+ break;
+
+ case VOLTE_SESSION_VSID:
+ idx = VOC_PATH_VOLTE_PASSIVE;
+ break;
+
+ case VOIP_SESSION_VSID:
+ idx = VOC_PATH_FULL;
+ break;
+
+ case QCHAT_SESSION_VSID:
+ idx = VOC_PATH_QCHAT_PASSIVE;
+ break;
+
+ case VOWLAN_SESSION_VSID:
+ idx = VOC_PATH_VOWLAN_PASSIVE;
+ break;
+
+ case VOICEMMODE1_VSID:
+ idx = VOC_PATH_VOICEMMODE1_PASSIVE;
+ break;
+
+ case VOICEMMODE2_VSID:
+ idx = VOC_PATH_VOICEMMODE2_PASSIVE;
+ break;
+
+ case ALL_SESSION_VSID:
+ idx = MAX_VOC_SESSIONS - 1;
+ break;
+
+ default:
+ pr_err("%s: Invalid session_id : %x\n", __func__, session_id);
+
+ break;
+ }
+
+ return idx;
+}
+
+static struct voice_data *voice_get_session_by_idx(int idx)
+{
+ return ((idx < 0 || idx >= MAX_VOC_SESSIONS) ?
+ NULL : &common.voice[idx]);
+}
+
+static bool is_voip_session(u32 session_id)
+{
+ return (session_id == common.voice[VOC_PATH_FULL].session_id);
+}
+
+static bool is_volte_session(u32 session_id)
+{
+ return (session_id == common.voice[VOC_PATH_VOLTE_PASSIVE].session_id);
+}
+
+static bool is_voice2_session(u32 session_id)
+{
+ return (session_id == common.voice[VOC_PATH_VOICE2_PASSIVE].session_id);
+}
+
+static bool is_qchat_session(u32 session_id)
+{
+ return (session_id == common.voice[VOC_PATH_QCHAT_PASSIVE].session_id);
+}
+
+static bool is_vowlan_session(u32 session_id)
+{
+ return (session_id == common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id);
+}
+
+static bool is_voicemmode1(u32 session_id)
+{
+ return session_id ==
+ common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id;
+}
+
+static bool is_voicemmode2(u32 session_id)
+{
+ return session_id ==
+ common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id;
+}
+
+static bool is_voc_state_active(int voc_state)
+{
+ if ((voc_state == VOC_RUN) ||
+ (voc_state == VOC_CHANGE) ||
+ (voc_state == VOC_STANDBY))
+ return true;
+
+ return false;
+}
+
+static void voc_set_error_state(uint16_t reset_proc)
+{
+ struct voice_data *v = NULL;
+ int i;
+
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ v = &common.voice[i];
+ if (v != NULL) {
+ v->voc_state = VOC_ERROR;
+ v->rec_info.recording = 0;
+ }
+ }
+}
+
+static bool is_other_session_active(u32 session_id)
+{
+ int i;
+ bool ret = false;
+
+ /* Check if there is other active session except the input one */
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ if (common.voice[i].session_id == session_id)
+ continue;
+
+ if (is_voc_state_active(common.voice[i].voc_state)) {
+ ret = true;
+ break;
+ }
+ }
+ pr_debug("%s: ret %d\n", __func__, ret);
+
+ return ret;
+}
+
+static bool is_sub1_vsid(u32 session_id)
+{
+ bool ret;
+
+ switch (session_id) {
+ case VOICE_SESSION_VSID:
+ case VOLTE_SESSION_VSID:
+ case VOWLAN_SESSION_VSID:
+ case VOICEMMODE1_VSID:
+ ret = true;
+ break;
+ default:
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool is_sub2_vsid(u32 session_id)
+{
+ bool ret;
+
+ switch (session_id) {
+ case VOICE2_SESSION_VSID:
+ case VOICEMMODE2_VSID:
+ ret = true;
+ break;
+ default:
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool is_voice_app_id(u32 session_id)
+{
+ return is_sub1_vsid(session_id) || is_sub2_vsid(session_id);
+}
+
+static void init_session_id(void)
+{
+ common.voice[VOC_PATH_PASSIVE].session_id = VOICE_SESSION_VSID;
+ common.voice[VOC_PATH_VOLTE_PASSIVE].session_id = VOLTE_SESSION_VSID;
+ common.voice[VOC_PATH_VOICE2_PASSIVE].session_id = VOICE2_SESSION_VSID;
+ common.voice[VOC_PATH_FULL].session_id = VOIP_SESSION_VSID;
+ common.voice[VOC_PATH_QCHAT_PASSIVE].session_id = QCHAT_SESSION_VSID;
+ common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id = VOWLAN_SESSION_VSID;
+ common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id =
+ VOICEMMODE1_VSID;
+ common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id =
+ VOICEMMODE2_VSID;
+}
+
+static bool is_cvd_version_queried(void)
+{
+ bool ret = 0;
+
+ if (!strcmp(common.cvd_version, CVD_VERSION_DEFAULT))
+ ret = false;
+ else
+ ret = true;
+
+ return ret;
+}
+
+static int voice_get_cvd_int_version(char *cvd_ver_string)
+{
+ unsigned int idx;
+ int cvd_int_ver = CVD_INT_VERSION_DEFAULT;
+
+ for (idx = 0; idx < CVD_INT_VERSION_MAX; idx++) {
+ if (strcmp((char *)cvd_ver_string,
+ cvd_version_table_mapping[idx].cvd_ver) == 0) {
+ cvd_int_ver =
+ cvd_version_table_mapping[idx].cvd_ver_int;
+ break;
+ }
+ }
+ return cvd_int_ver;
+}
+
+static int voice_apr_register(uint32_t session_id)
+{
+
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&common.common_lock);
+
+ /* register callback to APR */
+ if (common.apr_q6_mvm == NULL) {
+ pr_debug("%s: Start to register MVM callback\n", __func__);
+
+ common.apr_q6_mvm = apr_register("ADSP", "MVM",
+ qdsp_mvm_callback,
+ 0xFFFFFFFF, &common);
+
+ if (common.apr_q6_mvm == NULL) {
+ pr_err("%s: Unable to register MVM\n", __func__);
+ goto err;
+ }
+ }
+
+ if (common.apr_q6_cvs == NULL) {
+ pr_debug("%s: Start to register CVS callback\n", __func__);
+
+ common.apr_q6_cvs = apr_register("ADSP", "CVS",
+ qdsp_cvs_callback,
+ 0xFFFFFFFF, &common);
+
+ if (common.apr_q6_cvs == NULL) {
+ pr_err("%s: Unable to register CVS\n", __func__);
+ goto err;
+ }
+ rtac_set_voice_handle(RTAC_CVS, common.apr_q6_cvs);
+ }
+
+ if (common.apr_q6_cvp == NULL) {
+ pr_debug("%s: Start to register CVP callback\n", __func__);
+
+ common.apr_q6_cvp = apr_register("ADSP", "CVP",
+ qdsp_cvp_callback,
+ 0xFFFFFFFF, &common);
+
+ if (common.apr_q6_cvp == NULL) {
+ pr_err("%s: Unable to register CVP\n", __func__);
+ goto err;
+ }
+ rtac_set_voice_handle(RTAC_CVP, common.apr_q6_cvp);
+ }
+
+ mutex_unlock(&common.common_lock);
+
+ return 0;
+
+err:
+ if (common.apr_q6_cvs != NULL) {
+ apr_deregister(common.apr_q6_cvs);
+ common.apr_q6_cvs = NULL;
+ rtac_set_voice_handle(RTAC_CVS, NULL);
+ }
+ if (common.apr_q6_mvm != NULL) {
+ apr_deregister(common.apr_q6_mvm);
+ common.apr_q6_mvm = NULL;
+ }
+
+ mutex_unlock(&common.common_lock);
+
+ return -ENODEV;
+}
+
+static int voice_send_mvm_cvd_version_cmd(struct voice_data *v)
+{
+ int ret;
+ struct apr_hdr cvd_version_get_cmd;
+ void *apr_mvm;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ apr_mvm = common.apr_q6_mvm;
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Send command to CVD to retrieve Version */
+ cvd_version_get_cmd.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvd_version_get_cmd.pkt_size = APR_PKT_SIZE(
+ APR_HDR_SIZE,
+ sizeof(cvd_version_get_cmd) -
+ APR_HDR_SIZE);
+ cvd_version_get_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvd_version_get_cmd.dest_port = 0;
+ cvd_version_get_cmd.token = 0;
+ cvd_version_get_cmd.opcode = VSS_IVERSION_CMD_GET;
+
+ pr_debug("%s: send CVD version get cmd, pkt size = %d\n",
+ __func__, cvd_version_get_cmd.pkt_size);
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm,
+ (uint32_t *) &cvd_version_get_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error sending command\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout, fall back to default\n",
+ __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+ ret = 0;
+
+done:
+ if (ret) {
+ strlcpy(common.cvd_version, CVD_VERSION_0_0,
+ sizeof(common.cvd_version));
+ }
+ pr_debug("%s: CVD Version retrieved=%s\n",
+ __func__, common.cvd_version);
+
+ return ret;
+}
+
+static int voice_send_dual_control_cmd(struct voice_data *v)
+{
+ int ret = 0;
+ struct mvm_modem_dual_control_session_cmd mvm_voice_ctl_cmd;
+ void *apr_mvm;
+ u16 mvm_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: Send Dual Control command to MVM\n", __func__);
+ if (!is_voip_session(v->session_id)) {
+ mvm_handle = voice_get_mvm_handle(v);
+ mvm_voice_ctl_cmd.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_voice_ctl_cmd.hdr.pkt_size = APR_PKT_SIZE(
+ APR_HDR_SIZE,
+ sizeof(mvm_voice_ctl_cmd) -
+ APR_HDR_SIZE);
+ pr_debug("%s: send mvm Voice Ctl pkt size = %d\n",
+ __func__, mvm_voice_ctl_cmd.hdr.pkt_size);
+ mvm_voice_ctl_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_voice_ctl_cmd.hdr.dest_port = mvm_handle;
+ mvm_voice_ctl_cmd.hdr.token = 0;
+ mvm_voice_ctl_cmd.hdr.opcode =
+ VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL;
+ mvm_voice_ctl_cmd.voice_ctl.enable_flag = true;
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_voice_ctl_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error sending MVM Voice CTL CMD\n",
+ __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ }
+ ret = 0;
+fail:
+ return ret;
+}
+
+
+static int voice_create_mvm_cvs_session(struct voice_data *v)
+{
+ int ret = 0;
+ struct mvm_create_ctl_session_cmd mvm_session_cmd;
+ struct cvs_create_passive_ctl_session_cmd cvs_session_cmd;
+ struct cvs_create_full_ctl_session_cmd cvs_full_ctl_cmd;
+ struct mvm_attach_stream_cmd attach_stream_cmd;
+ void *apr_mvm, *apr_cvs, *apr_cvp;
+ u16 mvm_handle, cvs_handle, cvp_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+ apr_cvs = common.apr_q6_cvs;
+ apr_cvp = common.apr_q6_cvp;
+
+ if (!apr_mvm || !apr_cvs || !apr_cvp) {
+ pr_err("%s: apr_mvm or apr_cvs or apr_cvp is NULL\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+ cvs_handle = voice_get_cvs_handle(v);
+ cvp_handle = voice_get_cvp_handle(v);
+
+ pr_debug("%s: mvm_hdl=%d, cvs_hdl=%d\n", __func__,
+ mvm_handle, cvs_handle);
+ /* send cmd to create mvm session and wait for response */
+
+ if (!mvm_handle) {
+ memset(mvm_session_cmd.mvm_session.name, 0,
+ sizeof(mvm_session_cmd.mvm_session.name));
+ if (!is_voip_session(v->session_id)) {
+ mvm_session_cmd.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_session_cmd.hdr.pkt_size = APR_PKT_SIZE(
+ APR_HDR_SIZE,
+ sizeof(mvm_session_cmd) -
+ APR_HDR_SIZE);
+ pr_debug("%s: send mvm create session pkt size = %d\n",
+ __func__, mvm_session_cmd.hdr.pkt_size);
+ mvm_session_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_session_cmd.hdr.dest_port = 0;
+ mvm_session_cmd.hdr.token = 0;
+ mvm_session_cmd.hdr.opcode =
+ VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION;
+ if (is_volte_session(v->session_id)) {
+ strlcpy(mvm_session_cmd.mvm_session.name,
+ "default volte voice",
+ strlen("default volte voice")+1);
+ } else if (is_voice2_session(v->session_id)) {
+ strlcpy(mvm_session_cmd.mvm_session.name,
+ VOICE2_SESSION_VSID_STR,
+ strlen(VOICE2_SESSION_VSID_STR)+1);
+ } else if (is_qchat_session(v->session_id)) {
+ strlcpy(mvm_session_cmd.mvm_session.name,
+ QCHAT_SESSION_VSID_STR,
+ strlen(QCHAT_SESSION_VSID_STR)+1);
+ } else if (is_vowlan_session(v->session_id)) {
+ strlcpy(mvm_session_cmd.mvm_session.name,
+ VOWLAN_SESSION_VSID_STR,
+ strlen(VOWLAN_SESSION_VSID_STR)+1);
+ } else if (is_voicemmode1(v->session_id)) {
+ strlcpy(mvm_session_cmd.mvm_session.name,
+ VOICEMMODE1_VSID_STR,
+ strlen(VOICEMMODE1_VSID_STR) + 1);
+ } else if (is_voicemmode2(v->session_id)) {
+ strlcpy(mvm_session_cmd.mvm_session.name,
+ VOICEMMODE2_VSID_STR,
+ strlen(VOICEMMODE2_VSID_STR) + 1);
+ } else {
+ strlcpy(mvm_session_cmd.mvm_session.name,
+ "default modem voice",
+ strlen("default modem voice")+1);
+ }
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_mvm,
+ (uint32_t *) &mvm_session_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error sending MVM_CONTROL_SESSION\n",
+ __func__);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ } else {
+ pr_debug("%s: creating MVM full ctrl\n", __func__);
+ mvm_session_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mvm_session_cmd.hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_session_cmd) -
+ APR_HDR_SIZE);
+ mvm_session_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_session_cmd.hdr.dest_port = 0;
+ mvm_session_cmd.hdr.token = 0;
+ mvm_session_cmd.hdr.opcode =
+ VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION;
+ strlcpy(mvm_session_cmd.mvm_session.name,
+ "default voip",
+ strlen("default voip")+1);
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_mvm,
+ (uint32_t *) &mvm_session_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending MVM_CONTROL_SESSION\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ }
+ /* Get the created MVM handle. */
+ mvm_handle = voice_get_mvm_handle(v);
+ }
+ /* send cmd to create cvs session */
+ if (!cvs_handle) {
+ memset(cvs_session_cmd.cvs_session.name, 0,
+ sizeof(cvs_session_cmd.cvs_session.name));
+ if (!is_voip_session(v->session_id)) {
+ pr_debug("%s: creating CVS passive session\n",
+ __func__);
+
+ cvs_session_cmd.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_session_cmd.hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_session_cmd) -
+ APR_HDR_SIZE);
+ cvs_session_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_session_cmd.hdr.dest_port = 0;
+ cvs_session_cmd.hdr.token = 0;
+ cvs_session_cmd.hdr.opcode =
+ VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION;
+ if (is_volte_session(v->session_id)) {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ "default volte voice",
+ strlen("default volte voice")+1);
+ } else if (is_voice2_session(v->session_id)) {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ VOICE2_SESSION_VSID_STR,
+ strlen(VOICE2_SESSION_VSID_STR)+1);
+ } else if (is_qchat_session(v->session_id)) {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ QCHAT_SESSION_VSID_STR,
+ strlen(QCHAT_SESSION_VSID_STR)+1);
+ } else if (is_vowlan_session(v->session_id)) {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ VOWLAN_SESSION_VSID_STR,
+ strlen(VOWLAN_SESSION_VSID_STR)+1);
+ } else if (is_voicemmode1(v->session_id)) {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ VOICEMMODE1_VSID_STR,
+ strlen(VOICEMMODE1_VSID_STR) + 1);
+ } else if (is_voicemmode2(v->session_id)) {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ VOICEMMODE2_VSID_STR,
+ strlen(VOICEMMODE2_VSID_STR) + 1);
+ } else {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ "default modem voice",
+ strlen("default modem voice")+1);
+ }
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvs,
+ (uint32_t *) &cvs_session_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending STREAM_CONTROL_SESSION\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ /* Get the created CVS handle. */
+ cvs_handle = voice_get_cvs_handle(v);
+
+ } else {
+ pr_debug("%s: creating CVS full session\n", __func__);
+
+ cvs_full_ctl_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+
+ cvs_full_ctl_cmd.hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_full_ctl_cmd) -
+ APR_HDR_SIZE);
+
+ cvs_full_ctl_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_full_ctl_cmd.hdr.dest_port = 0;
+ cvs_full_ctl_cmd.hdr.token = 0;
+ cvs_full_ctl_cmd.hdr.opcode =
+ VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION;
+ cvs_full_ctl_cmd.cvs_session.direction = 2;
+ cvs_full_ctl_cmd.cvs_session.enc_media_type =
+ common.mvs_info.media_type;
+ cvs_full_ctl_cmd.cvs_session.dec_media_type =
+ common.mvs_info.media_type;
+ cvs_full_ctl_cmd.cvs_session.network_id =
+ common.mvs_info.network_type;
+ strlcpy(cvs_full_ctl_cmd.cvs_session.name,
+ "default q6 voice",
+ strlen("default q6 voice")+1);
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvs,
+ (uint32_t *) &cvs_full_ctl_cmd);
+
+ if (ret < 0) {
+ pr_err("%s: Err %d sending CREATE_FULL_CTRL\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ /* Get the created CVS handle. */
+ cvs_handle = voice_get_cvs_handle(v);
+
+ /* Attach MVM to CVS. */
+ pr_debug("%s: Attach MVM to stream\n", __func__);
+
+ attach_stream_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ attach_stream_cmd.hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(attach_stream_cmd) -
+ APR_HDR_SIZE);
+ attach_stream_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ attach_stream_cmd.hdr.dest_port = mvm_handle;
+ attach_stream_cmd.hdr.token = 0;
+ attach_stream_cmd.hdr.opcode =
+ VSS_IMVM_CMD_ATTACH_STREAM;
+ attach_stream_cmd.attach_stream.handle = cvs_handle;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm,
+ (uint32_t *) &attach_stream_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending ATTACH_STREAM\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ }
+ }
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int voice_unmap_cal_block(struct voice_data *v, int cal_index)
+{
+ int result = 0;
+ struct cal_block_data *cal_block;
+
+ if (common.cal_data[cal_index] == NULL) {
+ pr_err("%s: Cal type is NULL, index %d!\n",
+ __func__, cal_index);
+
+ goto done;
+ }
+
+ mutex_lock(&common.cal_data[cal_index]->lock);
+ cal_block = cal_utils_get_only_cal_block(
+ common.cal_data[cal_index]);
+ if (cal_block == NULL) {
+ pr_err("%s: Cal block is NULL, index %d!\n",
+ __func__, cal_index);
+
+ result = -EINVAL;
+ goto unlock;
+ }
+
+ if (cal_block->map_data.q6map_handle == 0) {
+ pr_debug("%s: Q6 handle is not set!\n", __func__);
+
+ result = -EINVAL;
+ goto unlock;
+ }
+
+ mutex_lock(&common.common_lock);
+ result = voice_send_mvm_unmap_memory_physical_cmd(
+ v, cal_block->map_data.q6map_handle);
+ if (result)
+ pr_err("%s: Voice_send_mvm_unmap_memory_physical_cmd failed for session 0x%x, err %d!\n",
+ __func__, v->session_id, result);
+
+ cal_block->map_data.q6map_handle = 0;
+ mutex_unlock(&common.common_lock);
+unlock:
+ mutex_unlock(&common.cal_data[cal_index]->lock);
+done:
+ return result;
+}
+
+static int voice_destroy_mvm_cvs_session(struct voice_data *v)
+{
+ int ret = 0;
+ struct mvm_detach_stream_cmd detach_stream;
+ struct apr_hdr mvm_destroy;
+ struct apr_hdr cvs_destroy;
+ void *apr_mvm, *apr_cvs;
+ u16 mvm_handle, cvs_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_mvm || !apr_cvs) {
+ pr_err("%s: apr_mvm or apr_cvs is NULL\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+ cvs_handle = voice_get_cvs_handle(v);
+
+ /* MVM, CVS sessions are destroyed only for Full control sessions. */
+ if (is_voip_session(v->session_id)) {
+ pr_debug("%s: MVM detach stream, VOC_STATE: %d\n", __func__,
+ v->voc_state);
+
+ /* Detach voice stream. */
+ detach_stream.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ detach_stream.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(detach_stream) - APR_HDR_SIZE);
+ detach_stream.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ detach_stream.hdr.dest_port = mvm_handle;
+ detach_stream.hdr.token = 0;
+ detach_stream.hdr.opcode = VSS_IMVM_CMD_DETACH_STREAM;
+ detach_stream.detach_stream.handle = cvs_handle;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &detach_stream);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending DETACH_STREAM\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait event timeout\n", __func__);
+
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ /* Unmap memory */
+ if (v->shmem_info.mem_handle != 0) {
+ ret = voice_send_mvm_unmap_memory_physical_cmd(v,
+ v->shmem_info.mem_handle);
+ if (ret < 0) {
+ pr_err("%s Memory_unmap for voip failed %d\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ v->shmem_info.mem_handle = 0;
+ }
+ }
+
+ /* Unmap Source Tracking shared memory if mapped earlier */
+ voice_unmap_and_free_source_tracking_shared_memory(v);
+
+ if (is_voip_session(v->session_id) ||
+ is_qchat_session(v->session_id) ||
+ is_volte_session(v->session_id) ||
+ is_vowlan_session(v->session_id) ||
+ v->voc_state == VOC_ERROR || common.is_destroy_cvd) {
+ /* Destroy CVS. */
+ pr_debug("%s: CVS destroy session\n", __func__);
+
+ cvs_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_destroy) - APR_HDR_SIZE);
+ cvs_destroy.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_destroy.dest_port = cvs_handle;
+ cvs_destroy.token = 0;
+ cvs_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_destroy);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending CVS DESTROY\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait event timeout\n", __func__);
+
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ cvs_handle = 0;
+ voice_set_cvs_handle(v, cvs_handle);
+
+ /* Unmap physical memory for all calibration buffers */
+ if (!is_other_session_active(v->session_id)) {
+ if (voice_unmap_cal_block(v, CVP_VOCPROC_CAL))
+ pr_err("%s: Unmap VOCPROC cal failed\n",
+ __func__);
+ if (voice_unmap_cal_block(v, CVP_VOCVOL_CAL))
+ pr_err("%s: Unmap VOCVOL cal failed\n",
+ __func__);
+ if (voice_unmap_cal_block(v, CVP_VOCDEV_CFG_CAL))
+ pr_err("%s: Unmap VOCDEV_CFG cal failed\n",
+ __func__);
+ if (voice_unmap_cal_block(v, CVS_VOCSTRM_CAL))
+ pr_err("%s: Unmap VOCSTRM cal failed\n",
+ __func__);
+ }
+
+ /* Destroy MVM. */
+ pr_debug("%s: MVM destroy session\n", __func__);
+
+ mvm_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_destroy) - APR_HDR_SIZE);
+ mvm_destroy.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_destroy.dest_port = mvm_handle;
+ mvm_destroy.token = 0;
+ mvm_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_destroy);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending MVM DESTROY\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ mvm_handle = 0;
+ voice_set_mvm_handle(v, mvm_handle);
+ }
+ return 0;
+fail:
+ return ret;
+}
+
+static int voice_send_tty_mode_cmd(struct voice_data *v)
+{
+ int ret = 0;
+ struct mvm_set_tty_mode_cmd mvm_tty_mode_cmd;
+ void *apr_mvm;
+ u16 mvm_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+
+ /* send tty mode cmd to mvm */
+ mvm_tty_mode_cmd.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_tty_mode_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_tty_mode_cmd) -
+ APR_HDR_SIZE);
+ pr_debug("%s: pkt size = %d\n",
+ __func__, mvm_tty_mode_cmd.hdr.pkt_size);
+ mvm_tty_mode_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_tty_mode_cmd.hdr.dest_port = mvm_handle;
+ mvm_tty_mode_cmd.hdr.token = 0;
+ mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE;
+ mvm_tty_mode_cmd.tty_mode.mode = v->tty_mode;
+ pr_debug("tty mode =%d\n", mvm_tty_mode_cmd.tty_mode.mode);
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_tty_mode_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_TTY_MODE\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ return 0;
+fail:
+ return ret;
+}
+
+static int voice_send_set_pp_enable_cmd(
+ struct voice_data *v, struct module_instance_info mod_inst_info,
+ int enable)
+{
+ struct enable_param enable_param = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ param_hdr.module_id = mod_inst_info.module_id;
+ param_hdr.instance_id = mod_inst_info.instance_id;
+ param_hdr.param_id = VOICE_PARAM_MOD_ENABLE;
+ param_hdr.param_size = sizeof(enable_param);
+ enable_param.enable = enable ? 1 : 0;
+
+ pr_debug("%s: voice_send_set_pp_enable_cmd, module_id=%d, instance_id=%d, enable=%d\n",
+ __func__, mod_inst_info.module_id, mod_inst_info.instance_id,
+ enable);
+
+ ret = voice_pack_and_set_cvs_ui_property(v, param_hdr,
+ (uint8_t *) &enable_param);
+ if (ret < 0)
+ pr_err("Fail: sending cvs set pp enable\n");
+
+ return ret;
+}
+
+static int voice_send_hd_cmd(struct voice_data *v, int enable)
+{
+ struct mvm_set_hd_enable_cmd mvm_set_hd_cmd;
+ int ret = 0;
+ void *apr_mvm;
+ u16 mvm_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ apr_mvm = common.apr_q6_mvm;
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mvm_handle = voice_get_mvm_handle(v);
+ if (!mvm_handle) {
+ pr_err("%s: mvm_handle is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mvm_set_hd_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_set_hd_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_set_hd_cmd) -
+ APR_HDR_SIZE);
+ mvm_set_hd_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id);
+ mvm_set_hd_cmd.hdr.dest_port = mvm_handle;
+ mvm_set_hd_cmd.hdr.token = 0;
+
+ if (enable)
+ mvm_set_hd_cmd.hdr.opcode = VSS_IHDVOICE_CMD_ENABLE;
+ else
+ mvm_set_hd_cmd.hdr.opcode = VSS_IHDVOICE_CMD_DISABLE;
+
+ pr_debug("%s: enable=%d\n", __func__, enable);
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_hd_cmd);
+ if (ret < 0) {
+ pr_err("%s: Failed to sending mvm set HD Voice enable %d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int voice_set_dtx(struct voice_data *v)
+{
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+ struct cvs_set_enc_dtx_mode_cmd cvs_set_dtx;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ /* Set DTX */
+ cvs_set_dtx.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_dtx.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_dtx) - APR_HDR_SIZE);
+ cvs_set_dtx.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_set_dtx.hdr.dest_port = cvs_handle;
+ cvs_set_dtx.hdr.token = 0;
+ cvs_set_dtx.hdr.opcode = VSS_ISTREAM_CMD_SET_ENC_DTX_MODE;
+ cvs_set_dtx.dtx_mode.enable = common.mvs_info.dtx_mode;
+
+ pr_debug("%s: Setting DTX %d\n", __func__, common.mvs_info.dtx_mode);
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_dtx);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_DTX\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ return -EINVAL;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int voice_send_mvm_media_type_cmd(struct voice_data *v)
+{
+ struct vss_imvm_cmd_set_cal_media_type_t mvm_set_cal_media_type;
+ int ret = 0;
+ void *apr_mvm;
+ u16 mvm_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+
+ mvm_set_cal_media_type.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_set_cal_media_type.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_set_cal_media_type) -
+ APR_HDR_SIZE);
+ mvm_set_cal_media_type.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_set_cal_media_type.hdr.dest_port = mvm_handle;
+ mvm_set_cal_media_type.hdr.token = 0;
+ mvm_set_cal_media_type.hdr.opcode = VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE;
+ mvm_set_cal_media_type.media_id = common.mvs_info.media_type;
+ pr_debug("%s: setting media_id as %x\n",
+ __func__ , mvm_set_cal_media_type.media_id);
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_cal_media_type);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending media type\n", __func__, ret);
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout %d\n", __func__, ret);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ return 0;
+fail:
+ return ret;
+}
+
+static int voice_send_dtmf_rx_detection_cmd(struct voice_data *v,
+ uint32_t enable)
+{
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+ struct cvs_set_rx_dtmf_detection_cmd cvs_dtmf_rx_detection;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ /* Set SET_DTMF_RX_DETECTION */
+ cvs_dtmf_rx_detection.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_dtmf_rx_detection.hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_dtmf_rx_detection) - APR_HDR_SIZE);
+ cvs_dtmf_rx_detection.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_dtmf_rx_detection.hdr.dest_port = cvs_handle;
+ cvs_dtmf_rx_detection.hdr.token = 0;
+ cvs_dtmf_rx_detection.hdr.opcode =
+ VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION;
+ cvs_dtmf_rx_detection.cvs_dtmf_det.enable = enable;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_dtmf_rx_detection);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_DTMF_RX_DETECTION\n",
+ __func__,
+ ret);
+ return -EINVAL;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ return -EINVAL;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void voice_vote_powerstate_to_bms(struct voice_data *v, bool state)
+{
+ union power_supply_propval pval = {0, };
+
+ if (!v->psy)
+ v->psy = power_supply_get_by_name("bms");
+ if (v->psy && !(is_voip_session(v->session_id) ||
+ is_vowlan_session(v->session_id))) {
+ pval.intval = VMBMS_VOICE_CALL_BIT;
+ if (state) {
+ power_supply_set_property(v->psy,
+ POWER_SUPPLY_PROP_HI_POWER,
+ &pval);
+ pr_debug("%s : Vote High power to BMS\n",
+ __func__);
+ } else {
+ power_supply_set_property(v->psy,
+ POWER_SUPPLY_PROP_LOW_POWER,
+ &pval);
+ pr_debug("%s: Vote low power to BMS\n",
+ __func__);
+ }
+ } else {
+ pr_debug("%s: No OP", __func__);
+ }
+
+}
+
+void voc_disable_dtmf_det_on_active_sessions(void)
+{
+ struct voice_data *v = NULL;
+ int i;
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ v = &common.voice[i];
+ if ((v->dtmf_rx_detect_en) &&
+ is_voc_state_active(v->voc_state)) {
+
+ pr_debug("disable dtmf det on ses_id=%d\n",
+ v->session_id);
+ voice_send_dtmf_rx_detection_cmd(v, 0);
+ }
+ }
+}
+
+int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+ v->dtmf_rx_detect_en = enable;
+
+ if (is_voc_state_active(v->voc_state))
+ ret = voice_send_dtmf_rx_detection_cmd(v,
+ v->dtmf_rx_detect_en);
+
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+void voc_set_destroy_cvd_flag(bool is_destroy_cvd)
+{
+ pr_debug("%s: %d\n", __func__, is_destroy_cvd);
+ common.is_destroy_cvd = is_destroy_cvd;
+}
+
+void voc_set_vote_bms_flag(bool is_vote_bms)
+{
+ pr_debug("%s: flag value: %d\n", __func__, is_vote_bms);
+ common.is_vote_bms = is_vote_bms;
+}
+
+int voc_alloc_cal_shared_memory(void)
+{
+ int rc = 0;
+
+ mutex_lock(&common.common_lock);
+ if (is_cal_memory_allocated()) {
+ pr_debug("%s: Calibration shared buffer already allocated",
+ __func__);
+ } else {
+ /* Allocate memory for calibration memory map table. */
+ rc = voice_alloc_cal_mem_map_table();
+ if ((rc < 0) && (rc != -EPROBE_DEFER)) {
+ pr_err("%s: Failed to allocate cal memory, err=%d",
+ __func__, rc);
+ }
+ }
+ mutex_unlock(&common.common_lock);
+
+ return rc;
+}
+
+int voc_alloc_voip_shared_memory(void)
+{
+ int rc = 0;
+
+ /* Allocate shared memory for OOB Voip */
+ rc = voice_alloc_oob_shared_mem();
+ if (rc < 0) {
+ pr_err("%s: Failed to alloc shared memory for OOB rc:%d\n",
+ __func__, rc);
+ } else {
+ /* Allocate mem map table for OOB */
+ rc = voice_alloc_oob_mem_table();
+ if (rc < 0) {
+ pr_err("%s: Failed to alloc mem map talbe rc:%d\n",
+ __func__, rc);
+
+ voice_free_oob_shared_mem();
+ }
+ }
+
+ return rc;
+}
+
+static int is_cal_memory_allocated(void)
+{
+ bool ret;
+
+ if (common.cal_mem_map_table.client != NULL &&
+ common.cal_mem_map_table.handle != NULL)
+ ret = true;
+ else
+ ret = false;
+
+ return ret;
+}
+
+
+static int free_cal_map_table(void)
+{
+ int ret = 0;
+
+ if ((common.cal_mem_map_table.client == NULL) ||
+ (common.cal_mem_map_table.handle == NULL))
+ goto done;
+
+ ret = msm_audio_ion_free(common.cal_mem_map_table.client,
+ common.cal_mem_map_table.handle);
+ if (ret < 0)
+ pr_err("%s: msm_audio_ion_free failed:\n", __func__);
+
+done:
+ common.cal_mem_map_table.client = NULL;
+ common.cal_mem_map_table.handle = NULL;
+ return ret;
+}
+
+static int is_rtac_memory_allocated(void)
+{
+ bool ret;
+
+ if (common.rtac_mem_map_table.client != NULL &&
+ common.rtac_mem_map_table.handle != NULL)
+ ret = true;
+ else
+ ret = false;
+
+ return ret;
+}
+
+static int free_rtac_map_table(void)
+{
+ int ret = 0;
+
+ if ((common.rtac_mem_map_table.client == NULL) ||
+ (common.rtac_mem_map_table.handle == NULL))
+ goto done;
+
+ ret = msm_audio_ion_free(common.rtac_mem_map_table.client,
+ common.rtac_mem_map_table.handle);
+ if (ret < 0)
+ pr_err("%s: msm_audio_ion_free failed:\n", __func__);
+
+done:
+ common.rtac_mem_map_table.client = NULL;
+ common.rtac_mem_map_table.handle = NULL;
+ return ret;
+}
+
+
+static int is_voip_memory_allocated(void)
+{
+ bool ret;
+ struct voice_data *v = voice_get_session(
+ common.voice[VOC_PATH_FULL].session_id);
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL, session_id:%d\n", __func__,
+ common.voice[VOC_PATH_FULL].session_id);
+
+ ret = false;
+ goto done;
+ }
+
+ mutex_lock(&common.common_lock);
+ if (v->shmem_info.sh_buf.client != NULL &&
+ v->shmem_info.sh_buf.handle != NULL)
+ ret = true;
+ else
+ ret = false;
+ mutex_unlock(&common.common_lock);
+
+done:
+ return ret;
+}
+
+static int voice_config_cvs_vocoder_amr_rate(struct voice_data *v)
+{
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+ struct cvs_set_amr_enc_rate_cmd cvs_set_amr_rate;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ pr_debug("%s: Setting AMR rate. Media Type: %d\n", __func__,
+ common.mvs_info.media_type);
+
+ cvs_set_amr_rate.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_amr_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_amr_rate) - APR_HDR_SIZE);
+ cvs_set_amr_rate.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_set_amr_rate.hdr.dest_port = cvs_handle;
+ cvs_set_amr_rate.hdr.token = 0;
+
+ if (common.mvs_info.media_type == VSS_MEDIA_ID_AMR_NB_MODEM)
+ cvs_set_amr_rate.hdr.opcode =
+ VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE;
+ else if (common.mvs_info.media_type == VSS_MEDIA_ID_AMR_WB_MODEM)
+ cvs_set_amr_rate.hdr.opcode =
+ VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE;
+
+ cvs_set_amr_rate.amr_rate.mode = common.mvs_info.rate;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amr_rate);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_AMR_RATE\n",
+ __func__, ret);
+
+ goto done;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+ return 0;
+done:
+ return ret;
+}
+
+static int voice_config_cvs_vocoder(struct voice_data *v)
+{
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+ /* Set media type. */
+ struct cvs_set_media_type_cmd cvs_set_media_cmd;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ cvs_set_media_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_media_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_media_cmd) -
+ APR_HDR_SIZE);
+ cvs_set_media_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_set_media_cmd.hdr.dest_port = cvs_handle;
+ cvs_set_media_cmd.hdr.token = 0;
+ cvs_set_media_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MEDIA_TYPE;
+ cvs_set_media_cmd.media_type.tx_media_id = common.mvs_info.media_type;
+ cvs_set_media_cmd.media_type.rx_media_id = common.mvs_info.media_type;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_media_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_MEDIA_TYPE\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ /* Set encoder properties. */
+ switch (common.mvs_info.media_type) {
+ case VSS_MEDIA_ID_EVRC_MODEM:
+ case VSS_MEDIA_ID_4GV_NB_MODEM:
+ case VSS_MEDIA_ID_4GV_WB_MODEM:
+ case VSS_MEDIA_ID_4GV_NW_MODEM: {
+ struct cvs_set_cdma_enc_minmax_rate_cmd cvs_set_cdma_rate;
+
+ pr_debug("Setting EVRC min-max rate\n");
+
+ cvs_set_cdma_rate.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_cdma_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_cdma_rate) - APR_HDR_SIZE);
+ cvs_set_cdma_rate.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_set_cdma_rate.hdr.dest_port = cvs_handle;
+ cvs_set_cdma_rate.hdr.token = 0;
+ cvs_set_cdma_rate.hdr.opcode =
+ VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE;
+ cvs_set_cdma_rate.cdma_rate.min_rate =
+ common.mvs_info.evrc_min_rate;
+ cvs_set_cdma_rate.cdma_rate.max_rate =
+ common.mvs_info.evrc_max_rate;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_cdma_rate);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_EVRC_MINMAX_RATE\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ if (common.mvs_info.media_type != VSS_MEDIA_ID_EVRC_MODEM) {
+ ret = voice_set_dtx(v);
+ if (ret < 0)
+ goto fail;
+ }
+
+ break;
+ }
+ case VSS_MEDIA_ID_AMR_NB_MODEM:
+ case VSS_MEDIA_ID_AMR_WB_MODEM: {
+ ret = voice_config_cvs_vocoder_amr_rate(v);
+ if (ret) {
+ pr_err("%s: Failed to update vocoder rate. %d\n",
+ __func__, ret);
+
+ goto fail;
+ }
+
+ ret = voice_set_dtx(v);
+ if (ret < 0)
+ goto fail;
+
+ break;
+ }
+ case VSS_MEDIA_ID_G729:
+ case VSS_MEDIA_ID_G711_ALAW:
+ case VSS_MEDIA_ID_G711_MULAW: {
+ ret = voice_set_dtx(v);
+
+ break;
+ }
+ default:
+ /* Do nothing. */
+ break;
+ }
+ return 0;
+
+fail:
+ return ret;
+}
+
+int voc_update_amr_vocoder_rate(uint32_t session_id)
+{
+ int ret = 0;
+ struct voice_data *v;
+
+ pr_debug("%s: session_id:%d", __func__, session_id);
+
+ v = voice_get_session(session_id);
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL, session_id:%d\n", __func__,
+ session_id);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&v->lock);
+ ret = voice_config_cvs_vocoder_amr_rate(v);
+ mutex_unlock(&v->lock);
+
+done:
+ return ret;
+}
+
+static int voice_send_start_voice_cmd(struct voice_data *v)
+{
+ struct apr_hdr mvm_start_voice_cmd;
+ int ret = 0;
+ void *apr_mvm;
+ u16 mvm_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+
+ mvm_start_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_start_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_start_voice_cmd) - APR_HDR_SIZE);
+ pr_debug("send mvm_start_voice_cmd pkt size = %d\n",
+ mvm_start_voice_cmd.pkt_size);
+ mvm_start_voice_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_start_voice_cmd.dest_port = mvm_handle;
+ mvm_start_voice_cmd.token = 0;
+ mvm_start_voice_cmd.opcode = VSS_IMVM_CMD_START_VOICE;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_start_voice_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IMVM_CMD_START_VOICE\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ } else {
+ if (common.is_vote_bms) {
+ /* vote high power to BMS during call start */
+ voice_vote_powerstate_to_bms(v, true);
+ }
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ return 0;
+fail:
+ return ret;
+}
+
+static void voc_get_tx_rx_topology(struct voice_data *v,
+ uint32_t *tx_topology_id,
+ uint32_t *rx_topology_id)
+{
+ uint32_t tx_id = 0;
+ uint32_t rx_id = 0;
+
+ if (v->lch_mode == VOICE_LCH_START || v->disable_topology) {
+ pr_debug("%s: Setting TX and RX topology to NONE for LCH\n",
+ __func__);
+
+ tx_id = VSS_IVOCPROC_TOPOLOGY_ID_NONE;
+ rx_id = VSS_IVOCPROC_TOPOLOGY_ID_NONE;
+ } else {
+ tx_id = voice_get_topology(CVP_VOC_TX_TOPOLOGY_CAL);
+ rx_id = voice_get_topology(CVP_VOC_RX_TOPOLOGY_CAL);
+ }
+
+ *tx_topology_id = tx_id;
+ *rx_topology_id = rx_id;
+}
+
+static int voice_send_set_device_cmd(struct voice_data *v)
+{
+ struct cvp_set_device_cmd cvp_setdev_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ u16 cvp_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvp = common.apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* set device and wait for response */
+ cvp_setdev_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_setdev_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_setdev_cmd) - APR_HDR_SIZE);
+ pr_debug(" send create cvp setdev, pkt size = %d\n",
+ cvp_setdev_cmd.hdr.pkt_size);
+ cvp_setdev_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_setdev_cmd.hdr.dest_port = cvp_handle;
+ cvp_setdev_cmd.hdr.token = 0;
+
+ if (voice_get_cvd_int_version(common.cvd_version) >=
+ CVD_INT_VERSION_2_2)
+ cvp_setdev_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_SET_DEVICE_V3;
+ else
+ cvp_setdev_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_SET_DEVICE_V2;
+
+ voc_get_tx_rx_topology(v,
+ &cvp_setdev_cmd.cvp_set_device_v2.tx_topology_id,
+ &cvp_setdev_cmd.cvp_set_device_v2.rx_topology_id);
+
+ cvp_setdev_cmd.cvp_set_device_v2.tx_port_id = v->dev_tx.port_id;
+ cvp_setdev_cmd.cvp_set_device_v2.rx_port_id = v->dev_rx.port_id;
+
+ if (common.ec_ref_ext) {
+ cvp_setdev_cmd.cvp_set_device_v2.vocproc_mode =
+ VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING;
+ cvp_setdev_cmd.cvp_set_device_v2.ec_ref_port_id =
+ common.ec_media_fmt_info.port_id;
+ } else {
+ cvp_setdev_cmd.cvp_set_device_v2.vocproc_mode =
+ VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING;
+ cvp_setdev_cmd.cvp_set_device_v2.ec_ref_port_id =
+ VSS_IVOCPROC_PORT_ID_NONE;
+ }
+ pr_debug("topology=%d , tx_port_id=%d, rx_port_id=%d\n",
+ cvp_setdev_cmd.cvp_set_device_v2.tx_topology_id,
+ cvp_setdev_cmd.cvp_set_device_v2.tx_port_id,
+ cvp_setdev_cmd.cvp_set_device_v2.rx_port_id);
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_setdev_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IVOCPROC_CMD_SET_DEVICE\n");
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return ret;
+}
+
+static int voice_send_stop_voice_cmd(struct voice_data *v)
+{
+ struct apr_hdr mvm_stop_voice_cmd;
+ int ret = 0;
+ void *apr_mvm;
+ u16 mvm_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+
+ mvm_stop_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_stop_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_stop_voice_cmd) - APR_HDR_SIZE);
+ pr_debug("send mvm_stop_voice_cmd pkt size = %d\n",
+ mvm_stop_voice_cmd.pkt_size);
+ mvm_stop_voice_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_stop_voice_cmd.dest_port = mvm_handle;
+ mvm_stop_voice_cmd.token = 0;
+ mvm_stop_voice_cmd.opcode = VSS_IMVM_CMD_STOP_VOICE;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_stop_voice_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IMVM_CMD_STOP_VOICE\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return ret;
+}
+static int voice_get_cal(struct cal_block_data **cal_block,
+ int cal_block_idx,
+ struct cal_block_data **col_data,
+ int col_data_idx, int session_id)
+{
+ int ret = 0;
+
+ *cal_block = cal_utils_get_only_cal_block(
+ common.cal_data[cal_block_idx]);
+ if (*cal_block == NULL) {
+ pr_err("%s: No cal data for cal %d!\n",
+ __func__, cal_block_idx);
+
+ ret = -ENODEV;
+ goto done;
+ }
+ ret = remap_cal_data(*cal_block, session_id);
+ if (ret < 0) {
+ pr_err("%s: Remap_cal_data failed for cal %d!\n",
+ __func__, cal_block_idx);
+
+ ret = -ENODEV;
+ goto done;
+ }
+
+ if (col_data == NULL)
+ goto done;
+
+ *col_data = cal_utils_get_only_cal_block(
+ common.cal_data[col_data_idx]);
+ if (*col_data == NULL) {
+ pr_err("%s: No cal data for cal %d!\n",
+ __func__, col_data_idx);
+
+ ret = -ENODEV;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int voice_send_cvs_register_cal_cmd(struct voice_data *v)
+{
+ struct cvs_register_cal_data_cmd cvs_reg_cal_cmd;
+ struct cal_block_data *cal_block = NULL;
+ struct cal_block_data *col_data = NULL;
+ int ret = 0;
+ memset(&cvs_reg_cal_cmd, 0, sizeof(cvs_reg_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.apr_q6_cvs) {
+ pr_err("%s: apr_cvs is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&common.cal_data[CVS_VOCSTRM_CAL]->lock);
+ mutex_lock(&common.cal_data[CVS_VOCSTRM_COL_CAL]->lock);
+
+ ret = voice_get_cal(&cal_block, CVS_VOCSTRM_CAL, &col_data,
+ CVS_VOCSTRM_COL_CAL, v->session_id);
+ if (ret < 0) {
+ pr_err("%s: Voice_get_cal failed for cal %d!\n",
+ __func__, CVS_VOCSTRM_CAL);
+
+ goto unlock;
+ }
+
+ memcpy(&cvs_reg_cal_cmd.cvs_cal_data.column_info[0],
+ (void *) &((struct audio_cal_info_voc_col *)
+ col_data->cal_info)->data,
+ col_data->cal_data.size);
+
+ cvs_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvs_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_reg_cal_cmd) - APR_HDR_SIZE);
+ cvs_reg_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_reg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v);
+ cvs_reg_cal_cmd.hdr.token = 0;
+ if (common.is_per_vocoder_cal_enabled)
+ cvs_reg_cal_cmd.hdr.opcode =
+ VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA;
+ else
+ cvs_reg_cal_cmd.hdr.opcode =
+ VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2;
+
+ cvs_reg_cal_cmd.cvs_cal_data.cal_mem_handle =
+ cal_block->map_data.q6map_handle;
+ cvs_reg_cal_cmd.cvs_cal_data.cal_mem_address_lsw =
+ lower_32_bits(cal_block->cal_data.paddr);
+ cvs_reg_cal_cmd.cvs_cal_data.cal_mem_address_msw =
+ msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+ cvs_reg_cal_cmd.cvs_cal_data.cal_mem_size =
+ cal_block->cal_data.size;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_reg_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d registering CVS cal\n", __func__, ret);
+
+ ret = -EINVAL;
+ goto unlock;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto unlock;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto unlock;
+ }
+unlock:
+ mutex_unlock(&common.cal_data[CVS_VOCSTRM_COL_CAL]->lock);
+ mutex_unlock(&common.cal_data[CVS_VOCSTRM_CAL]->lock);
+done:
+ return ret;
+}
+
+static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v)
+{
+ struct cvs_deregister_cal_data_cmd cvs_dereg_cal_cmd;
+ int ret = 0;
+ memset(&cvs_dereg_cal_cmd, 0, sizeof(cvs_dereg_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.apr_q6_cvs) {
+ pr_err("%s: apr_cvs is NULL\n", __func__);
+
+ ret = -EPERM;
+ goto done;
+ }
+
+ cvs_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvs_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_dereg_cal_cmd) - APR_HDR_SIZE);
+ cvs_dereg_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_dereg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v);
+ cvs_dereg_cal_cmd.hdr.token = 0;
+ if (common.is_per_vocoder_cal_enabled)
+ cvs_dereg_cal_cmd.hdr.opcode =
+ VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA;
+ else
+ cvs_dereg_cal_cmd.hdr.opcode =
+ VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_dereg_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d de-registering CVS cal\n", __func__, ret);
+ goto done;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+done:
+ return ret;
+
+}
+
+static int voice_send_cvp_create_cmd(struct voice_data *v)
+{
+ struct cvp_create_full_ctl_session_cmd cvp_session_cmd;
+ void *apr_cvp;
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ apr_cvp = common.apr_q6_cvp;
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* create cvp session and wait for response */
+ cvp_session_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_session_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_session_cmd) - APR_HDR_SIZE);
+ pr_debug("%s: send create cvp session, pkt size = %d\n",
+ __func__, cvp_session_cmd.hdr.pkt_size);
+ cvp_session_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_session_cmd.hdr.dest_port = 0;
+ cvp_session_cmd.hdr.token = 0;
+
+ if (voice_get_cvd_int_version(common.cvd_version) >=
+ CVD_INT_VERSION_2_2)
+ cvp_session_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3;
+ else
+ cvp_session_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2;
+
+ voc_get_tx_rx_topology(v,
+ &cvp_session_cmd.cvp_session.tx_topology_id,
+ &cvp_session_cmd.cvp_session.rx_topology_id);
+
+ cvp_session_cmd.cvp_session.direction = 2; /*tx and rx*/
+ cvp_session_cmd.cvp_session.tx_port_id = v->dev_tx.port_id;
+ cvp_session_cmd.cvp_session.rx_port_id = v->dev_rx.port_id;
+ cvp_session_cmd.cvp_session.profile_id =
+ VSS_ICOMMON_CAL_NETWORK_ID_NONE;
+ if (common.ec_ref_ext) {
+ cvp_session_cmd.cvp_session.vocproc_mode =
+ VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING;
+ cvp_session_cmd.cvp_session.ec_ref_port_id =
+ common.ec_media_fmt_info.port_id;
+ } else {
+ cvp_session_cmd.cvp_session.vocproc_mode =
+ VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING;
+ cvp_session_cmd.cvp_session.ec_ref_port_id =
+ VSS_IVOCPROC_PORT_ID_NONE;
+ }
+
+ pr_debug("tx_topology: %d tx_port_id=%d, rx_port_id=%d, mode: 0x%x\n",
+ cvp_session_cmd.cvp_session.tx_topology_id,
+ cvp_session_cmd.cvp_session.tx_port_id,
+ cvp_session_cmd.cvp_session.rx_port_id,
+ cvp_session_cmd.cvp_session.vocproc_mode);
+ pr_debug("rx_topology: %d, profile_id: 0x%x, pkt_size: %d\n",
+ cvp_session_cmd.cvp_session.rx_topology_id,
+ cvp_session_cmd.cvp_session.profile_id,
+ cvp_session_cmd.hdr.pkt_size);
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_session_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VOCPROC_FULL_CONTROL_SESSION\n");
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v)
+{
+ struct cvp_register_dev_cfg_cmd cvp_reg_dev_cfg_cmd;
+ struct cal_block_data *cal_block = NULL;
+ int ret = 0;
+ memset(&cvp_reg_dev_cfg_cmd, 0, sizeof(cvp_reg_dev_cfg_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL\n", __func__);
+
+ ret = -EPERM;
+ goto done;
+ }
+
+ mutex_lock(&common.cal_data[CVP_VOCDEV_CFG_CAL]->lock);
+
+ ret = voice_get_cal(&cal_block, CVP_VOCDEV_CFG_CAL, NULL,
+ 0, v->session_id);
+ if (ret < 0) {
+ pr_err("%s: Voice_get_cal failed for cal %d!\n",
+ __func__, CVP_VOCDEV_CFG_CAL);
+
+ goto unlock;
+ }
+
+ cvp_reg_dev_cfg_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_reg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_reg_dev_cfg_cmd) - APR_HDR_SIZE);
+ cvp_reg_dev_cfg_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_reg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_reg_dev_cfg_cmd.hdr.token = 0;
+ cvp_reg_dev_cfg_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG;
+
+ cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_handle =
+ cal_block->map_data.q6map_handle;
+ cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_address_lsw =
+ lower_32_bits(cal_block->cal_data.paddr);
+ cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_address_msw =
+ msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+ cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_size =
+ cal_block->cal_data.size;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(common.apr_q6_cvp,
+ (uint32_t *) &cvp_reg_dev_cfg_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d registering CVP dev cfg cal\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto unlock;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto unlock;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto unlock;
+ }
+unlock:
+ mutex_unlock(&common.cal_data[CVP_VOCDEV_CFG_CAL]->lock);
+done:
+ return ret;
+}
+
+static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v)
+{
+ struct cvp_deregister_dev_cfg_cmd cvp_dereg_dev_cfg_cmd;
+ int ret = 0;
+ memset(&cvp_dereg_dev_cfg_cmd, 0, sizeof(cvp_dereg_dev_cfg_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL\n", __func__);
+
+ ret = -EPERM;
+ goto done;
+ }
+
+ cvp_dereg_dev_cfg_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_dereg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_dereg_dev_cfg_cmd) - APR_HDR_SIZE);
+ cvp_dereg_dev_cfg_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_dereg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_dereg_dev_cfg_cmd.hdr.token = 0;
+ cvp_dereg_dev_cfg_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(common.apr_q6_cvp,
+ (uint32_t *) &cvp_dereg_dev_cfg_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d de-registering CVP dev cfg cal\n",
+ __func__, ret);
+ goto done;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int voice_send_cvp_register_cal_cmd(struct voice_data *v)
+{
+ struct cvp_register_cal_data_cmd cvp_reg_cal_cmd;
+ struct cal_block_data *cal_block = NULL;
+ struct cal_block_data *col_data = NULL;
+ int ret = 0;
+ memset(&cvp_reg_cal_cmd, 0, sizeof(cvp_reg_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&common.cal_data[CVP_VOCPROC_CAL]->lock);
+ mutex_lock(&common.cal_data[CVP_VOCPROC_COL_CAL]->lock);
+
+ ret = voice_get_cal(&cal_block, CVP_VOCPROC_CAL, &col_data,
+ CVP_VOCPROC_COL_CAL, v->session_id);
+ if (ret < 0) {
+ pr_err("%s: Voice_get_cal failed for cal %d!\n",
+ __func__, CVP_VOCPROC_CAL);
+
+ goto unlock;
+ }
+
+ v->dev_tx.dev_id = ((struct audio_cal_info_vocproc *)
+ cal_block->cal_info)->tx_acdb_id;
+ v->dev_rx.dev_id = ((struct audio_cal_info_vocproc *)
+ cal_block->cal_info)->rx_acdb_id;
+ pr_debug("%s: %s: Tx acdb id = %d and Rx acdb id = %d", __func__,
+ voc_get_session_name(v->session_id), v->dev_tx.dev_id,
+ v->dev_rx.dev_id);
+
+ memcpy(&cvp_reg_cal_cmd.cvp_cal_data.column_info[0],
+ (void *) &((struct audio_cal_info_voc_col *)
+ col_data->cal_info)->data,
+ col_data->cal_data.size);
+
+ cvp_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_reg_cal_cmd) - APR_HDR_SIZE);
+ cvp_reg_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_reg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_reg_cal_cmd.hdr.token = 0;
+ if (common.is_per_vocoder_cal_enabled)
+ cvp_reg_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA;
+ else
+ cvp_reg_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2;
+
+ cvp_reg_cal_cmd.cvp_cal_data.cal_mem_handle =
+ cal_block->map_data.q6map_handle;
+ cvp_reg_cal_cmd.cvp_cal_data.cal_mem_address_lsw =
+ lower_32_bits(cal_block->cal_data.paddr);
+ cvp_reg_cal_cmd.cvp_cal_data.cal_mem_address_msw =
+ msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+ cvp_reg_cal_cmd.cvp_cal_data.cal_mem_size =
+ cal_block->cal_data.size;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_reg_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d registering CVP cal\n", __func__, ret);
+
+ ret = -EINVAL;
+ goto unlock;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto unlock;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto unlock;
+ }
+unlock:
+ mutex_unlock(&common.cal_data[CVP_VOCPROC_COL_CAL]->lock);
+ mutex_unlock(&common.cal_data[CVP_VOCPROC_CAL]->lock);
+done:
+ return ret;
+}
+
+static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v)
+{
+ struct cvp_deregister_cal_data_cmd cvp_dereg_cal_cmd;
+ int ret = 0;
+ memset(&cvp_dereg_cal_cmd, 0, sizeof(cvp_dereg_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+ ret = -EPERM;
+ goto done;
+ }
+
+ cvp_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_dereg_cal_cmd) - APR_HDR_SIZE);
+ cvp_dereg_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_dereg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_dereg_cal_cmd.hdr.token = 0;
+ if (common.is_per_vocoder_cal_enabled)
+ cvp_dereg_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA;
+ else
+ cvp_dereg_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_dereg_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d de-registering CVP cal\n", __func__, ret);
+ goto done;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v)
+{
+ struct cvp_register_vol_cal_data_cmd cvp_reg_vol_cal_cmd;
+ struct cal_block_data *cal_block = NULL;
+ struct cal_block_data *col_data = NULL;
+ int ret = 0;
+ memset(&cvp_reg_vol_cal_cmd, 0, sizeof(cvp_reg_vol_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&common.cal_data[CVP_VOCVOL_CAL]->lock);
+ mutex_lock(&common.cal_data[CVP_VOCVOL_COL_CAL]->lock);
+
+ ret = voice_get_cal(&cal_block, CVP_VOCVOL_CAL, &col_data,
+ CVP_VOCVOL_COL_CAL, v->session_id);
+ if (ret < 0) {
+ pr_err("%s: Voice_get_cal failed for cal %d!\n",
+ __func__, CVP_VOCVOL_CAL);
+
+ goto unlock;
+ }
+
+ memcpy(&cvp_reg_vol_cal_cmd.cvp_vol_cal_data.column_info[0],
+ (void *) &((struct audio_cal_info_voc_col *)
+ col_data->cal_info)->data,
+ col_data->cal_data.size);
+
+ cvp_reg_vol_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_reg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_reg_vol_cal_cmd) - APR_HDR_SIZE);
+ cvp_reg_vol_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_reg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_reg_vol_cal_cmd.hdr.token = 0;
+ if (common.is_per_vocoder_cal_enabled)
+ cvp_reg_vol_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA;
+ else
+ cvp_reg_vol_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA;
+
+ cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_handle =
+ cal_block->map_data.q6map_handle;
+ cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_address_lsw =
+ lower_32_bits(cal_block->cal_data.paddr);
+ cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_address_msw =
+ msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+ cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_size =
+ cal_block->cal_data.size;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(common.apr_q6_cvp,
+ (uint32_t *) &cvp_reg_vol_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d registering CVP vol cal\n", __func__, ret);
+
+ ret = -EINVAL;
+ goto unlock;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto unlock;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto unlock;
+ }
+unlock:
+ mutex_unlock(&common.cal_data[CVP_VOCVOL_COL_CAL]->lock);
+ mutex_unlock(&common.cal_data[CVP_VOCVOL_CAL]->lock);
+done:
+ return ret;
+}
+
+static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v)
+{
+ struct cvp_deregister_vol_cal_data_cmd cvp_dereg_vol_cal_cmd;
+ int ret = 0;
+ memset(&cvp_dereg_vol_cal_cmd, 0, sizeof(cvp_dereg_vol_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL\n", __func__);
+
+ ret = -EPERM;
+ goto done;
+ }
+
+ cvp_dereg_vol_cal_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_dereg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_dereg_vol_cal_cmd) - APR_HDR_SIZE);
+ cvp_dereg_vol_cal_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_dereg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_dereg_vol_cal_cmd.hdr.token = 0;
+ if (common.is_per_vocoder_cal_enabled)
+ cvp_dereg_vol_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA;
+ else
+ cvp_dereg_vol_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(common.apr_q6_cvp,
+ (uint32_t *) &cvp_dereg_vol_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d de-registering CVP vol cal\n",
+ __func__, ret);
+ goto done;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int voice_map_memory_physical_cmd(struct voice_data *v,
+ struct mem_map_table *table_info,
+ dma_addr_t phys,
+ uint32_t size,
+ uint32_t token)
+{
+ struct vss_imemory_cmd_map_physical_t mvm_map_phys_cmd;
+ uint32_t *memtable;
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!common.apr_q6_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!table_info->data) {
+ pr_err("%s: memory table is NULL.\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ memtable = (uint32_t *) table_info->data;
+
+ /*
+ * Store next table descriptor's address(64 bit) as NULL as there
+ * is only one memory block
+ */
+ memtable[0] = 0;
+ memtable[1] = 0;
+
+ /* Store next table descriptor's size */
+ memtable[2] = 0;
+
+ /* Store shared mem adddress (64 bit) */
+ memtable[3] = lower_32_bits(phys);
+ memtable[4] = msm_audio_populate_upper_32_bits(phys);
+
+ /* Store shared memory size */
+ memtable[5] = size;
+
+ mvm_map_phys_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mvm_map_phys_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_map_phys_cmd) - APR_HDR_SIZE);
+ mvm_map_phys_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_map_phys_cmd.hdr.dest_port = voice_get_mvm_handle(v);
+ mvm_map_phys_cmd.hdr.token = token;
+ mvm_map_phys_cmd.hdr.opcode = VSS_IMEMORY_CMD_MAP_PHYSICAL;
+
+ mvm_map_phys_cmd.table_descriptor.mem_address_lsw =
+ lower_32_bits(table_info->phys);
+ mvm_map_phys_cmd.table_descriptor.mem_address_msw =
+ msm_audio_populate_upper_32_bits(table_info->phys);
+ mvm_map_phys_cmd.table_descriptor.mem_size =
+ sizeof(struct vss_imemory_block_t) +
+ sizeof(struct vss_imemory_table_descriptor_t);
+ mvm_map_phys_cmd.is_cached = true;
+ mvm_map_phys_cmd.cache_line_size = 128;
+ mvm_map_phys_cmd.access_mask = 3;
+ mvm_map_phys_cmd.page_align = 4096;
+ mvm_map_phys_cmd.min_data_width = 8;
+ mvm_map_phys_cmd.max_data_width = 64;
+
+ pr_debug("%s: next table desc: add: %lld, size: %d\n",
+ __func__, *((uint64_t *) memtable),
+ *(((uint32_t *) memtable) + 2));
+ pr_debug("%s: phy add of of mem being mapped LSW:0x%x, MSW:0x%x size: %d\n",
+ __func__, *(((uint32_t *) memtable) + 3),
+ *(((uint32_t *) memtable) + 4), *(((uint32_t *) memtable) + 5));
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(common.apr_q6_mvm, (uint32_t *) &mvm_map_phys_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending mvm map phy cmd\n", __func__, ret);
+
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int voice_pause_voice_call(struct voice_data *v)
+{
+ struct apr_hdr mvm_pause_voice_cmd;
+ void *apr_mvm;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ if (v == NULL) {
+ pr_err("%s: Voice data is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ apr_mvm = common.apr_q6_mvm;
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mvm_pause_voice_cmd.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mvm_pause_voice_cmd.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_pause_voice_cmd) - APR_HDR_SIZE);
+ mvm_pause_voice_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_pause_voice_cmd.dest_port = voice_get_mvm_handle(v);
+ mvm_pause_voice_cmd.token = 0;
+ mvm_pause_voice_cmd.opcode = VSS_IMVM_CMD_PAUSE_VOICE;
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ pr_debug("%s: send mvm_pause_voice_cmd pkt size = %d\n",
+ __func__, mvm_pause_voice_cmd.pkt_size);
+
+ ret = apr_send_pkt(apr_mvm,
+ (uint32_t *)&mvm_pause_voice_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IMVM_CMD_PAUSE_VOICE\n");
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int voice_map_cal_memory(struct cal_block_data *cal_block,
+ uint32_t session_id)
+{
+ int result = 0;
+ int voc_index;
+ struct voice_data *v = NULL;
+ pr_debug("%s\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("%s: Cal block is NULL!\n", __func__);
+
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block->cal_data.paddr == 0) {
+ pr_debug("%s: No address to map!\n", __func__);
+
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block->map_data.map_size == 0) {
+ pr_debug("%s: Map size is 0!\n", __func__);
+
+ result = -EINVAL;
+ goto done;
+ }
+
+ voc_index = voice_get_idx_for_session(session_id);
+ if (voc_index < 0) {
+ pr_err("%s: Invalid session ID %d\n", __func__, session_id);
+
+ goto done;
+ }
+
+ mutex_lock(&common.common_lock);
+ v = &common.voice[voc_index];
+
+ result = voice_map_memory_physical_cmd(v,
+ &common.cal_mem_map_table,
+ (dma_addr_t)cal_block->cal_data.paddr,
+ cal_block->map_data.map_size,
+ VOC_CAL_MEM_MAP_TOKEN);
+ if (result < 0) {
+ pr_err("%s: Mmap did not work! addr = 0x%pK, size = %zd\n",
+ __func__,
+ &cal_block->cal_data.paddr,
+ cal_block->map_data.map_size);
+
+ goto done_unlock;
+ }
+
+ cal_block->map_data.q6map_handle = common.cal_mem_handle;
+done_unlock:
+ mutex_unlock(&common.common_lock);
+done:
+ return result;
+}
+
+static int remap_cal_data(struct cal_block_data *cal_block,
+ uint32_t session_id)
+{
+ int ret = 0;
+ pr_debug("%s\n", __func__);
+
+ if (cal_block->map_data.ion_client == NULL) {
+ pr_err("%s: No ION allocation for session_id %d!\n",
+ __func__, session_id);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((cal_block->map_data.map_size > 0) &&
+ (cal_block->map_data.q6map_handle == 0)) {
+
+ /* cal type not used */
+ ret = voice_map_cal_memory(cal_block, session_id);
+ if (ret < 0) {
+ pr_err("%s: Mmap did not work! size = %zd\n",
+ __func__, cal_block->map_data.map_size);
+
+ goto done;
+ }
+ } else {
+ pr_debug("%s: Cal block 0x%pK, size %zd already mapped. Q6 map handle = %d\n",
+ __func__, &cal_block->cal_data.paddr,
+ cal_block->map_data.map_size,
+ cal_block->map_data.q6map_handle);
+ }
+done:
+ return ret;
+}
+
+static int voice_unmap_cal_memory(int32_t cal_type,
+ struct cal_block_data *cal_block)
+{
+ int result = 0;
+ int result2 = 0;
+ int i;
+ struct voice_data *v = NULL;
+ pr_debug("%s\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("%s: Cal block is NULL!\n", __func__);
+
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block->map_data.q6map_handle == 0) {
+ pr_debug("%s: Q6 handle is not set!\n", __func__);
+
+ result = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&common.common_lock);
+
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ v = &common.voice[i];
+
+ mutex_lock(&v->lock);
+ if (is_voc_state_active(v->voc_state)) {
+ result2 = voice_pause_voice_call(v);
+ if (result2 < 0) {
+ pr_err("%s: Voice_pause_voice_call failed for session 0x%x, err %d!\n",
+ __func__, v->session_id, result2);
+
+ result = result2;
+ }
+
+ if (cal_type == CVP_VOCPROC_DYNAMIC_CAL_TYPE)
+ voice_send_cvp_deregister_vol_cal_cmd(v);
+ else if (cal_type == CVP_VOCPROC_STATIC_CAL_TYPE)
+ voice_send_cvp_deregister_cal_cmd(v);
+ else if (cal_type == CVP_VOCDEV_CFG_CAL_TYPE)
+ voice_send_cvp_deregister_dev_cfg_cmd(v);
+ else if (cal_type == CVS_VOCSTRM_STATIC_CAL_TYPE)
+ voice_send_cvs_deregister_cal_cmd(v);
+ else
+ pr_err("%s: Invalid cal type %d!\n",
+ __func__, cal_type);
+
+ result2 = voice_send_start_voice_cmd(v);
+ if (result2) {
+ pr_err("%s: Voice_send_start_voice_cmd failed for session 0x%x, err %d!\n",
+ __func__, v->session_id, result2);
+
+ result = result2;
+ }
+ }
+
+ if ((cal_block->map_data.q6map_handle != 0) &&
+ (!is_other_session_active(v->session_id))) {
+
+ result2 = voice_send_mvm_unmap_memory_physical_cmd(
+ v, cal_block->map_data.q6map_handle);
+ if (result2) {
+ pr_err("%s: Voice_send_mvm_unmap_memory_physical_cmd failed for session 0x%x, err %d!\n",
+ __func__, v->session_id, result2);
+
+ result = result2;
+ }
+ cal_block->map_data.q6map_handle = 0;
+ }
+ mutex_unlock(&v->lock);
+ }
+ mutex_unlock(&common.common_lock);
+done:
+ return result;
+}
+
+int voc_register_vocproc_vol_table(void)
+{
+ int result = 0;
+ int result2 = 0;
+ int i;
+ struct voice_data *v = NULL;
+
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&common.common_lock);
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ v = &common.voice[i];
+
+ mutex_lock(&v->lock);
+ if (is_voc_state_active(v->voc_state)) {
+ result2 = voice_send_cvp_register_vol_cal_cmd(v);
+ if (result2 < 0) {
+ pr_err("%s: Failed to register vocvol table for session 0x%x!\n",
+ __func__, v->session_id);
+
+ result = result2;
+ /* Still try to register other sessions */
+ }
+ }
+ mutex_unlock(&v->lock);
+ }
+
+ mutex_unlock(&common.common_lock);
+ return result;
+}
+
+int voc_deregister_vocproc_vol_table(void)
+{
+ int result = 0;
+ int success = 0;
+ int i;
+ struct voice_data *v = NULL;
+
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&common.common_lock);
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ v = &common.voice[i];
+
+ mutex_lock(&v->lock);
+ if (is_voc_state_active(v->voc_state)) {
+ result = voice_send_cvp_deregister_vol_cal_cmd(v);
+ if (result < 0) {
+ pr_err("%s: Failed to deregister vocvol table for session 0x%x!\n",
+ __func__, v->session_id);
+
+ mutex_unlock(&v->lock);
+ mutex_unlock(&common.common_lock);
+ if (success) {
+ pr_err("%s: Try to re-register all deregistered sessions!\n",
+ __func__);
+
+ voc_register_vocproc_vol_table();
+ }
+ goto done;
+ } else {
+ success = 1;
+ }
+ }
+ mutex_unlock(&v->lock);
+ }
+ mutex_unlock(&common.common_lock);
+done:
+ return result;
+}
+
+int voc_map_rtac_block(struct rtac_cal_block_data *cal_block)
+{
+ int result = 0;
+ struct voice_data *v = NULL;
+
+ pr_debug("%s\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("%s: cal_block is NULL!\n",
+ __func__);
+
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block->cal_data.paddr == 0) {
+ pr_debug("%s: No address to map!\n",
+ __func__);
+
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (cal_block->map_data.map_size == 0) {
+ pr_debug("%s: map size is 0!\n",
+ __func__);
+
+ result = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&common.common_lock);
+ /* use first session */
+ v = &common.voice[0];
+ mutex_lock(&v->lock);
+
+ if (!is_rtac_memory_allocated()) {
+ result = voice_alloc_rtac_mem_map_table();
+ if (result < 0) {
+ pr_err("%s: RTAC alloc mem map table did not work! addr = 0x%pK, size = %d\n",
+ __func__,
+ &cal_block->cal_data.paddr,
+ cal_block->map_data.map_size);
+
+ goto done_unlock;
+ }
+ }
+
+ result = voice_map_memory_physical_cmd(v,
+ &common.rtac_mem_map_table,
+ (dma_addr_t)cal_block->cal_data.paddr,
+ cal_block->map_data.map_size,
+ VOC_RTAC_MEM_MAP_TOKEN);
+ if (result < 0) {
+ pr_err("%s: RTAC mmap did not work! addr = 0x%pK, size = %d\n",
+ __func__,
+ &cal_block->cal_data.paddr,
+ cal_block->map_data.map_size);
+
+ free_rtac_map_table();
+ goto done_unlock;
+ }
+
+ cal_block->map_data.map_handle = common.rtac_mem_handle;
+done_unlock:
+ mutex_unlock(&v->lock);
+ mutex_unlock(&common.common_lock);
+done:
+ return result;
+}
+
+int voc_unmap_rtac_block(uint32_t *mem_map_handle)
+{
+ int result = 0;
+ struct voice_data *v = NULL;
+
+ pr_debug("%s\n", __func__);
+
+ if (mem_map_handle == NULL) {
+ pr_debug("%s: Map handle is NULL, nothing to unmap\n",
+ __func__);
+
+ goto done;
+ }
+
+ if (*mem_map_handle == 0) {
+ pr_debug("%s: Map handle is 0, nothing to unmap\n",
+ __func__);
+
+ goto done;
+ }
+
+ mutex_lock(&common.common_lock);
+ /* Use first session */
+ /* Only used for apr wait lock */
+ v = &common.voice[0];
+ mutex_lock(&v->lock);
+
+ result = voice_send_mvm_unmap_memory_physical_cmd(
+ v, *mem_map_handle);
+ if (result) {
+ pr_err("%s: voice_send_mvm_unmap_memory_physical_cmd Failed for session 0x%x!\n",
+ __func__, v->session_id);
+ } else {
+ *mem_map_handle = 0;
+ common.rtac_mem_handle = 0;
+ free_rtac_map_table();
+ }
+ mutex_unlock(&v->lock);
+ mutex_unlock(&common.common_lock);
+done:
+ return result;
+}
+
+static int voice_setup_vocproc(struct voice_data *v)
+{
+ struct module_instance_info mod_inst_info = {0};
+ int ret = 0;
+
+ ret = voice_send_cvp_create_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: CVP create failed err:%d\n", __func__, ret);
+ goto fail;
+ }
+
+ ret = voice_send_cvp_media_fmt_info_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Set media format info failed err:%d\n", __func__,
+ ret);
+ goto fail;
+ }
+
+ ret = voice_send_cvp_topology_commit_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Set topology commit failed err:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+
+ mod_inst_info.module_id = MODULE_ID_VOICE_MODULE_ST;
+ mod_inst_info.instance_id = INSTANCE_ID_0;
+
+ voice_send_cvs_register_cal_cmd(v);
+ voice_send_cvp_register_dev_cfg_cmd(v);
+ voice_send_cvp_register_cal_cmd(v);
+ voice_send_cvp_register_vol_cal_cmd(v);
+
+ /* enable vocproc */
+ ret = voice_send_enable_vocproc_cmd(v);
+ if (ret < 0)
+ goto fail;
+
+ /* attach vocproc */
+ ret = voice_send_attach_vocproc_cmd(v);
+ if (ret < 0)
+ goto fail;
+
+ /* send tty mode if tty device is used */
+ voice_send_tty_mode_cmd(v);
+
+ if (is_voip_session(v->session_id)) {
+ ret = voice_send_mvm_cal_network_cmd(v);
+ if (ret < 0)
+ pr_err("%s: voice_send_mvm_cal_network_cmd: %d\n",
+ __func__, ret);
+
+ ret = voice_send_mvm_media_type_cmd(v);
+ if (ret < 0)
+ pr_err("%s: voice_send_mvm_media_type_cmd: %d\n",
+ __func__, ret);
+
+ voice_send_netid_timing_cmd(v);
+ }
+
+ if (v->st_enable && !v->tty_mode)
+ voice_send_set_pp_enable_cmd(v, mod_inst_info, v->st_enable);
+ /* Start in-call music delivery if this feature is enabled */
+ if (v->music_info.play_enable)
+ voice_cvs_start_playback(v);
+
+ /* Start in-call recording if this feature is enabled */
+ if (v->rec_info.rec_enable)
+ voice_cvs_start_record(v, v->rec_info.rec_mode);
+
+ if (v->dtmf_rx_detect_en)
+ voice_send_dtmf_rx_detection_cmd(v, v->dtmf_rx_detect_en);
+
+ if (v->hd_enable)
+ voice_send_hd_cmd(v, v->hd_enable);
+
+ rtac_add_voice(voice_get_cvs_handle(v),
+ voice_get_cvp_handle(v),
+ v->dev_rx.port_id, v->dev_tx.port_id,
+ v->dev_rx.dev_id, v->dev_tx.dev_id,
+ v->session_id);
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int voice_send_cvp_device_channels_cmd(struct voice_data *v)
+{
+ int ret = 0;
+ struct cvp_set_dev_channels_cmd cvp_set_dev_channels_cmd;
+ void *apr_cvp;
+ u16 cvp_handle;
+
+ if (!(voice_get_cvd_int_version(common.cvd_version) >=
+ CVD_INT_VERSION_2_2)) {
+ pr_debug("%s CVD ver %s doesnt support send_device_channels cmd\n",
+ __func__, common.cvd_version);
+
+ goto done;
+ }
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ apr_cvp = common.apr_q6_cvp;
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cvp_handle = voice_get_cvp_handle(v);
+ cvp_set_dev_channels_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_set_dev_channels_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_set_dev_channels_cmd) - APR_HDR_SIZE);
+ cvp_set_dev_channels_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_set_dev_channels_cmd.hdr.dest_port = cvp_handle;
+ cvp_set_dev_channels_cmd.hdr.token = 0;
+ cvp_set_dev_channels_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS;
+ cvp_set_dev_channels_cmd.cvp_set_channels.rx_num_channels =
+ VSS_NUM_DEV_CHANNELS_1;
+ cvp_set_dev_channels_cmd.cvp_set_channels.tx_num_channels =
+ v->dev_tx.no_of_channels;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_set_dev_channels_cmd);
+ if (ret < 0) {
+ pr_err("%s: Fail in sending VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS\n",
+ __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int voice_send_cvp_media_fmt_info_cmd(struct voice_data *v)
+{
+ int ret;
+
+ ret = voice_send_cvp_device_channels_cmd(v);
+ if (ret < 0)
+ goto done;
+
+ if (voice_get_cvd_int_version(common.cvd_version) >=
+ CVD_INT_VERSION_2_3) {
+ ret = voice_send_cvp_media_format_cmd(v, RX_PATH);
+ if (ret < 0)
+ goto done;
+
+ ret = voice_send_cvp_media_format_cmd(v, TX_PATH);
+ if (ret < 0)
+ goto done;
+
+ if (common.ec_ref_ext)
+ ret = voice_send_cvp_media_format_cmd(v, EC_REF_PATH);
+ }
+
+done:
+ return ret;
+}
+
+static int voice_send_cvp_media_format_cmd(struct voice_data *v,
+ uint32_t param_type)
+{
+ struct vss_param_endpoint_media_format_info media_fmt_info = {0};
+ struct param_hdr_v3 param_hdr = {0};
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ param_hdr.module_id = VSS_MODULE_CVD_GENERIC;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_size = sizeof(media_fmt_info);
+
+ switch (param_type) {
+ case RX_PATH:
+ param_hdr.param_id = VSS_PARAM_RX_PORT_ENDPOINT_MEDIA_INFO;
+ media_fmt_info.port_id = v->dev_rx.port_id;
+ media_fmt_info.num_channels = v->dev_rx.no_of_channels;
+ media_fmt_info.bits_per_sample = v->dev_rx.bits_per_sample;
+ media_fmt_info.sample_rate = v->dev_rx.sample_rate;
+ memcpy(&media_fmt_info.channel_mapping,
+ &v->dev_rx.channel_mapping, VSS_CHANNEL_MAPPING_SIZE);
+ break;
+
+ case TX_PATH:
+ param_hdr.param_id = VSS_PARAM_TX_PORT_ENDPOINT_MEDIA_INFO;
+ media_fmt_info.port_id = v->dev_tx.port_id;
+ media_fmt_info.num_channels = v->dev_tx.no_of_channels;
+ media_fmt_info.bits_per_sample = v->dev_tx.bits_per_sample;
+ media_fmt_info.sample_rate = v->dev_tx.sample_rate;
+ memcpy(&media_fmt_info.channel_mapping,
+ &v->dev_tx.channel_mapping, VSS_CHANNEL_MAPPING_SIZE);
+ break;
+
+ case EC_REF_PATH:
+ param_hdr.param_id = VSS_PARAM_EC_REF_PORT_ENDPOINT_MEDIA_INFO;
+ media_fmt_info.port_id = common.ec_media_fmt_info.port_id;
+ media_fmt_info.num_channels =
+ common.ec_media_fmt_info.num_channels;
+ media_fmt_info.bits_per_sample =
+ common.ec_media_fmt_info.bits_per_sample;
+ media_fmt_info.sample_rate =
+ common.ec_media_fmt_info.sample_rate;
+ memcpy(&media_fmt_info.channel_mapping,
+ &common.ec_media_fmt_info.channel_mapping,
+ VSS_CHANNEL_MAPPING_SIZE);
+ break;
+
+ default:
+ pr_err("%s: Invalid param type %d\n", __func__, param_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = voice_pack_and_set_cvp_param(v, param_hdr,
+ (u8 *) &media_fmt_info);
+ if (ret)
+ pr_err("%s: Failed to set media format params on CVP, err %d\n",
+ __func__, ret);
+
+done:
+ return ret;
+}
+
+static int voice_send_cvp_topology_commit_cmd(struct voice_data *v)
+{
+ int ret = 0;
+ struct apr_hdr cvp_topology_commit_cmd;
+ void *apr_cvp;
+ u16 cvp_handle;
+
+ if (!(voice_get_cvd_int_version(common.cvd_version) >=
+ CVD_INT_VERSION_2_2)) {
+ pr_debug("%s CVD version string %s doesnt support this command\n",
+ __func__, common.cvd_version);
+
+ goto done;
+ }
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ apr_cvp = common.apr_q6_cvp;
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cvp_handle = voice_get_cvp_handle(v);
+ cvp_topology_commit_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_topology_commit_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_topology_commit_cmd) - APR_HDR_SIZE);
+ cvp_topology_commit_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_topology_commit_cmd.dest_port = cvp_handle;
+ cvp_topology_commit_cmd.token = 0;
+ cvp_topology_commit_cmd.opcode = VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_topology_commit_cmd);
+ if (ret < 0) {
+ pr_err("%s: Fail in sending VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT\n",
+ __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int voice_send_enable_vocproc_cmd(struct voice_data *v)
+{
+ int ret = 0;
+ struct apr_hdr cvp_enable_cmd;
+ void *apr_cvp;
+ u16 cvp_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvp = common.apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* enable vocproc and wait for respose */
+ cvp_enable_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_enable_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_enable_cmd) - APR_HDR_SIZE);
+ pr_debug("cvp_enable_cmd pkt size = %d, cvp_handle=%d\n",
+ cvp_enable_cmd.pkt_size, cvp_handle);
+ cvp_enable_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_enable_cmd.dest_port = cvp_handle;
+ cvp_enable_cmd.token = 0;
+ cvp_enable_cmd.opcode = VSS_IVOCPROC_CMD_ENABLE;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_enable_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IVOCPROC_CMD_ENABLE\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return ret;
+}
+
+static int voice_send_mvm_cal_network_cmd(struct voice_data *v)
+{
+ struct vss_imvm_cmd_set_cal_network_t mvm_set_cal_network;
+ int ret = 0;
+ void *apr_mvm;
+ u16 mvm_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+
+ mvm_set_cal_network.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_set_cal_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_set_cal_network) - APR_HDR_SIZE);
+ mvm_set_cal_network.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_set_cal_network.hdr.dest_port = mvm_handle;
+ mvm_set_cal_network.hdr.token = 0;
+ mvm_set_cal_network.hdr.opcode = VSS_IMVM_CMD_SET_CAL_NETWORK;
+ mvm_set_cal_network.network_id = VSS_ICOMMON_CAL_NETWORK_ID_NONE;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_cal_network);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_NETWORK\n", __func__, ret);
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout %d\n", __func__, ret);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ return 0;
+fail:
+ return ret;
+}
+
+static int voice_send_netid_timing_cmd(struct voice_data *v)
+{
+ int ret = 0;
+ void *apr_mvm;
+ u16 mvm_handle;
+ struct mvm_set_network_cmd mvm_set_network;
+ struct mvm_set_voice_timing_cmd mvm_set_voice_timing;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+
+ ret = voice_config_cvs_vocoder(v);
+ if (ret < 0) {
+ pr_err("%s: Error %d configuring CVS voc",
+ __func__, ret);
+ goto fail;
+ }
+ /* Set network ID. */
+ pr_debug("Setting network ID %x\n", common.mvs_info.network_type);
+
+ mvm_set_network.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_set_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_set_network) - APR_HDR_SIZE);
+ mvm_set_network.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_set_network.hdr.dest_port = mvm_handle;
+ mvm_set_network.hdr.token = 0;
+ mvm_set_network.hdr.opcode = VSS_IMVM_CMD_SET_CAL_NETWORK;
+ mvm_set_network.network.network_id = common.mvs_info.network_type;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_network);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_NETWORK\n", __func__, ret);
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ /* Set voice timing. */
+ pr_debug("Setting voice timing\n");
+
+ mvm_set_voice_timing.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_set_voice_timing.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_set_voice_timing) -
+ APR_HDR_SIZE);
+ mvm_set_voice_timing.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_set_voice_timing.hdr.dest_port = mvm_handle;
+ mvm_set_voice_timing.hdr.token = 0;
+ mvm_set_voice_timing.hdr.opcode = VSS_ICOMMON_CMD_SET_VOICE_TIMING;
+ mvm_set_voice_timing.timing.mode = 0;
+ mvm_set_voice_timing.timing.enc_offset = 8000;
+ mvm_set_voice_timing.timing.dec_req_offset = 3300;
+ mvm_set_voice_timing.timing.dec_offset = 8300;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_voice_timing);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_TIMING\n", __func__, ret);
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return ret;
+}
+
+static int voice_send_attach_vocproc_cmd(struct voice_data *v)
+{
+ int ret = 0;
+ struct mvm_attach_vocproc_cmd mvm_a_vocproc_cmd;
+ void *apr_mvm;
+ u16 mvm_handle, cvp_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* attach vocproc and wait for response */
+ mvm_a_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_a_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_a_vocproc_cmd) - APR_HDR_SIZE);
+ pr_debug("send mvm_a_vocproc_cmd pkt size = %d\n",
+ mvm_a_vocproc_cmd.hdr.pkt_size);
+ mvm_a_vocproc_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_a_vocproc_cmd.hdr.dest_port = mvm_handle;
+ mvm_a_vocproc_cmd.hdr.token = 0;
+ mvm_a_vocproc_cmd.hdr.opcode = VSS_IMVM_CMD_ATTACH_VOCPROC;
+ mvm_a_vocproc_cmd.mvm_attach_cvp_handle.handle = cvp_handle;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_a_vocproc_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IMVM_CMD_ATTACH_VOCPROC\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return ret;
+}
+
+static void voc_update_session_params(struct voice_data *v)
+{
+ /* reset LCH mode */
+ v->lch_mode = 0;
+
+ /* clear disable topology setting */
+ v->disable_topology = false;
+
+ /* clear mute setting */
+ v->dev_rx.dev_mute = common.default_mute_val;
+ v->dev_tx.dev_mute = common.default_mute_val;
+ v->stream_rx.stream_mute = common.default_mute_val;
+ v->stream_tx.stream_mute = common.default_mute_val;
+}
+
+static int voice_destroy_vocproc(struct voice_data *v)
+{
+ struct mvm_detach_vocproc_cmd mvm_d_vocproc_cmd;
+ struct apr_hdr cvp_destroy_session_cmd;
+ struct module_instance_info mod_inst_info = {0};
+ int ret = 0;
+ void *apr_mvm, *apr_cvp;
+ u16 mvm_handle, cvp_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+ apr_cvp = common.apr_q6_cvp;
+
+ if (!apr_mvm || !apr_cvp) {
+ pr_err("%s: apr_mvm or apr_cvp is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+ cvp_handle = voice_get_cvp_handle(v);
+
+ mod_inst_info.module_id = MODULE_ID_VOICE_MODULE_ST;
+ mod_inst_info.instance_id = INSTANCE_ID_0;
+
+ /* disable slowtalk if st_enable is set */
+ if (v->st_enable)
+ voice_send_set_pp_enable_cmd(v, mod_inst_info, 0);
+
+ /* Disable HD Voice if hd_enable is set */
+ if (v->hd_enable)
+ voice_send_hd_cmd(v, 0);
+
+ /* stop playback or recording */
+ v->music_info.force = 1;
+ voice_cvs_stop_playback(v);
+ voice_cvs_stop_record(v);
+ /* If voice call is active during VoLTE, SRVCC happens.
+ Start recording on voice session if recording started during VoLTE.
+ */
+ if (is_volte_session(v->session_id) &&
+ ((common.voice[VOC_PATH_PASSIVE].voc_state == VOC_RUN) ||
+ (common.voice[VOC_PATH_PASSIVE].voc_state == VOC_CHANGE))) {
+ if (v->rec_info.rec_enable) {
+ voice_cvs_start_record(
+ &common.voice[VOC_PATH_PASSIVE],
+ v->rec_info.rec_mode);
+ common.srvcc_rec_flag = true;
+
+ pr_debug("%s: switch recording, srvcc_rec_flag %d\n",
+ __func__, common.srvcc_rec_flag);
+ }
+ }
+
+ /* send stop voice cmd */
+ voice_send_stop_voice_cmd(v);
+
+ /* send stop dtmf detecton cmd */
+ if (v->dtmf_rx_detect_en)
+ voice_send_dtmf_rx_detection_cmd(v, 0);
+
+ /* detach VOCPROC and wait for response from mvm */
+ mvm_d_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_d_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_d_vocproc_cmd) - APR_HDR_SIZE);
+ pr_debug("mvm_d_vocproc_cmd pkt size = %d\n",
+ mvm_d_vocproc_cmd.hdr.pkt_size);
+ mvm_d_vocproc_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_d_vocproc_cmd.hdr.dest_port = mvm_handle;
+ mvm_d_vocproc_cmd.hdr.token = 0;
+ mvm_d_vocproc_cmd.hdr.opcode = VSS_IMVM_CMD_DETACH_VOCPROC;
+ mvm_d_vocproc_cmd.mvm_detach_cvp_handle.handle = cvp_handle;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_d_vocproc_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IMVM_CMD_DETACH_VOCPROC\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ voice_send_cvp_deregister_vol_cal_cmd(v);
+ voice_send_cvp_deregister_cal_cmd(v);
+ voice_send_cvp_deregister_dev_cfg_cmd(v);
+ voice_send_cvs_deregister_cal_cmd(v);
+
+ /* destrop cvp session */
+ cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_destroy_session_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_destroy_session_cmd) - APR_HDR_SIZE);
+ pr_debug("cvp_destroy_session_cmd pkt size = %d\n",
+ cvp_destroy_session_cmd.pkt_size);
+ cvp_destroy_session_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_destroy_session_cmd.dest_port = cvp_handle;
+ cvp_destroy_session_cmd.token = 0;
+ cvp_destroy_session_cmd.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_destroy_session_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending APRV2_IBASIC_CMD_DESTROY_SESSION\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ rtac_remove_voice(voice_get_cvs_handle(v));
+ cvp_handle = 0;
+ voice_set_cvp_handle(v, cvp_handle);
+ return 0;
+fail:
+ return ret;
+}
+
+static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v,
+ uint32_t mem_handle)
+{
+ struct vss_imemory_cmd_unmap_t mem_unmap;
+ int ret = 0;
+ void *apr_mvm;
+ u16 mvm_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = common.apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+
+ mem_unmap.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mem_unmap.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mem_unmap) - APR_HDR_SIZE);
+ mem_unmap.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mem_unmap.hdr.dest_port = mvm_handle;
+ mem_unmap.hdr.token = 0;
+ mem_unmap.hdr.opcode = VSS_IMEMORY_CMD_UNMAP;
+ mem_unmap.mem_handle = mem_handle;
+
+ pr_debug("%s: mem_handle: 0x%x\n", __func__, mem_unmap.mem_handle);
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mem_unmap);
+ if (ret < 0) {
+ pr_err("mem_unmap op[0x%x]ret[%d]\n",
+ mem_unmap.hdr.opcode, ret);
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout %d\n", __func__, ret);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v)
+{
+ struct vss_istream_cmd_set_oob_packet_exchange_config_t
+ packet_exchange_config_pkt;
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ cvs_handle = voice_get_cvs_handle(v);
+
+ packet_exchange_config_pkt.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ packet_exchange_config_pkt.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(packet_exchange_config_pkt) -
+ APR_HDR_SIZE);
+ packet_exchange_config_pkt.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ packet_exchange_config_pkt.hdr.dest_port = cvs_handle;
+ packet_exchange_config_pkt.hdr.token = 0;
+ packet_exchange_config_pkt.hdr.opcode =
+ VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG;
+ packet_exchange_config_pkt.mem_handle = v->shmem_info.mem_handle;
+ /* dec buffer address */
+ packet_exchange_config_pkt.dec_buf_addr_lsw =
+ lower_32_bits(v->shmem_info.sh_buf.buf[0].phys);
+ packet_exchange_config_pkt.dec_buf_addr_msw =
+ msm_audio_populate_upper_32_bits(
+ v->shmem_info.sh_buf.buf[0].phys);
+ packet_exchange_config_pkt.dec_buf_size = 4096;
+ /* enc buffer address */
+ packet_exchange_config_pkt.enc_buf_addr_lsw =
+ lower_32_bits(v->shmem_info.sh_buf.buf[1].phys);
+ packet_exchange_config_pkt.enc_buf_addr_msw =
+ msm_audio_populate_upper_32_bits(
+ v->shmem_info.sh_buf.buf[1].phys);
+ packet_exchange_config_pkt.enc_buf_size = 4096;
+
+ pr_debug("%s: dec buf add: lsw %0x msw %0x, size %d, enc buf add: lsw %0x msw %0x, size %d\n",
+ __func__,
+ packet_exchange_config_pkt.dec_buf_addr_lsw,
+ packet_exchange_config_pkt.dec_buf_addr_msw,
+ packet_exchange_config_pkt.dec_buf_size,
+ packet_exchange_config_pkt.enc_buf_addr_lsw,
+ packet_exchange_config_pkt.enc_buf_addr_msw,
+ packet_exchange_config_pkt.enc_buf_size);
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &packet_exchange_config_pkt);
+ if (ret < 0) {
+ pr_err("Failed to send packet exchange config cmd %d\n", ret);
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret)
+ pr_err("%s: wait_event timeout %d\n", __func__, ret);
+
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return ret;
+}
+
+static int voice_send_cvs_data_exchange_mode_cmd(struct voice_data *v)
+{
+ struct vss_istream_cmd_set_packet_exchange_mode_t data_exchange_pkt;
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ cvs_handle = voice_get_cvs_handle(v);
+
+ data_exchange_pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ data_exchange_pkt.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(data_exchange_pkt) - APR_HDR_SIZE);
+ data_exchange_pkt.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ data_exchange_pkt.hdr.dest_port = cvs_handle;
+ data_exchange_pkt.hdr.token = 0;
+ data_exchange_pkt.hdr.opcode = VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE;
+ data_exchange_pkt.mode = VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &data_exchange_pkt);
+ if (ret < 0) {
+ pr_err("Failed to send data exchange mode %d\n", ret);
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret)
+ pr_err("%s: wait_event timeout %d\n", __func__, ret);
+
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ return 0;
+fail:
+ return ret;
+}
+
+static int voice_send_stream_mute_cmd(struct voice_data *v, uint16_t direction,
+ uint16_t mute_flag, uint32_t ramp_duration)
+{
+ struct cvs_set_mute_cmd cvs_mute_cmd;
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!common.apr_q6_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* send mute/unmute to cvs */
+ cvs_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_mute_cmd) - APR_HDR_SIZE);
+ cvs_mute_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_mute_cmd.hdr.dest_port = voice_get_cvs_handle(v);
+ cvs_mute_cmd.hdr.token = 0;
+ cvs_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2;
+ cvs_mute_cmd.cvs_set_mute.direction = direction;
+ cvs_mute_cmd.cvs_set_mute.mute_flag = mute_flag;
+ cvs_mute_cmd.cvs_set_mute.ramp_duration_ms = ramp_duration;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_mute_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending stream mute\n", __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int voice_send_device_mute_cmd(struct voice_data *v, uint16_t direction,
+ uint16_t mute_flag, uint32_t ramp_duration)
+{
+ struct cvp_set_mute_cmd cvp_mute_cmd;
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ cvp_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_mute_cmd) - APR_HDR_SIZE);
+ cvp_mute_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_mute_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_mute_cmd.hdr.token = 0;
+ cvp_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2;
+ cvp_mute_cmd.cvp_set_mute.direction = direction;
+ cvp_mute_cmd.cvp_set_mute.mute_flag = mute_flag;
+ cvp_mute_cmd.cvp_set_mute.ramp_duration_ms = ramp_duration;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_mute_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending rx device cmd\n", __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int voice_send_vol_step_cmd(struct voice_data *v)
+{
+ struct cvp_set_rx_volume_step_cmd cvp_vol_step_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ u16 cvp_handle;
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvp = common.apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* send volume index to cvp */
+ cvp_vol_step_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_vol_step_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_vol_step_cmd) - APR_HDR_SIZE);
+ cvp_vol_step_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_vol_step_cmd.hdr.dest_port = cvp_handle;
+ cvp_vol_step_cmd.hdr.token = 0;
+ cvp_vol_step_cmd.hdr.opcode = VSS_IVOLUME_CMD_SET_STEP;
+ cvp_vol_step_cmd.cvp_set_vol_step.direction = VSS_IVOLUME_DIRECTION_RX;
+ cvp_vol_step_cmd.cvp_set_vol_step.value = v->dev_rx.volume_step_value;
+ cvp_vol_step_cmd.cvp_set_vol_step.ramp_duration_ms =
+ v->dev_rx.volume_ramp_duration_ms;
+ pr_debug("%s step_value:%d, ramp_duration_ms:%d",
+ __func__,
+ cvp_vol_step_cmd.cvp_set_vol_step.value,
+ cvp_vol_step_cmd.cvp_set_vol_step.ramp_duration_ms);
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_vol_step_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending RX VOL step\n");
+ return -EINVAL;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ return -EINVAL;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ return ret;
+ }
+ return 0;
+}
+
+static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode)
+{
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+
+ struct cvs_start_record_cmd cvs_start_record;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ if (!v->rec_info.recording) {
+ cvs_start_record.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_start_record.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_start_record) - APR_HDR_SIZE);
+ cvs_start_record.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_start_record.hdr.dest_port = cvs_handle;
+ cvs_start_record.hdr.token = 0;
+ cvs_start_record.hdr.opcode = VSS_IRECORD_CMD_START;
+
+ cvs_start_record.rec_mode.port_id =
+ VSS_IRECORD_PORT_ID_DEFAULT;
+ if (rec_mode == VOC_REC_UPLINK) {
+ cvs_start_record.rec_mode.rx_tap_point =
+ VSS_IRECORD_TAP_POINT_NONE;
+ cvs_start_record.rec_mode.tx_tap_point =
+ VSS_IRECORD_TAP_POINT_STREAM_END;
+ } else if (rec_mode == VOC_REC_DOWNLINK) {
+ cvs_start_record.rec_mode.rx_tap_point =
+ VSS_IRECORD_TAP_POINT_STREAM_END;
+ cvs_start_record.rec_mode.tx_tap_point =
+ VSS_IRECORD_TAP_POINT_NONE;
+ } else if (rec_mode == VOC_REC_BOTH) {
+ cvs_start_record.rec_mode.rx_tap_point =
+ VSS_IRECORD_TAP_POINT_STREAM_END;
+ cvs_start_record.rec_mode.tx_tap_point =
+ VSS_IRECORD_TAP_POINT_STREAM_END;
+ } else {
+ pr_err("%s: Invalid in-call rec_mode %d\n", __func__,
+ rec_mode);
+
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_record);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending START_RECORD\n", __func__,
+ ret);
+
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ v->rec_info.recording = 1;
+ } else {
+ pr_debug("%s: Start record already sent\n", __func__);
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int voice_cvs_stop_record(struct voice_data *v)
+{
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+ struct apr_hdr cvs_stop_record;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ if (v->rec_info.recording) {
+ cvs_stop_record.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvs_stop_record.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_stop_record) - APR_HDR_SIZE);
+ cvs_stop_record.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_stop_record.dest_port = cvs_handle;
+ cvs_stop_record.token = 0;
+ cvs_stop_record.opcode = VSS_IRECORD_CMD_STOP;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_record);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending STOP_RECORD\n",
+ __func__, ret);
+
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+ v->rec_info.recording = 0;
+ } else {
+ pr_debug("%s: Stop record already sent\n", __func__);
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+int voc_start_record(uint32_t port_id, uint32_t set, uint32_t session_id)
+{
+ int ret = 0;
+ int rec_mode = 0;
+ u16 cvs_handle;
+ int rec_set = 0;
+ struct voice_session_itr itr;
+ struct voice_data *v = NULL;
+
+ /* check if session_id is valid */
+ if (!voice_is_valid_session_id(session_id)) {
+ pr_err("%s: Invalid session id:%u\n", __func__,
+ session_id);
+
+ return -EINVAL;
+ }
+
+ voice_itr_init(&itr, session_id);
+ pr_debug("%s: session_id:%u\n", __func__, session_id);
+
+ while (voice_itr_get_next_session(&itr, &v)) {
+ if (v == NULL) {
+ pr_err("%s: v is NULL, sessionid:%u\n", __func__,
+ session_id);
+
+ break;
+ }
+ pr_debug("%s: port_id: %d, set: %d, v: %pK\n",
+ __func__, port_id, set, v);
+
+ mutex_lock(&v->lock);
+ rec_mode = v->rec_info.rec_mode;
+ rec_set = set;
+ if (set) {
+ if ((v->rec_route_state.ul_flag != 0) &&
+ (v->rec_route_state.dl_flag != 0)) {
+ pr_debug("%s: rec mode already set.\n",
+ __func__);
+
+ mutex_unlock(&v->lock);
+ continue;
+ }
+
+ if (port_id == VOICE_RECORD_TX) {
+ if ((v->rec_route_state.ul_flag == 0)
+ && (v->rec_route_state.dl_flag == 0)) {
+ rec_mode = VOC_REC_UPLINK;
+ v->rec_route_state.ul_flag = 1;
+ } else if ((v->rec_route_state.ul_flag == 0)
+ && (v->rec_route_state.dl_flag != 0)) {
+ voice_cvs_stop_record(v);
+ rec_mode = VOC_REC_BOTH;
+ v->rec_route_state.ul_flag = 1;
+ }
+ } else if (port_id == VOICE_RECORD_RX) {
+ if ((v->rec_route_state.ul_flag == 0)
+ && (v->rec_route_state.dl_flag == 0)) {
+ rec_mode = VOC_REC_DOWNLINK;
+ v->rec_route_state.dl_flag = 1;
+ } else if ((v->rec_route_state.ul_flag != 0)
+ && (v->rec_route_state.dl_flag == 0)) {
+ voice_cvs_stop_record(v);
+ rec_mode = VOC_REC_BOTH;
+ v->rec_route_state.dl_flag = 1;
+ }
+ }
+ rec_set = 1;
+ } else {
+ if ((v->rec_route_state.ul_flag == 0) &&
+ (v->rec_route_state.dl_flag == 0)) {
+ pr_debug("%s: rec already stops.\n",
+ __func__);
+ mutex_unlock(&v->lock);
+ continue;
+ }
+
+ if (port_id == VOICE_RECORD_TX) {
+ if ((v->rec_route_state.ul_flag != 0)
+ && (v->rec_route_state.dl_flag == 0)) {
+ v->rec_route_state.ul_flag = 0;
+ rec_set = 0;
+ } else if ((v->rec_route_state.ul_flag != 0)
+ && (v->rec_route_state.dl_flag != 0)) {
+ voice_cvs_stop_record(v);
+ v->rec_route_state.ul_flag = 0;
+ rec_mode = VOC_REC_DOWNLINK;
+ rec_set = 1;
+ }
+ } else if (port_id == VOICE_RECORD_RX) {
+ if ((v->rec_route_state.ul_flag == 0)
+ && (v->rec_route_state.dl_flag != 0)) {
+ v->rec_route_state.dl_flag = 0;
+ rec_set = 0;
+ } else if ((v->rec_route_state.ul_flag != 0)
+ && (v->rec_route_state.dl_flag != 0)) {
+ voice_cvs_stop_record(v);
+ v->rec_route_state.dl_flag = 0;
+ rec_mode = VOC_REC_UPLINK;
+ rec_set = 1;
+ }
+ }
+ }
+ pr_debug("%s: mode =%d, set =%d\n", __func__,
+ rec_mode, rec_set);
+ cvs_handle = voice_get_cvs_handle(v);
+
+ if (cvs_handle != 0) {
+ if (rec_set)
+ ret = voice_cvs_start_record(v, rec_mode);
+ else
+ ret = voice_cvs_stop_record(v);
+ }
+
+ /* During SRVCC, recording will switch from VoLTE session to
+ voice session.
+ Then stop recording, need to stop recording on voice session.
+ */
+ if ((!rec_set) && common.srvcc_rec_flag) {
+ pr_debug("%s, srvcc_rec_flag:%d\n", __func__,
+ common.srvcc_rec_flag);
+
+ voice_cvs_stop_record(&common.voice[VOC_PATH_PASSIVE]);
+ common.srvcc_rec_flag = false;
+ }
+
+ /* Cache the value */
+ v->rec_info.rec_enable = rec_set;
+ v->rec_info.rec_mode = rec_mode;
+
+ mutex_unlock(&v->lock);
+ }
+
+ return ret;
+}
+
+static int voice_cvs_start_playback(struct voice_data *v)
+{
+ int ret = 0;
+ struct cvs_start_playback_cmd cvs_start_playback;
+ void *apr_cvs;
+ u16 cvs_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ if (!v->music_info.playing && v->music_info.count) {
+ cvs_start_playback.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_start_playback.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_start_playback) - APR_HDR_SIZE);
+ cvs_start_playback.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_start_playback.hdr.dest_port = cvs_handle;
+ cvs_start_playback.hdr.token = 0;
+ cvs_start_playback.hdr.opcode = VSS_IPLAYBACK_CMD_START;
+ cvs_start_playback.playback_mode.port_id =
+ v->music_info.port_id;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_playback);
+
+ if (ret < 0) {
+ pr_err("%s: Error %d sending START_PLAYBACK\n",
+ __func__, ret);
+
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ v->music_info.playing = 1;
+ } else {
+ pr_debug("%s: Start playback already sent\n", __func__);
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int voice_cvs_stop_playback(struct voice_data *v)
+{
+ int ret = 0;
+ struct apr_hdr cvs_stop_playback;
+ void *apr_cvs;
+ u16 cvs_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ if (v->music_info.playing && ((!v->music_info.count) ||
+ (v->music_info.force))) {
+ cvs_stop_playback.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvs_stop_playback.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_stop_playback) - APR_HDR_SIZE);
+ cvs_stop_playback.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvs_stop_playback.dest_port = cvs_handle;
+ cvs_stop_playback.token = 0;
+
+ cvs_stop_playback.opcode = VSS_IPLAYBACK_CMD_STOP;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_playback);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending STOP_PLAYBACK\n",
+ __func__, ret);
+
+
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto fail;
+ }
+
+ v->music_info.playing = 0;
+ v->music_info.force = 0;
+ } else {
+ pr_debug("%s: Stop playback already sent\n", __func__);
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int voc_lch_ops(struct voice_data *v, enum voice_lch_mode lch_mode)
+{
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ switch (lch_mode) {
+ case VOICE_LCH_START:
+
+ ret = voc_end_voice_call(v->session_id);
+ if (ret < 0)
+ pr_err("%s: voice call end failed %d\n",
+ __func__, ret);
+ break;
+ case VOICE_LCH_STOP:
+
+ ret = voc_start_voice_call(v->session_id);
+ if (ret < 0) {
+ pr_err("%s: voice call start failed %d\n",
+ __func__, ret);
+ goto done;
+ }
+ break;
+ default:
+ pr_err("%s: Invalid LCH mode: %d\n",
+ __func__, v->lch_mode);
+ break;
+ }
+done:
+ return ret;
+}
+
+int voc_start_playback(uint32_t set, uint16_t port_id)
+{
+ struct voice_data *v = NULL;
+ int ret = 0;
+ struct voice_session_itr itr;
+ u16 cvs_handle;
+
+ pr_debug("%s port_id = %#x set = %d", __func__, port_id, set);
+
+ voice_itr_init(&itr, ALL_SESSION_VSID);
+ while (voice_itr_get_next_session(&itr, &v)) {
+ if ((v != NULL) &&
+ (((port_id == VOICE_PLAYBACK_TX) &&
+ is_sub1_vsid(v->session_id)) ||
+ ((port_id == VOICE2_PLAYBACK_TX) &&
+ is_sub2_vsid(v->session_id)))) {
+
+ mutex_lock(&v->lock);
+ v->music_info.port_id = port_id;
+ v->music_info.play_enable = set;
+ if (set)
+ v->music_info.count++;
+ else
+ v->music_info.count--;
+ pr_debug("%s: music_info count=%d\n", __func__,
+ v->music_info.count);
+
+ cvs_handle = voice_get_cvs_handle(v);
+ if (cvs_handle != 0) {
+ if (set)
+ ret = voice_cvs_start_playback(v);
+ else
+ ret = voice_cvs_stop_playback(v);
+ }
+ mutex_unlock(&v->lock);
+ } else {
+ pr_err("%s: Invalid session\n", __func__);
+ }
+ }
+
+ return ret;
+}
+
+int voc_disable_topology(uint32_t session_id, uint32_t disable)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+
+ v->disable_topology = disable;
+
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+static int voice_set_packet_exchange_mode_and_config(uint32_t session_id,
+ uint32_t mode)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+ return -EINVAL;
+ }
+
+ if (v->voc_state != VOC_RUN)
+ ret = voice_send_cvs_data_exchange_mode_cmd(v);
+
+ if (ret) {
+ pr_err("%s: Error voice_send_data_exchange_mode_cmd %d\n",
+ __func__, ret);
+ goto fail;
+ }
+
+ ret = voice_send_cvs_packet_exchange_config_cmd(v);
+ if (ret) {
+ pr_err("%s: Error: voice_send_packet_exchange_config_cmd %d\n",
+ __func__, ret);
+ goto fail;
+ }
+
+ return ret;
+fail:
+ return -EINVAL;
+}
+
+int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+ uint32_t ramp_duration)
+{
+ struct voice_data *v = NULL;
+ int ret = 0;
+ struct voice_session_itr itr;
+
+ voice_itr_init(&itr, session_id);
+ while (voice_itr_get_next_session(&itr, &v)) {
+ if (v != NULL) {
+ mutex_lock(&v->lock);
+ v->stream_tx.stream_mute = mute;
+ v->stream_tx.stream_mute_ramp_duration_ms =
+ ramp_duration;
+ if (is_voc_state_active(v->voc_state) &&
+ (v->lch_mode == 0))
+ ret = voice_send_stream_mute_cmd(v,
+ VSS_IVOLUME_DIRECTION_TX,
+ v->stream_tx.stream_mute,
+ v->stream_tx.stream_mute_ramp_duration_ms);
+ mutex_unlock(&v->lock);
+ } else {
+ pr_err("%s: invalid session_id 0x%x\n", __func__,
+ session_id);
+
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+ uint32_t ramp_duration)
+{
+ struct voice_data *v = NULL;
+ int ret = 0;
+ struct voice_session_itr itr;
+
+ voice_itr_init(&itr, session_id);
+ while (voice_itr_get_next_session(&itr, &v)) {
+ if (v != NULL) {
+ mutex_lock(&v->lock);
+ if (dir == VSS_IVOLUME_DIRECTION_TX) {
+ v->dev_tx.dev_mute = mute;
+ v->dev_tx.dev_mute_ramp_duration_ms =
+ ramp_duration;
+ } else {
+ v->dev_rx.dev_mute = mute;
+ v->dev_rx.dev_mute_ramp_duration_ms =
+ ramp_duration;
+ }
+
+ if (((v->voc_state == VOC_RUN) ||
+ (v->voc_state == VOC_STANDBY)) &&
+ (v->lch_mode == 0))
+ ret = voice_send_device_mute_cmd(v,
+ dir,
+ mute,
+ ramp_duration);
+ mutex_unlock(&v->lock);
+ } else {
+ pr_err("%s: invalid session_id 0x%x\n", __func__,
+ session_id);
+
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int voc_get_rx_device_mute(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+
+ ret = v->dev_rx.dev_mute;
+
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_set_tty_mode(uint32_t session_id, uint8_t tty_mode)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+
+ v->tty_mode = tty_mode;
+
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+uint8_t voc_get_tty_mode(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+
+ ret = v->tty_mode;
+
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_set_pp_enable(uint32_t session_id,
+ struct module_instance_info mod_inst_info,
+ uint32_t enable)
+{
+ struct voice_data *v = NULL;
+ int ret = 0;
+ struct voice_session_itr itr;
+ int mid = mod_inst_info.module_id;
+ int iid = mod_inst_info.instance_id;
+
+ voice_itr_init(&itr, session_id);
+ while (voice_itr_get_next_session(&itr, &v)) {
+ if (v != NULL) {
+ if (!(is_voice_app_id(v->session_id)))
+ continue;
+
+ mutex_lock(&v->lock);
+ if (mid == MODULE_ID_VOICE_MODULE_ST &&
+ iid == INSTANCE_ID_0)
+ v->st_enable = enable;
+
+ if (v->voc_state == VOC_RUN) {
+ if ((mid == MODULE_ID_VOICE_MODULE_ST) &&
+ iid == INSTANCE_ID_0 && (!v->tty_mode))
+ ret = voice_send_set_pp_enable_cmd(
+ v, mod_inst_info, enable);
+ }
+ mutex_unlock(&v->lock);
+ } else {
+ pr_err("%s: invalid session_id 0x%x\n", __func__,
+ session_id);
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int voc_set_hd_enable(uint32_t session_id, uint32_t enable)
+{
+ struct voice_data *v = NULL;
+ int ret = 0;
+ struct voice_session_itr itr;
+
+ voice_itr_init(&itr, session_id);
+ while (voice_itr_get_next_session(&itr, &v)) {
+ if (v != NULL) {
+ mutex_lock(&v->lock);
+ v->hd_enable = enable;
+
+ if (v->voc_state == VOC_RUN)
+ ret = voice_send_hd_cmd(v, enable);
+
+ mutex_unlock(&v->lock);
+ } else {
+ pr_err("%s: invalid session_id 0x%x\n", __func__,
+ session_id);
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int voc_set_afe_sidetone(uint32_t session_id, bool sidetone_enable)
+{
+ struct voice_data *v = NULL;
+ int ret = -EINVAL;
+ struct voice_session_itr itr;
+ u16 rx_port, tx_port;
+
+ common.sidetone_enable = sidetone_enable;
+ voice_itr_init(&itr, session_id);
+ while (voice_itr_get_next_session(&itr, &v)) {
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__,
+ session_id);
+ ret = -EINVAL;
+ break;
+ }
+ mutex_lock(&v->lock);
+ if (v->voc_state != VOC_RUN) {
+ mutex_unlock(&v->lock);
+ continue;
+ }
+ rx_port = v->dev_rx.port_id;
+ tx_port = v->dev_tx.port_id;
+ ret = afe_sidetone_enable(tx_port, rx_port,
+ sidetone_enable);
+ if (!ret) {
+ mutex_unlock(&v->lock);
+ break;
+ }
+ mutex_unlock(&v->lock);
+ }
+ return ret;
+}
+
+bool voc_get_afe_sidetone(void)
+{
+ bool ret;
+
+ ret = common.sidetone_enable;
+ return ret;
+}
+
+int voc_get_pp_enable(uint32_t session_id,
+ struct module_instance_info mod_inst_info)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+ if (mod_inst_info.module_id == MODULE_ID_VOICE_MODULE_ST &&
+ mod_inst_info.instance_id == INSTANCE_ID_0)
+ ret = v->st_enable;
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_set_rx_vol_step(uint32_t session_id, uint32_t dir, uint32_t vol_step,
+ uint32_t ramp_duration)
+{
+ struct voice_data *v = NULL;
+ int ret = 0;
+ struct voice_session_itr itr;
+
+ pr_debug("%s session id = %#x vol = %u", __func__, session_id,
+ vol_step);
+
+ voice_itr_init(&itr, session_id);
+ while (voice_itr_get_next_session(&itr, &v)) {
+ if (v != NULL) {
+ mutex_lock(&v->lock);
+ v->dev_rx.volume_step_value = vol_step;
+ v->dev_rx.volume_ramp_duration_ms = ramp_duration;
+ if (is_voc_state_active(v->voc_state))
+ ret = voice_send_vol_step_cmd(v);
+ mutex_unlock(&v->lock);
+ } else {
+ pr_err("%s: invalid session_id 0x%x\n", __func__,
+ session_id);
+
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int voc_set_device_config(uint32_t session_id, uint8_t path_dir,
+ struct media_format_info *finfo)
+{
+ struct voice_data *v = voice_get_session(session_id);
+
+ if (v == NULL) {
+ pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id);
+
+ return -EINVAL;
+ }
+
+ pr_debug("%s: path_dir=%d port_id=%x, channels=%d, sample_rate=%d, bits_per_sample=%d\n",
+ __func__, path_dir, finfo->port_id, finfo->num_channels,
+ finfo->sample_rate, finfo->bits_per_sample);
+
+ mutex_lock(&v->lock);
+ switch (path_dir) {
+ case RX_PATH:
+ v->dev_rx.port_id = q6audio_get_port_id(finfo->port_id);
+ v->dev_rx.no_of_channels = finfo->num_channels;
+ v->dev_rx.sample_rate = finfo->sample_rate;
+ v->dev_rx.bits_per_sample = finfo->bits_per_sample;
+ memcpy(&v->dev_rx.channel_mapping, &finfo->channel_mapping,
+ VSS_CHANNEL_MAPPING_SIZE);
+ break;
+ case TX_PATH:
+ v->dev_tx.port_id = q6audio_get_port_id(finfo->port_id);
+ v->dev_tx.no_of_channels = finfo->num_channels;
+ v->dev_tx.sample_rate = finfo->sample_rate;
+ v->dev_tx.bits_per_sample = finfo->bits_per_sample;
+ memcpy(&v->dev_tx.channel_mapping, &finfo->channel_mapping,
+ VSS_CHANNEL_MAPPING_SIZE);
+ break;
+ default:
+ pr_err("%s: Invalid path_dir %d\n", __func__, path_dir);
+ mutex_unlock(&v->lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&v->lock);
+
+ return 0;
+}
+
+int voc_set_ext_ec_ref_media_fmt_info(struct media_format_info *finfo)
+{
+ mutex_lock(&common.common_lock);
+ if (common.ec_ref_ext) {
+ common.ec_media_fmt_info.num_channels = finfo->num_channels;
+ common.ec_media_fmt_info.bits_per_sample =
+ finfo->bits_per_sample;
+ common.ec_media_fmt_info.sample_rate = finfo->sample_rate;
+ memcpy(&common.ec_media_fmt_info.channel_mapping,
+ &finfo->channel_mapping, VSS_CHANNEL_MAPPING_SIZE);
+ } else {
+ pr_debug("%s: Ext Ec Ref not active, returning", __func__);
+ }
+ mutex_unlock(&common.common_lock);
+ return 0;
+}
+
+int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set)
+{
+ struct voice_data *v = voice_get_session(session_id);
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+ return -EINVAL;
+ }
+
+ pr_debug("%s: path_dir=%d, set=%d\n", __func__, path_dir, set);
+
+ mutex_lock(&v->lock);
+
+ if (path_dir == RX_PATH)
+ v->voc_route_state.rx_route_flag = set;
+ else
+ v->voc_route_state.tx_route_flag = set;
+
+ mutex_unlock(&v->lock);
+
+ return 0;
+}
+
+uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+ return 0;
+ }
+
+ mutex_lock(&v->lock);
+
+ if (path_dir == RX_PATH)
+ ret = v->voc_route_state.rx_route_flag;
+ else
+ ret = v->voc_route_state.tx_route_flag;
+
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_end_voice_call(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+
+ if (v->voc_state == VOC_RUN || v->voc_state == VOC_ERROR ||
+ v->voc_state == VOC_CHANGE || v->voc_state == VOC_STANDBY) {
+
+ pr_debug("%s: VOC_STATE: %d\n", __func__, v->voc_state);
+
+ ret = voice_destroy_vocproc(v);
+ if (ret < 0)
+ pr_err("%s: destroy voice failed\n", __func__);
+
+ voc_update_session_params(v);
+
+ voice_destroy_mvm_cvs_session(v);
+ v->voc_state = VOC_RELEASE;
+ if (common.is_vote_bms) {
+ /* vote low power to BMS during call stop */
+ voice_vote_powerstate_to_bms(v, false);
+ }
+ } else {
+ pr_err("%s: Error: End voice called in state %d\n",
+ __func__, v->voc_state);
+
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&v->lock);
+ return ret;
+}
+
+int voc_standby_voice_call(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ struct apr_hdr mvm_standby_voice_cmd;
+ void *apr_mvm;
+ u16 mvm_handle;
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: voc state=%d", __func__, v->voc_state);
+
+ if (v->voc_state == VOC_RUN) {
+ apr_mvm = common.apr_q6_mvm;
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ ret = -EINVAL;
+ goto fail;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+ mvm_standby_voice_cmd.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mvm_standby_voice_cmd.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_standby_voice_cmd) - APR_HDR_SIZE);
+ pr_debug("send mvm_standby_voice_cmd pkt size = %d\n",
+ mvm_standby_voice_cmd.pkt_size);
+ mvm_standby_voice_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_standby_voice_cmd.dest_port = mvm_handle;
+ mvm_standby_voice_cmd.token = 0;
+ mvm_standby_voice_cmd.opcode = VSS_IMVM_CMD_STANDBY_VOICE;
+ v->mvm_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_mvm,
+ (uint32_t *)&mvm_standby_voice_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IMVM_CMD_STANDBY_VOICE\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+ v->voc_state = VOC_STANDBY;
+ }
+fail:
+ return ret;
+}
+
+int voc_disable_device(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: voc state=%d\n", __func__, v->voc_state);
+
+ mutex_lock(&v->lock);
+ if (v->voc_state == VOC_RUN) {
+ ret = voice_pause_voice_call(v);
+ if (ret < 0) {
+ pr_err("%s: Pause Voice Call failed for session 0x%x, err %d!\n",
+ __func__, v->session_id, ret);
+ goto done;
+ }
+ rtac_remove_voice(voice_get_cvs_handle(v));
+ voice_send_cvp_deregister_vol_cal_cmd(v);
+ voice_send_cvp_deregister_cal_cmd(v);
+ voice_send_cvp_deregister_dev_cfg_cmd(v);
+
+ v->voc_state = VOC_CHANGE;
+ } else {
+ pr_debug("%s: called in voc state=%d, No_OP\n",
+ __func__, v->voc_state);
+ }
+
+done:
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_enable_device(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ struct module_instance_info mod_inst_info = {0};
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: voc state=%d\n", __func__, v->voc_state);
+ mutex_lock(&v->lock);
+ if (v->voc_state == VOC_CHANGE) {
+ ret = voice_send_tty_mode_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Sending TTY mode failed, ret=%d\n",
+ __func__, ret);
+ /* Not a critical error, allow voice call to continue */
+ }
+
+ mod_inst_info.module_id = MODULE_ID_VOICE_MODULE_ST;
+ mod_inst_info.instance_id = INSTANCE_ID_0;
+
+ if (v->tty_mode) {
+ /* disable slowtalk */
+ voice_send_set_pp_enable_cmd(v, mod_inst_info, 0);
+ } else {
+ /* restore slowtalk */
+ voice_send_set_pp_enable_cmd(v, mod_inst_info,
+ v->st_enable);
+ }
+
+ ret = voice_send_set_device_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Set device failed, ret=%d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = voice_send_cvp_media_fmt_info_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Set format failed err:%d\n", __func__, ret);
+ goto done;
+ }
+
+ ret = voice_send_cvp_topology_commit_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Set topology commit failed\n", __func__);
+ goto done;
+ }
+
+ voice_send_cvp_register_dev_cfg_cmd(v);
+ voice_send_cvp_register_cal_cmd(v);
+ voice_send_cvp_register_vol_cal_cmd(v);
+
+ rtac_add_voice(voice_get_cvs_handle(v),
+ voice_get_cvp_handle(v),
+ v->dev_rx.port_id, v->dev_tx.port_id,
+ v->dev_rx.dev_id, v->dev_tx.dev_id,
+ v->session_id);
+
+ ret = voice_send_start_voice_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Fail in sending START_VOICE, ret=%d\n",
+ __func__, ret);
+ goto done;
+ }
+ v->voc_state = VOC_RUN;
+ } else {
+ pr_debug("%s: called in voc state=%d, No_OP\n",
+ __func__, v->voc_state);
+ }
+
+done:
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mutex_lock(&v->lock);
+ if (v->lch_mode == lch_mode) {
+ pr_debug("%s: Session %d already in LCH mode %d\n",
+ __func__, session_id, lch_mode);
+
+ mutex_unlock(&v->lock);
+ goto done;
+ }
+
+ v->lch_mode = lch_mode;
+ mutex_unlock(&v->lock);
+
+ ret = voc_lch_ops(v, v->lch_mode);
+ if (ret < 0) {
+ pr_err("%s: lch ops failed %d\n", __func__, ret);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+int voc_resume_voice_call(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ ret = voice_send_start_voice_cmd(v);
+ if (ret < 0) {
+ pr_err("Fail in sending START_VOICE\n");
+ goto fail;
+ }
+ v->voc_state = VOC_RUN;
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+int voc_start_voice_call(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+
+ if (v->voc_state == VOC_ERROR) {
+ pr_debug("%s: VOC in ERR state\n", __func__);
+
+ voice_destroy_mvm_cvs_session(v);
+ v->voc_state = VOC_INIT;
+ }
+
+ if ((v->voc_state == VOC_INIT) ||
+ (v->voc_state == VOC_RELEASE)) {
+ ret = voice_apr_register(session_id);
+ if (ret < 0) {
+ pr_err("%s: apr register failed\n", __func__);
+ goto fail;
+ }
+
+ if (is_cvd_version_queried()) {
+ pr_debug("%s: Returning the cached value %s\n",
+ __func__, common.cvd_version);
+ } else {
+ ret = voice_send_mvm_cvd_version_cmd(v);
+ if (ret < 0)
+ pr_debug("%s: Error retrieving CVD version %d\n",
+ __func__, ret);
+ }
+
+ ret = voice_create_mvm_cvs_session(v);
+ if (ret < 0) {
+ pr_err("create mvm and cvs failed\n");
+ goto fail;
+ }
+
+ if (is_voip_session(session_id)) {
+ /* Allocate oob mem if not already allocated and
+ * memory map the oob memory block.
+ */
+ ret = voice_alloc_and_map_oob_mem(v);
+ if (ret < 0) {
+ pr_err("%s: voice_alloc_and_map_oob_mem() failed, ret:%d\n",
+ __func__, ret);
+
+ goto fail;
+ }
+
+ ret = voice_set_packet_exchange_mode_and_config(
+ session_id,
+ VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND);
+ if (ret) {
+ pr_err("%s: Err: exchange_mode_and_config %d\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ }
+ ret = voice_send_dual_control_cmd(v);
+ if (ret < 0) {
+ pr_err("Err Dual command failed\n");
+ goto fail;
+ }
+ ret = voice_setup_vocproc(v);
+ if (ret < 0) {
+ pr_err("setup voice failed\n");
+ goto fail;
+ }
+
+ ret = voice_send_vol_step_cmd(v);
+ if (ret < 0)
+ pr_err("voice volume failed\n");
+
+ ret = voice_send_stream_mute_cmd(v,
+ VSS_IVOLUME_DIRECTION_TX,
+ v->stream_tx.stream_mute,
+ v->stream_tx.stream_mute_ramp_duration_ms);
+ if (ret < 0)
+ pr_err("voice mute failed\n");
+
+ ret = voice_send_start_voice_cmd(v);
+ if (ret < 0) {
+ pr_err("start voice failed\n");
+ goto fail;
+ }
+
+ v->voc_state = VOC_RUN;
+ } else {
+ pr_err("%s: Error: Start voice called in state %d\n",
+ __func__, v->voc_state);
+
+ ret = -EINVAL;
+ goto fail;
+ }
+fail:
+ mutex_unlock(&v->lock);
+ return ret;
+}
+
+int voc_set_ext_ec_ref_port_id(uint16_t port_id, bool state)
+{
+ int ret = 0;
+
+ mutex_lock(&common.common_lock);
+ if (state == true) {
+ if (port_id == AFE_PORT_INVALID) {
+ pr_err("%s: Invalid port id", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+ common.ec_ref_ext = true;
+ } else {
+ common.ec_ref_ext = false;
+ }
+ /* Cache EC Fromat Info in common */
+ common.ec_media_fmt_info.port_id = port_id;
+exit:
+ mutex_unlock(&common.common_lock);
+ return ret;
+}
+
+int voc_get_ext_ec_ref_port_id(void)
+{
+ if (common.ec_ref_ext)
+ return common.ec_media_fmt_info.port_id;
+ else
+ return AFE_PORT_INVALID;
+}
+
+void voc_register_mvs_cb(ul_cb_fn ul_cb,
+ dl_cb_fn dl_cb,
+ voip_ssr_cb ssr_cb,
+ void *private_data)
+{
+ common.mvs_info.ul_cb = ul_cb;
+ common.mvs_info.dl_cb = dl_cb;
+ common.mvs_info.ssr_cb = ssr_cb;
+ common.mvs_info.private_data = private_data;
+}
+
+void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb,
+ void *private_data)
+{
+ common.dtmf_info.dtmf_rx_ul_cb = dtmf_rx_ul_cb;
+ common.dtmf_info.private_data = private_data;
+}
+
+void voc_config_vocoder(uint32_t media_type,
+ uint32_t rate,
+ uint32_t network_type,
+ uint32_t dtx_mode,
+ uint32_t evrc_min_rate,
+ uint32_t evrc_max_rate)
+{
+ common.mvs_info.media_type = media_type;
+ common.mvs_info.rate = rate;
+ common.mvs_info.network_type = network_type;
+ common.mvs_info.dtx_mode = dtx_mode;
+ common.mvs_info.evrc_min_rate = evrc_min_rate;
+ common.mvs_info.evrc_max_rate = evrc_max_rate;
+}
+
+static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv)
+{
+ uint32_t *ptr = NULL, min_payload_size = 0;
+ struct common_data *c = NULL;
+ struct voice_data *v = NULL;
+ int i = 0;
+ struct vss_iversion_rsp_get_t *version_rsp = NULL;
+
+ if ((data == NULL) || (priv == NULL)) {
+ pr_err("%s: data or priv is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ c = priv;
+
+ pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
+ data->payload_size, data->opcode);
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("%s: Reset event received in Voice service\n",
+ __func__);
+
+ if (common.mvs_info.ssr_cb) {
+ pr_debug("%s: Informing reset event to VoIP\n",
+ __func__);
+ common.mvs_info.ssr_cb(data->opcode,
+ common.mvs_info.private_data);
+ }
+
+ apr_reset(c->apr_q6_mvm);
+ c->apr_q6_mvm = NULL;
+
+ /* clean up memory handle */
+ c->cal_mem_handle = 0;
+ c->rtac_mem_handle = 0;
+ cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES,
+ common.cal_data);
+ rtac_clear_mapping(VOICE_RTAC_CAL);
+
+ /* Sub-system restart is applicable to all sessions. */
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ c->voice[i].mvm_handle = 0;
+ c->voice[i].shmem_info.mem_handle = 0;
+ }
+
+ /* Free the ION memory and clear handles for Source Tracking */
+ if (is_source_tracking_shared_memomry_allocated()) {
+ msm_audio_ion_free(
+ common.source_tracking_sh_mem.sh_mem_block.client,
+ common.source_tracking_sh_mem.sh_mem_block.handle);
+ common.source_tracking_sh_mem.mem_handle = 0;
+ common.source_tracking_sh_mem.sh_mem_block.client =
+ NULL;
+ common.source_tracking_sh_mem.sh_mem_block.handle =
+ NULL;
+ }
+ /* clean up srvcc rec flag */
+ c->srvcc_rec_flag = false;
+ voc_set_error_state(data->reset_proc);
+ return 0;
+ }
+
+ pr_debug("%s: session_idx 0x%x\n", __func__, data->dest_port);
+
+ v = voice_get_session_by_idx(data->dest_port);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ return -EINVAL;
+ }
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ if (data->payload_size >= sizeof(ptr[0]) * 2) {
+ ptr = data->payload;
+
+ pr_debug("%x %x\n", ptr[0], ptr[1]);
+ /* ping mvm service ACK */
+ switch (ptr[0]) {
+ case VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION:
+ case VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION:
+ /* Passive session is used for CS call
+ * Full session is used for VoIP call. */
+ pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+ if (!ptr[1]) {
+ pr_debug("%s: MVM handle is %d\n",
+ __func__, data->src_port);
+ voice_set_mvm_handle(v, data->src_port);
+ } else
+ pr_err("got NACK for sending MVM create session\n");
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ v->async_err = ptr[1];
+ wake_up(&v->mvm_wait);
+ break;
+ case VSS_IMVM_CMD_START_VOICE:
+ case VSS_IMVM_CMD_ATTACH_VOCPROC:
+ case VSS_IMVM_CMD_STOP_VOICE:
+ case VSS_IMVM_CMD_DETACH_VOCPROC:
+ case VSS_ISTREAM_CMD_SET_TTY_MODE:
+ case APRV2_IBASIC_CMD_DESTROY_SESSION:
+ case VSS_IMVM_CMD_ATTACH_STREAM:
+ case VSS_IMVM_CMD_DETACH_STREAM:
+ case VSS_ICOMMON_CMD_SET_NETWORK:
+ case VSS_ICOMMON_CMD_SET_VOICE_TIMING:
+ case VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL:
+ case VSS_IMVM_CMD_SET_CAL_NETWORK:
+ case VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE:
+ case VSS_IMEMORY_CMD_MAP_PHYSICAL:
+ case VSS_IMEMORY_CMD_UNMAP:
+ case VSS_IMVM_CMD_PAUSE_VOICE:
+ case VSS_IMVM_CMD_STANDBY_VOICE:
+ case VSS_IHDVOICE_CMD_ENABLE:
+ case VSS_IHDVOICE_CMD_DISABLE:
+ pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ v->async_err = ptr[1];
+ wake_up(&v->mvm_wait);
+ break;
+ case VSS_IVERSION_CMD_GET:
+ pr_debug("%s: Error retrieving CVD Version, error:%d\n",
+ __func__, ptr[1]);
+
+ strlcpy(common.cvd_version, CVD_VERSION_0_0,
+ sizeof(common.cvd_version));
+ pr_debug("%s: Fall back to default value, CVD Version = %s\n",
+ __func__, common.cvd_version);
+
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ v->async_err = ptr[1];
+ wake_up(&v->mvm_wait);
+ break;
+ default:
+ pr_debug("%s: not match cmd = 0x%x\n",
+ __func__, ptr[0]);
+ break;
+ }
+ }
+ } else if (data->opcode == VSS_IMEMORY_RSP_MAP) {
+ pr_debug("%s, Revd VSS_IMEMORY_RSP_MAP response\n", __func__);
+
+ if (data->payload_size < sizeof(ptr[0])) {
+ pr_err("%s: payload has invalid size[%d]\n", __func__,
+ data->payload_size);
+ return -EINVAL;
+ }
+
+ if (data->payload_size && data->token == VOIP_MEM_MAP_TOKEN) {
+ ptr = data->payload;
+ if (ptr[0]) {
+ v->shmem_info.mem_handle = ptr[0];
+ pr_debug("%s: shared mem_handle: 0x[%x]\n",
+ __func__, v->shmem_info.mem_handle);
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->mvm_wait);
+ }
+ } else if (data->payload_size &&
+ data->token == VOC_CAL_MEM_MAP_TOKEN) {
+ ptr = data->payload;
+ if (ptr[0]) {
+ c->cal_mem_handle = ptr[0];
+
+ pr_debug("%s: cal mem handle 0x%x\n",
+ __func__, c->cal_mem_handle);
+
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->mvm_wait);
+ }
+ } else if (data->payload_size &&
+ data->token == VOC_VOICE_HOST_PCM_MAP_TOKEN) {
+ ptr = data->payload;
+ if (ptr[0]) {
+ common.voice_host_pcm_mem_handle = ptr[0];
+
+ pr_debug("%s: vhpcm mem handle 0x%x\n",
+ __func__,
+ common.voice_host_pcm_mem_handle);
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->mvm_wait);
+ }
+ } else if (data->payload_size &&
+ data->token == VOC_RTAC_MEM_MAP_TOKEN) {
+ ptr = data->payload;
+ if (ptr[0]) {
+ c->rtac_mem_handle = ptr[0];
+
+ pr_debug("%s: cal mem handle 0x%x\n",
+ __func__, c->rtac_mem_handle);
+
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->mvm_wait);
+ }
+ } else if (data->payload_size &&
+ data->token == VOC_SOURCE_TRACKING_MEM_MAP_TOKEN) {
+ ptr = data->payload;
+ if (ptr[0]) {
+ common.source_tracking_sh_mem.mem_handle =
+ ptr[0];
+
+ pr_debug("%s: Source Tracking shared mem handle 0x%x\n",
+ __func__,
+ common.source_tracking_sh_mem.mem_handle);
+
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->mvm_wait);
+ }
+ } else {
+ pr_err("%s: Unknown mem map token %d\n",
+ __func__, data->token);
+ }
+ } else if (data->opcode == VSS_IVERSION_RSP_GET) {
+ pr_debug("%s: Received VSS_IVERSION_RSP_GET\n", __func__);
+
+ if (data->payload_size) {
+ min_payload_size = (data->payload_size >
+ CVD_VERSION_STRING_MAX_SIZE)
+ ? CVD_VERSION_STRING_MAX_SIZE :
+ data->payload_size;
+ version_rsp =
+ (struct vss_iversion_rsp_get_t *)data->payload;
+ memcpy(common.cvd_version, version_rsp->version,
+ min_payload_size);
+ common.cvd_version[min_payload_size - 1] = '\0';
+ pr_debug("%s: CVD Version = %s\n",
+ __func__, common.cvd_version);
+
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->mvm_wait);
+ }
+ }
+ return 0;
+}
+
+static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv)
+{
+ uint32_t *ptr = NULL;
+ struct common_data *c = NULL;
+ struct voice_data *v = NULL;
+ int i = 0;
+
+ if ((data == NULL) || (priv == NULL)) {
+ pr_err("%s: data or priv is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ c = priv;
+
+ pr_debug("%s: session_id 0x%x\n", __func__, data->dest_port);
+ pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
+ data->payload_size, data->opcode);
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("%s: Reset event received in Voice service\n",
+ __func__);
+
+ apr_reset(c->apr_q6_cvs);
+ c->apr_q6_cvs = NULL;
+
+ /* Sub-system restart is applicable to all sessions. */
+ for (i = 0; i < MAX_VOC_SESSIONS; i++)
+ c->voice[i].cvs_handle = 0;
+
+ cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES,
+ common.cal_data);
+
+ /* Free the ION memory and clear handles for Source Tracking */
+ if (is_source_tracking_shared_memomry_allocated()) {
+ msm_audio_ion_free(
+ common.source_tracking_sh_mem.sh_mem_block.client,
+ common.source_tracking_sh_mem.sh_mem_block.handle);
+ common.source_tracking_sh_mem.mem_handle = 0;
+ common.source_tracking_sh_mem.sh_mem_block.client =
+ NULL;
+ common.source_tracking_sh_mem.sh_mem_block.handle =
+ NULL;
+ }
+ voc_set_error_state(data->reset_proc);
+ return 0;
+ }
+
+ v = voice_get_session_by_idx(data->dest_port);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ return -EINVAL;
+ }
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ if (data->payload_size) {
+ ptr = data->payload;
+
+ pr_debug("%x %x\n", ptr[0], ptr[1]);
+ if (ptr[1] != 0) {
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, ptr[0], ptr[1]);
+ }
+ /*response from CVS */
+ switch (ptr[0]) {
+ case VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION:
+ case VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION:
+ if (!ptr[1]) {
+ pr_debug("%s: CVS handle is %d\n",
+ __func__, data->src_port);
+ voice_set_cvs_handle(v, data->src_port);
+ } else
+ pr_err("got NACK for sending CVS create session\n");
+ v->cvs_state = CMD_STATUS_SUCCESS;
+ v->async_err = ptr[1];
+ wake_up(&v->cvs_wait);
+ break;
+ case VSS_IVOLUME_CMD_MUTE_V2:
+ case VSS_ISTREAM_CMD_SET_MEDIA_TYPE:
+ case VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE:
+ case VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE:
+ case VSS_ISTREAM_CMD_SET_ENC_DTX_MODE:
+ case VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE:
+ case APRV2_IBASIC_CMD_DESTROY_SESSION:
+ case VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2:
+ case VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA:
+ case VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA:
+ case VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA:
+ case VSS_ICOMMON_CMD_MAP_MEMORY:
+ case VSS_ICOMMON_CMD_UNMAP_MEMORY:
+ case VSS_ICOMMON_CMD_SET_UI_PROPERTY:
+ case VSS_ICOMMON_CMD_SET_UI_PROPERTY_V2:
+ case VSS_IPLAYBACK_CMD_START:
+ case VSS_IPLAYBACK_CMD_STOP:
+ case VSS_IRECORD_CMD_START:
+ case VSS_IRECORD_CMD_STOP:
+ case VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE:
+ case VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG:
+ case VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION:
+ pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+ v->cvs_state = CMD_STATUS_SUCCESS;
+ v->async_err = ptr[1];
+ wake_up(&v->cvs_wait);
+ break;
+ case VSS_ICOMMON_CMD_SET_PARAM_V2:
+ case VSS_ICOMMON_CMD_SET_PARAM_V3:
+ pr_debug("%s: VSS_ICOMMON_CMD_SET_PARAM\n",
+ __func__);
+ rtac_make_voice_callback(RTAC_CVS, ptr,
+ data->payload_size);
+ break;
+ case VSS_ICOMMON_CMD_GET_PARAM_V2:
+ case VSS_ICOMMON_CMD_GET_PARAM_V3:
+ pr_debug("%s: VSS_ICOMMON_CMD_GET_PARAM_V2\n",
+ __func__);
+ /* Should only come here if there is an APR */
+ /* error or malformed APR packet. Otherwise */
+ /* response will be returned as */
+ /* VSS_ICOMMON_RSP_GET_PARAM */
+ if (ptr[1] != 0) {
+ pr_err("%s: CVP get param error = %d, resuming\n",
+ __func__, ptr[1]);
+ rtac_make_voice_callback(RTAC_CVP,
+ data->payload,
+ data->payload_size);
+ }
+ break;
+ default:
+ pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+ break;
+ }
+ }
+ } else if (data->opcode ==
+ VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_READY) {
+ int ret = 0;
+ u16 cvs_handle;
+ uint32_t *cvs_voc_pkt;
+ struct cvs_enc_buffer_consumed_cmd send_enc_buf_consumed_cmd;
+ void *apr_cvs;
+
+ pr_debug("Encoder buffer is ready\n");
+
+ apr_cvs = common.apr_q6_cvs;
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL\n", __func__);
+ return -EINVAL;
+ }
+ cvs_handle = voice_get_cvs_handle(v);
+
+ send_enc_buf_consumed_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ send_enc_buf_consumed_cmd.hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(send_enc_buf_consumed_cmd) - APR_HDR_SIZE);
+
+ send_enc_buf_consumed_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ send_enc_buf_consumed_cmd.hdr.dest_port = cvs_handle;
+ send_enc_buf_consumed_cmd.hdr.token = 0;
+ send_enc_buf_consumed_cmd.hdr.opcode =
+ VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_CONSUMED;
+
+ cvs_voc_pkt = v->shmem_info.sh_buf.buf[1].data;
+ if (cvs_voc_pkt != NULL && common.mvs_info.ul_cb != NULL) {
+ if (v->shmem_info.sh_buf.buf[1].size <
+ ((3 * sizeof(uint32_t)) + cvs_voc_pkt[2])) {
+ pr_err("%s: invalid voc pkt size\n", __func__);
+ return -EINVAL;
+ }
+ /* cvs_voc_pkt[0] contains tx timestamp */
+ common.mvs_info.ul_cb((uint8_t *)&cvs_voc_pkt[3],
+ cvs_voc_pkt[2],
+ cvs_voc_pkt[0],
+ common.mvs_info.private_data);
+ } else
+ pr_err("%s: cvs_voc_pkt or ul_cb is NULL\n", __func__);
+
+ ret = apr_send_pkt(apr_cvs,
+ (uint32_t *) &send_enc_buf_consumed_cmd);
+ if (ret < 0) {
+ pr_err("%s: Err send ENC_BUF_CONSUMED_NOTIFY %d\n",
+ __func__, ret);
+ goto fail;
+ }
+ } else if (data->opcode == VSS_ISTREAM_EVT_SEND_ENC_BUFFER) {
+ pr_debug("Recd VSS_ISTREAM_EVT_SEND_ENC_BUFFER\n");
+ } else if (data->opcode ==
+ VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_REQUEST) {
+ int ret = 0;
+ u16 cvs_handle;
+ uint32_t *cvs_voc_pkt;
+ struct cvs_dec_buffer_ready_cmd send_dec_buf;
+ void *apr_cvs;
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL\n", __func__);
+ return -EINVAL;
+ }
+ cvs_handle = voice_get_cvs_handle(v);
+
+ send_dec_buf.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+
+ send_dec_buf.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(send_dec_buf) - APR_HDR_SIZE);
+
+ send_dec_buf.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ send_dec_buf.hdr.dest_port = cvs_handle;
+ send_dec_buf.hdr.token = 0;
+ send_dec_buf.hdr.opcode =
+ VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_READY;
+
+ cvs_voc_pkt = (uint32_t *)(v->shmem_info.sh_buf.buf[0].data);
+ if (cvs_voc_pkt != NULL && common.mvs_info.dl_cb != NULL) {
+ /* Set timestamp to 0 and advance the pointer */
+ cvs_voc_pkt[0] = 0;
+ /* Set media_type and advance the pointer */
+ cvs_voc_pkt[1] = common.mvs_info.media_type;
+ common.mvs_info.dl_cb(
+ (uint8_t *)&cvs_voc_pkt[2],
+ common.mvs_info.private_data);
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &send_dec_buf);
+ if (ret < 0) {
+ pr_err("%s: Err send DEC_BUF_READY_NOTIFI %d\n",
+ __func__, ret);
+ goto fail;
+ }
+ } else {
+ pr_debug("%s: voc_pkt or dl_cb is NULL\n", __func__);
+ goto fail;
+ }
+ } else if (data->opcode == VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER) {
+ pr_debug("Recd VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER\n");
+ } else if (data->opcode == VSS_ISTREAM_EVT_SEND_DEC_BUFFER) {
+ pr_debug("Send dec buf resp\n");
+ } else if (data->opcode == APR_RSP_ACCEPTED) {
+ ptr = data->payload;
+ if (ptr[0])
+ pr_debug("%s: APR_RSP_ACCEPTED for 0x%x:\n",
+ __func__, ptr[0]);
+ } else if (data->opcode == VSS_ISTREAM_EVT_NOT_READY) {
+ pr_debug("Recd VSS_ISTREAM_EVT_NOT_READY\n");
+ } else if (data->opcode == VSS_ISTREAM_EVT_READY) {
+ pr_debug("Recd VSS_ISTREAM_EVT_READY\n");
+ } else if ((data->opcode == VSS_ICOMMON_RSP_GET_PARAM) ||
+ (data->opcode == VSS_ICOMMON_RSP_GET_PARAM_V3)) {
+ pr_debug("%s: VSS_ICOMMON_RSP_GET_PARAM\n", __func__);
+ ptr = data->payload;
+ if (ptr[0] != 0) {
+ pr_err("%s: VSS_ICOMMON_RSP_GET_PARAM returned error = 0x%x\n",
+ __func__, ptr[0]);
+ }
+ rtac_make_voice_callback(RTAC_CVS, data->payload,
+ data->payload_size);
+ } else if (data->opcode == VSS_ISTREAM_EVT_RX_DTMF_DETECTED) {
+ struct vss_istream_evt_rx_dtmf_detected *dtmf_rx_detected;
+ uint32_t *voc_pkt = data->payload;
+ uint32_t pkt_len = data->payload_size;
+
+ if ((voc_pkt != NULL) &&
+ (pkt_len ==
+ sizeof(struct vss_istream_evt_rx_dtmf_detected))) {
+
+ dtmf_rx_detected =
+ (struct vss_istream_evt_rx_dtmf_detected *) voc_pkt;
+ pr_debug("RX_DTMF_DETECTED low_freq=%d high_freq=%d\n",
+ dtmf_rx_detected->low_freq,
+ dtmf_rx_detected->high_freq);
+ if (c->dtmf_info.dtmf_rx_ul_cb)
+ c->dtmf_info.dtmf_rx_ul_cb((uint8_t *)voc_pkt,
+ voc_get_session_name(v->session_id),
+ c->dtmf_info.private_data);
+ } else {
+ pr_err("Invalid packet\n");
+ }
+ } else
+ pr_debug("Unknown opcode 0x%x\n", data->opcode);
+
+fail:
+ return 0;
+}
+
+static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv)
+{
+ uint32_t *ptr = NULL;
+ struct common_data *c = NULL;
+ struct voice_data *v = NULL;
+ int i = 0;
+
+ if ((data == NULL) || (priv == NULL)) {
+ pr_err("%s: data or priv is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ c = priv;
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("%s: Reset event received in Voice service\n",
+ __func__);
+
+ apr_reset(c->apr_q6_cvp);
+ c->apr_q6_cvp = NULL;
+ cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES,
+ common.cal_data);
+
+ /* Sub-system restart is applicable to all sessions. */
+ for (i = 0; i < MAX_VOC_SESSIONS; i++)
+ c->voice[i].cvp_handle = 0;
+
+ /*
+ * Free the ION memory and clear handles for
+ * Source Tracking
+ */
+ if (is_source_tracking_shared_memomry_allocated()) {
+ msm_audio_ion_free(
+ common.source_tracking_sh_mem.sh_mem_block.client,
+ common.source_tracking_sh_mem.sh_mem_block.handle);
+ common.source_tracking_sh_mem.mem_handle = 0;
+ common.source_tracking_sh_mem.sh_mem_block.client =
+ NULL;
+ common.source_tracking_sh_mem.sh_mem_block.handle =
+ NULL;
+ }
+ voc_set_error_state(data->reset_proc);
+ return 0;
+ }
+
+ v = voice_get_session_by_idx(data->dest_port);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ return -EINVAL;
+ }
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ if (data->payload_size >= (2 * sizeof(uint32_t))) {
+ ptr = data->payload;
+
+ pr_debug("%x %x\n", ptr[0], ptr[1]);
+ if (ptr[1] != 0) {
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, ptr[0], ptr[1]);
+ }
+ switch (ptr[0]) {
+ case VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2:
+ case VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3:
+ /*response from CVP */
+ pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+ if (!ptr[1]) {
+ voice_set_cvp_handle(v, data->src_port);
+ pr_debug("status: %d, cvphdl=%d\n",
+ ptr[1], data->src_port);
+ } else
+ pr_err("got NACK from CVP create session response\n");
+ v->cvp_state = CMD_STATUS_SUCCESS;
+ v->async_err = ptr[1];
+ wake_up(&v->cvp_wait);
+ break;
+ case VSS_IVOCPROC_CMD_SET_DEVICE_V2:
+ case VSS_IVOCPROC_CMD_SET_DEVICE_V3:
+ case VSS_IVOLUME_CMD_SET_STEP:
+ case VSS_IVOCPROC_CMD_ENABLE:
+ case VSS_IVOCPROC_CMD_DISABLE:
+ case APRV2_IBASIC_CMD_DESTROY_SESSION:
+ case VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA:
+ case VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA:
+ case VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2:
+ case VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA:
+ case VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA:
+ case VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA:
+ case VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA:
+ case VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA:
+ case VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG:
+ case VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG:
+ case VSS_ICOMMON_CMD_MAP_MEMORY:
+ case VSS_ICOMMON_CMD_UNMAP_MEMORY:
+ case VSS_IVOLUME_CMD_MUTE_V2:
+ case VSS_IVPCM_CMD_START_V2:
+ case VSS_IVPCM_CMD_STOP:
+ case VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS:
+ case VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT:
+ v->cvp_state = CMD_STATUS_SUCCESS;
+ v->async_err = ptr[1];
+ wake_up(&v->cvp_wait);
+ break;
+ case VSS_IVPCM_EVT_PUSH_BUFFER_V2:
+ break;
+ case VSS_ICOMMON_CMD_SET_PARAM_V2:
+ case VSS_ICOMMON_CMD_SET_PARAM_V3:
+ switch (data->token) {
+ case VOC_SET_MEDIA_FORMAT_PARAM_TOKEN:
+ pr_debug("%s: VSS_ICOMMON_CMD_SET_PARAM called by voice_send_cvp_media_format_cmd\n",
+ __func__);
+ v->cvp_state = CMD_STATUS_SUCCESS;
+ v->async_err = ptr[1];
+ wake_up(&v->cvp_wait);
+ break;
+ case VOC_RTAC_SET_PARAM_TOKEN:
+ pr_debug("%s: VSS_ICOMMON_CMD_SET_PARAM called by rtac\n",
+ __func__);
+ rtac_make_voice_callback(
+ RTAC_CVP, ptr,
+ data->payload_size);
+ break;
+ default:
+ pr_debug("%s: invalid token for command VSS_ICOMMON_CMD_SET_PARAM: %d\n",
+ __func__, data->token);
+ break;
+ }
+ break;
+ case VSS_ICOMMON_CMD_GET_PARAM_V2:
+ case VSS_ICOMMON_CMD_GET_PARAM_V3:
+ pr_debug("%s: VSS_ICOMMON_CMD_GET_PARAM_V2\n",
+ __func__);
+ /* Should only come here if there is an APR */
+ /* error or malformed APR packet. Otherwise */
+ /* response will be returned as */
+ /* VSS_ICOMMON_RSP_GET_PARAM */
+ if (ptr[1] != 0) {
+ pr_err("%s: CVP get param error = %d, resuming\n",
+ __func__, ptr[1]);
+ rtac_make_voice_callback(RTAC_CVP,
+ data->payload,
+ data->payload_size);
+ }
+ break;
+ case VSS_ISOUNDFOCUS_CMD_SET_SECTORS:
+ if (!ptr[1])
+ common.is_sound_focus_resp_success =
+ true;
+ else
+ common.is_sound_focus_resp_success =
+ false;
+ v->cvp_state = CMD_STATUS_SUCCESS;
+ v->async_err = ptr[1];
+ wake_up(&v->cvp_wait);
+ break;
+ case VSS_ISOUNDFOCUS_CMD_GET_SECTORS:
+ /*
+ * Should only come here if there is an error
+ * response received from ADSP. Otherwise
+ * response will be returned as
+ * VSS_ISOUNDFOCUS_RSP_GET_SECTORS
+ */
+ pr_err("%s: VSS_ISOUNDFOCUS_CMD_GET_SECTORS failed\n",
+ __func__);
+
+ common.is_sound_focus_resp_success = false;
+ v->cvp_state = CMD_STATUS_SUCCESS;
+ v->async_err = ptr[1];
+ wake_up(&v->cvp_wait);
+ break;
+ case VSS_ISOURCETRACK_CMD_GET_ACTIVITY:
+ if (!ptr[1]) {
+ /* Read data from shared memory */
+ memcpy(&common.sourceTrackingResponse,
+ common.source_tracking_sh_mem.
+ sh_mem_block.data,
+ sizeof(struct
+ vss_isourcetrack_activity_data_t));
+ common.is_source_tracking_resp_success =
+ true;
+ } else {
+ common.is_source_tracking_resp_success =
+ false;
+ pr_err("%s: Error received for source tracking params\n",
+ __func__);
+ }
+ v->cvp_state = CMD_STATUS_SUCCESS;
+ v->async_err = ptr[1];
+ wake_up(&v->cvp_wait);
+ break;
+ default:
+ pr_debug("%s: not match cmd = 0x%x\n",
+ __func__, ptr[0]);
+ break;
+ }
+ }
+ } else if ((data->opcode == VSS_ICOMMON_RSP_GET_PARAM) ||
+ (data->opcode == VSS_ICOMMON_RSP_GET_PARAM_V3)) {
+ pr_debug("%s: VSS_ICOMMON_RSP_GET_PARAM\n", __func__);
+ ptr = data->payload;
+ if (ptr[0] != 0) {
+ pr_err("%s: VSS_ICOMMON_RSP_GET_PARAM returned error = 0x%x\n",
+ __func__, ptr[0]);
+ }
+ rtac_make_voice_callback(RTAC_CVP, data->payload,
+ data->payload_size);
+ } else if (data->opcode == VSS_IVPCM_EVT_NOTIFY_V2) {
+ if ((data->payload != NULL) && data->payload_size ==
+ sizeof(struct vss_ivpcm_evt_notify_v2_t) &&
+ common.hostpcm_info.hostpcm_evt_cb != NULL) {
+ common.hostpcm_info.hostpcm_evt_cb(data->payload,
+ voc_get_session_name(v->session_id),
+ common.hostpcm_info.private_data);
+ }
+ } else if (data->opcode == VSS_ISOUNDFOCUS_RSP_GET_SECTORS) {
+ if (data->payload && (data->payload_size ==
+ sizeof(struct vss_isoundfocus_rsp_get_sectors_t))) {
+ common.is_sound_focus_resp_success = true;
+ memcpy(&common.soundFocusResponse,
+ (struct vss_isoundfocus_rsp_get_sectors_t *)
+ data->payload,
+ sizeof(struct
+ vss_isoundfocus_rsp_get_sectors_t));
+ } else {
+ common.is_sound_focus_resp_success = false;
+ pr_debug("%s: Invalid payload received from CVD\n",
+ __func__);
+ }
+ v->cvp_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->cvp_wait);
+ }
+ return 0;
+}
+
+static int voice_free_oob_shared_mem(void)
+{
+ int rc = 0;
+ int cnt = 0;
+ int bufcnt = NUM_OF_BUFFERS;
+ struct voice_data *v = voice_get_session(
+ common.voice[VOC_PATH_FULL].session_id);
+
+ mutex_lock(&common.common_lock);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rc = msm_audio_ion_free(v->shmem_info.sh_buf.client,
+ v->shmem_info.sh_buf.handle);
+ v->shmem_info.sh_buf.client = NULL;
+ v->shmem_info.sh_buf.handle = NULL;
+ if (rc < 0) {
+ pr_err("%s: Error:%d freeing memory\n", __func__, rc);
+
+ goto done;
+ }
+
+
+ while (cnt < bufcnt) {
+ v->shmem_info.sh_buf.buf[cnt].data = NULL;
+ v->shmem_info.sh_buf.buf[cnt].phys = 0;
+ cnt++;
+ }
+
+ v->shmem_info.sh_buf.client = NULL;
+ v->shmem_info.sh_buf.handle = NULL;
+
+done:
+ mutex_unlock(&common.common_lock);
+ return rc;
+}
+
+static int voice_alloc_oob_shared_mem(void)
+{
+ int cnt = 0;
+ int rc = 0;
+ size_t len;
+ void *mem_addr;
+ dma_addr_t phys;
+ int bufsz = BUFFER_BLOCK_SIZE;
+ int bufcnt = NUM_OF_BUFFERS;
+ struct voice_data *v = voice_get_session(
+ common.voice[VOC_PATH_FULL].session_id);
+
+ mutex_lock(&common.common_lock);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rc = msm_audio_ion_alloc("voip_client", &(v->shmem_info.sh_buf.client),
+ &(v->shmem_info.sh_buf.handle),
+ bufsz*bufcnt,
+ &phys, &len,
+ &mem_addr);
+ if (rc < 0) {
+ pr_err("%s: audio ION alloc failed, rc = %d\n",
+ __func__, rc);
+
+ goto done;
+ }
+
+ while (cnt < bufcnt) {
+ v->shmem_info.sh_buf.buf[cnt].data = mem_addr + (cnt * bufsz);
+ v->shmem_info.sh_buf.buf[cnt].phys = phys + (cnt * bufsz);
+ v->shmem_info.sh_buf.buf[cnt].size = bufsz;
+ cnt++;
+ }
+
+ pr_debug("%s buf[0].data:[%pK], buf[0].phys:[%pK], &buf[0].phys:[%pK],\n",
+ __func__,
+ (void *)v->shmem_info.sh_buf.buf[0].data,
+ &v->shmem_info.sh_buf.buf[0].phys,
+ (void *)&v->shmem_info.sh_buf.buf[0].phys);
+ pr_debug("%s: buf[1].data:[%pK], buf[1].phys[%pK], &buf[1].phys[%pK]\n",
+ __func__,
+ (void *)v->shmem_info.sh_buf.buf[1].data,
+ &v->shmem_info.sh_buf.buf[1].phys,
+ (void *)&v->shmem_info.sh_buf.buf[1].phys);
+
+ memset((void *)v->shmem_info.sh_buf.buf[0].data, 0, (bufsz * bufcnt));
+
+done:
+ mutex_unlock(&common.common_lock);
+ return rc;
+}
+
+static int voice_alloc_oob_mem_table(void)
+{
+ int rc = 0;
+ size_t len;
+ struct voice_data *v = voice_get_session(
+ common.voice[VOC_PATH_FULL].session_id);
+
+ mutex_lock(&common.common_lock);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rc = msm_audio_ion_alloc("voip_client", &(v->shmem_info.memtbl.client),
+ &(v->shmem_info.memtbl.handle),
+ sizeof(struct vss_imemory_table_t),
+ &v->shmem_info.memtbl.phys,
+ &len,
+ &(v->shmem_info.memtbl.data));
+ if (rc < 0) {
+ pr_err("%s: audio ION alloc failed, rc = %d\n",
+ __func__, rc);
+
+ goto done;
+ }
+
+ v->shmem_info.memtbl.size = sizeof(struct vss_imemory_table_t);
+ pr_debug("%s data[%pK]phys[%pK][%pK]\n", __func__,
+ (void *)v->shmem_info.memtbl.data,
+ &v->shmem_info.memtbl.phys,
+ (void *)&v->shmem_info.memtbl.phys);
+
+done:
+ mutex_unlock(&common.common_lock);
+ return rc;
+}
+
+int voc_send_cvp_start_vocpcm(uint32_t session_id,
+ struct vss_ivpcm_tap_point *vpcm_tp,
+ uint32_t no_of_tp)
+{
+ struct cvp_start_cmd cvp_start_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ u16 cvp_handle;
+ struct voice_data *v = voice_get_session(session_id);
+ int i = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ apr_cvp = common.apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* Fill the header */
+ cvp_start_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_start_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(struct vss_ivpcm_tap_point) * no_of_tp) +
+ sizeof(cvp_start_cmd.vpcm_start_cmd.num_tap_points) +
+ sizeof(cvp_start_cmd.vpcm_start_cmd.mem_handle);
+ cvp_start_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id);
+ cvp_start_cmd.hdr.dest_port = cvp_handle;
+ cvp_start_cmd.hdr.token = 0;
+ cvp_start_cmd.hdr.opcode = VSS_IVPCM_CMD_START_V2;
+
+ for (i = 0; i < no_of_tp; i++) {
+ cvp_start_cmd.vpcm_start_cmd.tap_points[i].tap_point =
+ vpcm_tp[i].tap_point;
+ cvp_start_cmd.vpcm_start_cmd.tap_points[i].direction =
+ vpcm_tp[i].direction;
+ cvp_start_cmd.vpcm_start_cmd.tap_points[i].sampling_rate =
+ vpcm_tp[i].sampling_rate;
+ cvp_start_cmd.vpcm_start_cmd.tap_points[i].duration = 0;
+ }
+
+ cvp_start_cmd.vpcm_start_cmd.mem_handle =
+ common.voice_host_pcm_mem_handle;
+ cvp_start_cmd.vpcm_start_cmd.num_tap_points = no_of_tp;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_start_cmd);
+ if (ret < 0) {
+ pr_err("%s: Fail: sending vocpcm map memory,\n", __func__);
+ goto done;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto done;
+ }
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+int voc_send_cvp_stop_vocpcm(uint32_t session_id)
+{
+ struct cvp_command vpcm_stop_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ u16 cvp_handle;
+ struct voice_data *v = voice_get_session(session_id);
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ apr_cvp = common.apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* fill in the header */
+ vpcm_stop_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ vpcm_stop_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(vpcm_stop_cmd) - APR_HDR_SIZE);
+ vpcm_stop_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id);
+ vpcm_stop_cmd.hdr.dest_port = cvp_handle;
+ vpcm_stop_cmd.hdr.token = 0;
+ vpcm_stop_cmd.hdr.opcode = VSS_IVPCM_CMD_STOP;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &vpcm_stop_cmd);
+ if (ret < 0) {
+ pr_err("Fail: sending vocpcm stop,\n");
+ goto done;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto done;
+ }
+
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+int voc_send_cvp_map_vocpcm_memory(uint32_t session_id,
+ struct mem_map_table *tp_mem_table,
+ phys_addr_t paddr, uint32_t bufsize)
+{
+ return voice_map_memory_physical_cmd(voice_get_session(session_id),
+ tp_mem_table,
+ (dma_addr_t) paddr, bufsize,
+ VOC_VOICE_HOST_PCM_MAP_TOKEN);
+}
+
+int voc_send_cvp_unmap_vocpcm_memory(uint32_t session_id)
+{
+ int ret = 0;
+
+ ret = voice_send_mvm_unmap_memory_physical_cmd(
+ voice_get_session(session_id),
+ common.voice_host_pcm_mem_handle);
+
+ if (ret == 0)
+ common.voice_host_pcm_mem_handle = 0;
+
+ return ret;
+}
+
+int voc_send_cvp_vocpcm_push_buf_evt(uint32_t session_id,
+ struct vss_ivpcm_evt_push_buffer_v2_t *push_buff_evt)
+{
+ struct cvp_push_buf_cmd vpcm_push_buf_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ u16 cvp_handle;
+ struct voice_data *v = voice_get_session(session_id);
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ apr_cvp = common.apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memset(&vpcm_push_buf_cmd, 0, sizeof(vpcm_push_buf_cmd));
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* fill in the header */
+ vpcm_push_buf_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ vpcm_push_buf_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(vpcm_push_buf_cmd) - APR_HDR_SIZE);
+ vpcm_push_buf_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ vpcm_push_buf_cmd.hdr.dest_port = cvp_handle;
+ vpcm_push_buf_cmd.hdr.token = 0;
+ vpcm_push_buf_cmd.hdr.opcode = VSS_IVPCM_EVT_PUSH_BUFFER_V2;
+
+ vpcm_push_buf_cmd.vpcm_evt_push_buffer.tap_point =
+ push_buff_evt->tap_point;
+ vpcm_push_buf_cmd.vpcm_evt_push_buffer.push_buf_mask =
+ push_buff_evt->push_buf_mask;
+ vpcm_push_buf_cmd.vpcm_evt_push_buffer.out_buf_mem_address =
+ push_buff_evt->out_buf_mem_address;
+ vpcm_push_buf_cmd.vpcm_evt_push_buffer.in_buf_mem_address =
+ push_buff_evt->in_buf_mem_address;
+ vpcm_push_buf_cmd.vpcm_evt_push_buffer.out_buf_mem_size =
+ push_buff_evt->out_buf_mem_size;
+ vpcm_push_buf_cmd.vpcm_evt_push_buffer.in_buf_mem_size =
+ push_buff_evt->in_buf_mem_size;
+ vpcm_push_buf_cmd.vpcm_evt_push_buffer.sampling_rate =
+ push_buff_evt->sampling_rate;
+ vpcm_push_buf_cmd.vpcm_evt_push_buffer.num_in_channels =
+ push_buff_evt->num_in_channels;
+
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &vpcm_push_buf_cmd);
+ if (ret < 0) {
+ pr_err("Fail: sending vocpcm map memory,\n");
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+void voc_register_hpcm_evt_cb(hostpcm_cb_fn hostpcm_cb,
+ void *private_data)
+{
+ common.hostpcm_info.hostpcm_evt_cb = hostpcm_cb;
+ common.hostpcm_info.private_data = private_data;
+}
+
+void voc_deregister_hpcm_evt_cb(void)
+{
+ common.hostpcm_info.hostpcm_evt_cb = NULL;
+ common.hostpcm_info.private_data = NULL;
+}
+
+int voc_get_cvd_version(char *cvd_version)
+{
+ int ret = 0;
+ struct voice_data *v = voice_get_session(VOICE_SESSION_VSID);
+
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n",
+ __func__, VOICE_SESSION_VSID);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (is_cvd_version_queried()) {
+ pr_debug("%s: Returning the cached value %s\n",
+ __func__, common.cvd_version);
+
+ goto done;
+ }
+
+ /* Register callback to APR */
+ ret = voice_apr_register(VOICE_SESSION_VSID);
+ if (ret < 0) {
+ pr_err("%s: apr register failed\n", __func__);
+ goto done;
+ }
+
+ mutex_lock(&common.common_lock);
+ mutex_lock(&v->lock);
+ ret = voice_send_mvm_cvd_version_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: voice_send_mvm_cvd_version_cmd failed\n", __func__);
+ goto unlock;
+ }
+ ret = 0;
+
+unlock:
+ mutex_unlock(&v->lock);
+ mutex_unlock(&common.common_lock);
+
+done:
+ if (cvd_version)
+ memcpy(cvd_version, common.cvd_version,
+ CVD_VERSION_STRING_MAX_SIZE);
+
+ return ret;
+}
+
+static int voice_alloc_cal_mem_map_table(void)
+{
+ int ret = 0;
+ size_t len;
+
+ ret = msm_audio_ion_alloc("voc_cal",
+ &(common.cal_mem_map_table.client),
+ &(common.cal_mem_map_table.handle),
+ sizeof(struct vss_imemory_table_t),
+ &common.cal_mem_map_table.phys,
+ &len,
+ &(common.cal_mem_map_table.data));
+ if ((ret < 0) && (ret != -EPROBE_DEFER)) {
+ pr_err("%s: audio ION alloc failed, rc = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ common.cal_mem_map_table.size = sizeof(struct vss_imemory_table_t);
+ pr_debug("%s: data %pK phys %pK\n", __func__,
+ common.cal_mem_map_table.data,
+ &common.cal_mem_map_table.phys);
+
+done:
+ return ret;
+}
+
+static int voice_alloc_rtac_mem_map_table(void)
+{
+ int ret = 0;
+ size_t len;
+
+ ret = msm_audio_ion_alloc("voc_rtac_cal",
+ &(common.rtac_mem_map_table.client),
+ &(common.rtac_mem_map_table.handle),
+ sizeof(struct vss_imemory_table_t),
+ &common.rtac_mem_map_table.phys,
+ &len,
+ &(common.rtac_mem_map_table.data));
+ if (ret < 0) {
+ pr_err("%s: audio ION alloc failed, rc = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ common.rtac_mem_map_table.size = sizeof(struct vss_imemory_table_t);
+ pr_debug("%s: data %pK phys %pK\n", __func__,
+ common.rtac_mem_map_table.data,
+ &common.rtac_mem_map_table.phys);
+
+done:
+ return ret;
+}
+
+static int voice_alloc_and_map_oob_mem(struct voice_data *v)
+{
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ return -EINVAL;
+ }
+
+ if (!is_voip_memory_allocated()) {
+ ret = voc_alloc_voip_shared_memory();
+ if (ret < 0) {
+ pr_err("%s: Failed to create voip oob memory %d\n",
+ __func__, ret);
+
+ goto done;
+ }
+ }
+
+ ret = voice_map_memory_physical_cmd(v,
+ &v->shmem_info.memtbl,
+ v->shmem_info.sh_buf.buf[0].phys,
+ v->shmem_info.sh_buf.buf[0].size * NUM_OF_BUFFERS,
+ VOIP_MEM_MAP_TOKEN);
+ if (ret) {
+ pr_err("%s: mvm_map_memory_phy failed %d\n",
+ __func__, ret);
+
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+uint32_t voice_get_topology(uint32_t topology_idx)
+{
+ uint32_t topology = VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT;
+ struct cal_block_data *cal_block = NULL;
+
+ /* initialize as defualt topology */
+ if (topology_idx == CVP_VOC_RX_TOPOLOGY_CAL) {
+ topology = VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT;
+ } else if (topology_idx == CVP_VOC_TX_TOPOLOGY_CAL) {
+ topology = VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS;
+ } else {
+ pr_err("%s: cal index %x is invalid!\n",
+ __func__, topology_idx);
+
+ goto done;
+ }
+
+ if (common.cal_data[topology_idx] == NULL) {
+ pr_err("%s: cal type is NULL for cal index %x\n",
+ __func__, topology_idx);
+
+ goto done;
+ }
+
+ mutex_lock(&common.cal_data[topology_idx]->lock);
+ cal_block = cal_utils_get_only_cal_block(
+ common.cal_data[topology_idx]);
+ if (cal_block == NULL) {
+ pr_debug("%s: cal_block not found for cal index %x\n",
+ __func__, topology_idx);
+
+ goto unlock;
+ }
+
+ topology = ((struct audio_cal_info_voc_top *)
+ cal_block->cal_info)->topology;
+unlock:
+ mutex_unlock(&common.cal_data[topology_idx]->lock);
+done:
+ pr_debug("%s: Using topology %d\n", __func__, topology);
+
+ return topology;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+ int ret = -EINVAL;
+
+ switch (cal_type) {
+ case CVP_VOC_RX_TOPOLOGY_CAL_TYPE:
+ ret = CVP_VOC_RX_TOPOLOGY_CAL;
+ break;
+ case CVP_VOC_TX_TOPOLOGY_CAL_TYPE:
+ ret = CVP_VOC_TX_TOPOLOGY_CAL;
+ break;
+ case CVP_VOCPROC_STATIC_CAL_TYPE:
+ ret = CVP_VOCPROC_CAL;
+ break;
+ case CVP_VOCPROC_DYNAMIC_CAL_TYPE:
+ ret = CVP_VOCVOL_CAL;
+ break;
+ case CVS_VOCSTRM_STATIC_CAL_TYPE:
+ ret = CVS_VOCSTRM_CAL;
+ break;
+ case CVP_VOCDEV_CFG_CAL_TYPE:
+ ret = CVP_VOCDEV_CFG_CAL;
+ break;
+ case CVP_VOCPROC_STATIC_COL_CAL_TYPE:
+ ret = CVP_VOCPROC_COL_CAL;
+ break;
+ case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE:
+ ret = CVP_VOCVOL_COL_CAL;
+ break;
+ case CVS_VOCSTRM_STATIC_COL_CAL_TYPE:
+ ret = CVS_VOCSTRM_COL_CAL;
+ break;
+ case VOICE_RTAC_INFO_CAL_TYPE:
+ ret = VOICE_RTAC_INFO_CAL;
+ break;
+ case VOICE_RTAC_APR_CAL_TYPE:
+ ret = VOICE_RTAC_APR_CAL;
+ break;
+ default:
+ pr_err("%s: Invalid cal type %d!\n", __func__, cal_type);
+ }
+ return ret;
+}
+
+static int voice_prepare_volume_boost(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ return voc_deregister_vocproc_vol_table();
+}
+
+static int voice_enable_volume_boost(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ return voc_register_vocproc_vol_table();
+}
+
+static int voice_alloc_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ int cal_version;
+
+ pr_debug("%s\n", __func__);
+
+ cal_version = cal_utils_get_cal_type_version(data);
+ common.is_per_vocoder_cal_enabled =
+ !!(cal_version & PER_VOCODER_CAL_BIT_MASK);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: Could not get cal index %d!\n",
+ __func__, cal_index);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_alloc_cal(data_size, data,
+ common.cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: Cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int voice_dealloc_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: Could not get cal index %d!\n",
+ __func__, cal_index);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_dealloc_cal(data_size, data,
+ common.cal_data[cal_index]);
+ if (ret < 0) {
+ pr_err("%s: Cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int voice_set_cal(int32_t cal_type,
+ size_t data_size, void *data)
+{
+ int ret = 0;
+ int cal_index;
+ pr_debug("%s\n", __func__);
+
+ cal_index = get_cal_type_index(cal_type);
+ if (cal_index < 0) {
+ pr_err("%s: Could not get cal index %d!\n",
+ __func__, cal_index);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = cal_utils_set_cal(data_size, data,
+ common.cal_data[cal_index], 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: Cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+ __func__, ret, cal_type);
+
+ ret = -EINVAL;
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static void voice_delete_cal_data(void)
+{
+ pr_debug("%s\n", __func__);
+
+ cal_utils_destroy_cal_types(MAX_VOICE_CAL_TYPES, common.cal_data);
+
+ return;
+}
+
+static int voice_init_cal_data(void)
+{
+ int ret = 0;
+ struct cal_type_info cal_type_info[] = {
+ {{CVP_VOC_RX_TOPOLOGY_CAL_TYPE,
+ {NULL, NULL, NULL, voice_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{CVP_VOC_TX_TOPOLOGY_CAL_TYPE,
+ {NULL, NULL, NULL, voice_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{CVP_VOCPROC_STATIC_CAL_TYPE,
+ {voice_alloc_cal, voice_dealloc_cal, NULL,
+ voice_set_cal, NULL, NULL} },
+ {NULL, voice_unmap_cal_memory,
+ cal_utils_match_buf_num} },
+
+ {{CVP_VOCPROC_DYNAMIC_CAL_TYPE,
+ {voice_alloc_cal, voice_dealloc_cal,
+ voice_prepare_volume_boost,
+ voice_set_cal, NULL,
+ voice_enable_volume_boost} },
+ {NULL, voice_unmap_cal_memory,
+ cal_utils_match_buf_num} },
+
+ {{CVP_VOCDEV_CFG_CAL_TYPE,
+ {voice_alloc_cal, voice_dealloc_cal, NULL,
+ voice_set_cal, NULL, NULL} },
+ {NULL, voice_unmap_cal_memory,
+ cal_utils_match_buf_num} },
+
+ {{CVP_VOCPROC_STATIC_COL_CAL_TYPE,
+ {NULL, NULL, NULL, voice_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE,
+ {NULL, NULL, NULL, voice_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{CVS_VOCSTRM_STATIC_CAL_TYPE,
+ {voice_alloc_cal, voice_dealloc_cal, NULL,
+ voice_set_cal, NULL, NULL} },
+ {NULL, voice_unmap_cal_memory,
+ cal_utils_match_buf_num} },
+
+ {{CVS_VOCSTRM_STATIC_COL_CAL_TYPE,
+ {NULL, NULL, NULL, voice_set_cal, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{VOICE_RTAC_INFO_CAL_TYPE,
+ {NULL, NULL, NULL, NULL, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+
+ {{VOICE_RTAC_APR_CAL_TYPE,
+ {NULL, NULL, NULL, NULL, NULL, NULL} },
+ {NULL, NULL, cal_utils_match_buf_num} },
+ };
+
+ ret = cal_utils_create_cal_types(MAX_VOICE_CAL_TYPES, common.cal_data,
+ cal_type_info);
+ if (ret < 0) {
+ pr_err("%s: Could not create cal type!\n",
+ __func__);
+
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return ret;
+err:
+ voice_delete_cal_data();
+ memset(&common, 0, sizeof(struct common_data));
+ return ret;
+}
+
+static int voice_send_set_sound_focus_cmd(struct voice_data *v,
+ struct sound_focus_param soundFocusData)
+{
+ struct cvp_set_sound_focus_param_cmd_t cvp_set_sound_focus_param_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ u16 cvp_handle;
+ int i;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ apr_cvp = common.apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* send Sound Focus Params to cvp */
+ cvp_set_sound_focus_param_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_set_sound_focus_param_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_set_sound_focus_param_cmd) - APR_HDR_SIZE);
+ cvp_set_sound_focus_param_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_set_sound_focus_param_cmd.hdr.dest_port = cvp_handle;
+ cvp_set_sound_focus_param_cmd.hdr.token = 0;
+ cvp_set_sound_focus_param_cmd.hdr.opcode =
+ VSS_ISOUNDFOCUS_CMD_SET_SECTORS;
+
+ memset(&(cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param), 0xFF,
+ sizeof(struct vss_isoundfocus_cmd_set_sectors_t));
+ for (i = 0; i < MAX_SECTORS; i++) {
+ cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param.
+ start_angles[i] = soundFocusData.start_angle[i];
+ cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param.
+ enables[i] = soundFocusData.enable[i];
+ pr_debug("%s: start_angle[%d] = %d\n",
+ __func__, i, soundFocusData.start_angle[i]);
+ pr_debug("%s: enable[%d] = %d\n",
+ __func__, i, soundFocusData.enable[i]);
+ }
+ cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param.gain_step =
+ soundFocusData.gain_step;
+ pr_debug("%s: gain_step = %d\n", __func__, soundFocusData.gain_step);
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+
+ ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_set_sound_focus_param_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error in sending APR command\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+ if (common.is_sound_focus_resp_success) {
+ ret = 0;
+ } else {
+ pr_err("%s: Error in setting sound focus params\n", __func__);
+
+ ret = -EINVAL;
+ }
+
+done:
+ pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int voc_set_sound_focus(struct sound_focus_param soundFocusData)
+{
+ struct voice_data *v = NULL;
+ int ret = -EINVAL;
+ struct voice_session_itr itr;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ mutex_lock(&common.common_lock);
+ voice_itr_init(&itr, ALL_SESSION_VSID);
+ while (voice_itr_get_next_session(&itr, &v)) {
+ if (v != NULL) {
+ mutex_lock(&v->lock);
+ if (is_voc_state_active(v->voc_state) &&
+ (v->lch_mode != VOICE_LCH_START) &&
+ !v->disable_topology)
+ ret = voice_send_set_sound_focus_cmd(v,
+ soundFocusData);
+ mutex_unlock(&v->lock);
+ } else {
+ pr_err("%s: invalid session\n", __func__);
+
+ ret = -EINVAL;
+ break;
+ }
+ }
+ mutex_unlock(&common.common_lock);
+ pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int voice_send_get_sound_focus_cmd(struct voice_data *v,
+ struct sound_focus_param *soundFocusData)
+{
+ struct apr_hdr cvp_get_sound_focus_param_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ u16 cvp_handle;
+ int i;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ if (!v) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ apr_cvp = common.apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* send APR command to retrive Sound Focus Params */
+ cvp_get_sound_focus_param_cmd.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_get_sound_focus_param_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_get_sound_focus_param_cmd) - APR_HDR_SIZE);
+ cvp_get_sound_focus_param_cmd.src_port =
+ voice_get_idx_for_session(v->session_id);
+ cvp_get_sound_focus_param_cmd.dest_port = cvp_handle;
+ cvp_get_sound_focus_param_cmd.token = 0;
+ cvp_get_sound_focus_param_cmd.opcode = VSS_ISOUNDFOCUS_CMD_GET_SECTORS;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_get_sound_focus_param_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error in sending APR command\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+ if (common.is_sound_focus_resp_success) {
+ for (i = 0; i < MAX_SECTORS; i++) {
+ soundFocusData->start_angle[i] =
+ common.soundFocusResponse.start_angles[i];
+ soundFocusData->enable[i] =
+ common.soundFocusResponse.enables[i];
+ pr_debug("%s: start_angle[%d] = %d\n",
+ __func__, i, soundFocusData->start_angle[i]);
+ pr_debug("%s: enable[%d] = %d\n",
+ __func__, i, soundFocusData->enable[i]);
+ }
+ soundFocusData->gain_step = common.soundFocusResponse.gain_step;
+ pr_debug("%s: gain_step = %d\n", __func__,
+ soundFocusData->gain_step);
+
+ common.is_sound_focus_resp_success = false;
+ ret = 0;
+ } else {
+ pr_err("%s: Invalid payload received from CVD\n", __func__);
+
+ ret = -EINVAL;
+ }
+done:
+ pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int voc_get_sound_focus(struct sound_focus_param *soundFocusData)
+{
+ struct voice_data *v = NULL;
+ int ret = -EINVAL;
+ struct voice_session_itr itr;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ mutex_lock(&common.common_lock);
+ voice_itr_init(&itr, ALL_SESSION_VSID);
+ while (voice_itr_get_next_session(&itr, &v)) {
+ if (v) {
+ mutex_lock(&v->lock);
+ if (is_voc_state_active(v->voc_state) &&
+ (v->lch_mode != VOICE_LCH_START) &&
+ !v->disable_topology)
+ ret = voice_send_get_sound_focus_cmd(v,
+ soundFocusData);
+ mutex_unlock(&v->lock);
+ } else {
+ pr_err("%s: invalid session\n", __func__);
+
+ ret = -EINVAL;
+ break;
+ }
+ }
+ mutex_unlock(&common.common_lock);
+ pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int is_source_tracking_shared_memomry_allocated(void)
+{
+ bool ret;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ if (common.source_tracking_sh_mem.sh_mem_block.client != NULL &&
+ common.source_tracking_sh_mem.sh_mem_block.handle != NULL)
+ ret = true;
+ else
+ ret = false;
+
+ pr_debug("%s: Exit\n", __func__);
+
+ return ret;
+}
+
+static int voice_alloc_source_tracking_shared_memory(void)
+{
+ int ret = 0;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ ret = msm_audio_ion_alloc("source_tracking_sh_mem_block",
+ &(common.source_tracking_sh_mem.sh_mem_block.client),
+ &(common.source_tracking_sh_mem.sh_mem_block.handle),
+ BUFFER_BLOCK_SIZE,
+ &(common.source_tracking_sh_mem.sh_mem_block.phys),
+ (size_t *)&(common.source_tracking_sh_mem.sh_mem_block.size),
+ &(common.source_tracking_sh_mem.sh_mem_block.data));
+ if (ret < 0) {
+ pr_err("%s: audio ION alloc failed for sh_mem block, ret = %d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ memset((void *)(common.source_tracking_sh_mem.sh_mem_block.data), 0,
+ common.source_tracking_sh_mem.sh_mem_block.size);
+
+ pr_debug("%s: sh_mem_block: phys:[%pK], data:[0x%pK], size:[%zd]\n",
+ __func__,
+ &(common.source_tracking_sh_mem.sh_mem_block.phys),
+ (void *)(common.source_tracking_sh_mem.sh_mem_block.data),
+ (size_t)(common.source_tracking_sh_mem.sh_mem_block.size));
+
+ ret = msm_audio_ion_alloc("source_tracking_sh_mem_table",
+ &(common.source_tracking_sh_mem.sh_mem_table.client),
+ &(common.source_tracking_sh_mem.sh_mem_table.handle),
+ sizeof(struct vss_imemory_table_t),
+ &(common.source_tracking_sh_mem.sh_mem_table.phys),
+ (size_t *)&(common.source_tracking_sh_mem.sh_mem_table.size),
+ &(common.source_tracking_sh_mem.sh_mem_table.data));
+ if (ret < 0) {
+ pr_err("%s: audio ION alloc failed for sh_mem table, ret = %d\n",
+ __func__, ret);
+
+ ret = msm_audio_ion_free(
+ common.source_tracking_sh_mem.sh_mem_block.client,
+ common.source_tracking_sh_mem.sh_mem_block.handle);
+ common.source_tracking_sh_mem.sh_mem_block.client = NULL;
+ common.source_tracking_sh_mem.sh_mem_block.handle = NULL;
+ if (ret < 0)
+ pr_err("%s: Error:%d freeing memory\n", __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ memset((void *)(common.source_tracking_sh_mem.sh_mem_table.data), 0,
+ common.source_tracking_sh_mem.sh_mem_table.size);
+
+ pr_debug("%s sh_mem_table: phys:[%pK], data:[0x%pK], size:[%zd],\n",
+ __func__,
+ &(common.source_tracking_sh_mem.sh_mem_table.phys),
+ (void *)(common.source_tracking_sh_mem.sh_mem_table.data),
+ (size_t)(common.source_tracking_sh_mem.sh_mem_table.size));
+
+done:
+ pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int voice_alloc_and_map_source_tracking_shared_memory(
+ struct voice_data *v)
+{
+ int ret = 0;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ ret = voice_alloc_source_tracking_shared_memory();
+ if (ret) {
+ pr_err("%s: Failed to allocate shared memory %d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = voice_map_memory_physical_cmd(v,
+ &(common.source_tracking_sh_mem.sh_mem_table),
+ common.source_tracking_sh_mem.sh_mem_block.phys,
+ common.source_tracking_sh_mem.sh_mem_block.size,
+ VOC_SOURCE_TRACKING_MEM_MAP_TOKEN);
+ if (ret) {
+ pr_err("%s: memory mapping failed %d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+done:
+ pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int voice_unmap_and_free_source_tracking_shared_memory(
+ struct voice_data *v)
+{
+ int ret = 0;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ if (common.source_tracking_sh_mem.mem_handle != 0) {
+ ret = voice_send_mvm_unmap_memory_physical_cmd(v,
+ common.source_tracking_sh_mem.mem_handle);
+ if (ret < 0) {
+ pr_err("%s: Memory_unmap failed err %d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ if ((common.source_tracking_sh_mem.sh_mem_block.client == NULL) ||
+ (common.source_tracking_sh_mem.sh_mem_block.handle == NULL))
+ goto done;
+
+ ret = msm_audio_ion_free(
+ common.source_tracking_sh_mem.sh_mem_block.client,
+ common.source_tracking_sh_mem.sh_mem_block.handle);
+ if (ret < 0) {
+ pr_err("%s: Error:%d freeing memory\n", __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+done:
+ common.source_tracking_sh_mem.mem_handle = 0;
+ common.source_tracking_sh_mem.sh_mem_block.client = NULL;
+ common.source_tracking_sh_mem.sh_mem_block.handle = NULL;
+ pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int voice_send_get_source_tracking_cmd(struct voice_data *v,
+ struct source_tracking_param *sourceTrackingData)
+{
+ struct cvp_get_source_tracking_param_cmd_t st_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ u16 cvp_handle;
+ int i;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ if (!v) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvp = common.apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvp_handle = voice_get_cvp_handle(v);
+
+ if (!is_source_tracking_shared_memomry_allocated()) {
+ ret = voice_alloc_and_map_source_tracking_shared_memory(v);
+ if (ret) {
+ pr_err("%s: Fail in allocating/mapping shared memory\n",
+ __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+ st_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ st_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(st_cmd) - APR_HDR_SIZE);
+ st_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id);
+ st_cmd.hdr.dest_port = cvp_handle;
+ st_cmd.hdr.token = 0;
+ st_cmd.hdr.opcode = VSS_ISOURCETRACK_CMD_GET_ACTIVITY;
+
+ st_cmd.cvp_get_source_tracking_param.mem_handle =
+ common.source_tracking_sh_mem.mem_handle;
+ st_cmd.cvp_get_source_tracking_param.mem_address_lsw =
+ lower_32_bits(common.source_tracking_sh_mem.sh_mem_block.phys);
+ st_cmd.cvp_get_source_tracking_param.mem_address_msw =
+ msm_audio_populate_upper_32_bits(common.source_tracking_sh_mem.
+ sh_mem_block.phys);
+ st_cmd.cvp_get_source_tracking_param.mem_size =
+ (uint32_t)common.source_tracking_sh_mem.sh_mem_block.size;
+ pr_debug("%s: mem_handle=0x%x, mem_address_lsw=0x%x, msw=0x%x, mem_size=%d\n",
+ __func__,
+ st_cmd.cvp_get_source_tracking_param.mem_handle,
+ st_cmd.cvp_get_source_tracking_param.mem_address_lsw,
+ st_cmd.cvp_get_source_tracking_param.mem_address_msw,
+ (uint32_t)st_cmd.cvp_get_source_tracking_param.mem_size);
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp,
+ (uint32_t *) &st_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error in sending APR command\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ v->async_err));
+ ret = adsp_err_get_lnx_err_code(
+ v->async_err);
+ goto done;
+ }
+
+ if (common.is_source_tracking_resp_success) {
+ for (i = 0; i < MAX_SECTORS; i++) {
+ sourceTrackingData->vad[i] =
+ common.sourceTrackingResponse.voice_active[i];
+ pr_debug("%s: vad[%d] = %d\n",
+ __func__, i, sourceTrackingData->vad[i]);
+ }
+ sourceTrackingData->doa_speech =
+ common.sourceTrackingResponse.talker_doa;
+ pr_debug("%s: doa_speech = %d\n",
+ __func__, sourceTrackingData->doa_speech);
+
+ for (i = 0; i < MAX_NOISE_SOURCE_INDICATORS; i++) {
+ sourceTrackingData->doa_noise[i] =
+ common.sourceTrackingResponse.interferer_doa[i];
+ pr_debug("%s: doa_noise[%d] = %d\n",
+ __func__, i, sourceTrackingData->doa_noise[i]);
+ }
+ for (i = 0; i < MAX_POLAR_ACTIVITY_INDICATORS; i++) {
+ sourceTrackingData->polar_activity[i] =
+ common.sourceTrackingResponse.sound_strength[i];
+ pr_debug("%s: polar_activity[%d] = %d\n",
+ __func__, i, sourceTrackingData->polar_activity[i]);
+ }
+ common.is_source_tracking_resp_success = false;
+ ret = 0;
+ } else {
+ pr_err("%s: Error response received from CVD\n", __func__);
+
+ ret = -EINVAL;
+ }
+done:
+ pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int voc_get_source_tracking(struct source_tracking_param *sourceTrackingData)
+{
+ struct voice_data *v = NULL;
+ int ret = -EINVAL;
+ struct voice_session_itr itr;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ mutex_lock(&common.common_lock);
+
+ voice_itr_init(&itr, ALL_SESSION_VSID);
+ while (voice_itr_get_next_session(&itr, &v)) {
+ if (v != NULL) {
+ mutex_lock(&v->lock);
+ if (is_voc_state_active(v->voc_state) &&
+ (v->lch_mode != VOICE_LCH_START) &&
+ !v->disable_topology)
+ ret = voice_send_get_source_tracking_cmd(v,
+ sourceTrackingData);
+ mutex_unlock(&v->lock);
+ } else {
+ pr_err("%s: invalid session\n", __func__);
+
+ break;
+ }
+ }
+
+ mutex_unlock(&common.common_lock);
+ pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int voice_set_cvp_param(struct voice_data *v,
+ struct vss_icommon_mem_mapping_hdr *mem_hdr,
+ u32 *param_data, u32 param_size)
+{
+ struct vss_icommon_cmd_set_param *set_param = NULL;
+ uint32_t pkt_size = sizeof(struct vss_icommon_cmd_set_param);
+ void *apr_cvp;
+ int ret = 0;
+
+ apr_cvp = common.apr_q6_cvp;
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (param_data != NULL)
+ pkt_size += param_size;
+ set_param = kzalloc(pkt_size, GFP_KERNEL);
+ if (!set_param)
+ return -ENOMEM;
+
+ set_param->apr_hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ set_param->apr_hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE, pkt_size - APR_HDR_SIZE);
+ set_param->apr_hdr.src_svc = 0;
+ set_param->apr_hdr.src_domain = APR_DOMAIN_APPS;
+ set_param->apr_hdr.src_port = voice_get_idx_for_session(v->session_id);
+ set_param->apr_hdr.dest_svc = 0;
+ set_param->apr_hdr.dest_domain = APR_DOMAIN_ADSP;
+ set_param->apr_hdr.dest_port = voice_get_cvp_handle(v);
+ set_param->apr_hdr.token = VOC_SET_MEDIA_FORMAT_PARAM_TOKEN;
+ set_param->apr_hdr.opcode = q6common_is_instance_id_supported() ?
+ VSS_ICOMMON_CMD_SET_PARAM_V3 :
+ VSS_ICOMMON_CMD_SET_PARAM_V2;
+
+ set_param->payload_size = param_size;
+
+ if (mem_hdr != NULL) {
+ set_param->mem_hdr = *mem_hdr;
+ } else if (param_data != NULL) {
+ memcpy(set_param->param_data, param_data, param_size);
+ } else {
+ pr_err("%s: Both memory header and param data are NULL\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvp, (u32 *) set_param);
+ if (ret < 0) {
+ pr_err("%s: Failed to send apr packet, error %d\n", __func__,
+ ret);
+ goto done;
+ }
+
+ ret = wait_event_timeout(v->cvp_wait,
+ v->cvp_state == CMD_STATUS_SUCCESS,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n", __func__,
+ adsp_err_get_err_str(v->async_err));
+ ret = adsp_err_get_lnx_err_code(v->async_err);
+ goto done;
+ }
+ ret = 0;
+
+done:
+ kfree(set_param);
+ return ret;
+}
+
+static int voice_pack_and_set_cvp_param(struct voice_data *v,
+ struct param_hdr_v3 param_hdr,
+ u8 *param_data)
+{
+ u8 *packed_data = NULL;
+ u32 total_size = 0;
+ int ret = 0;
+
+ total_size = sizeof(union param_hdrs) + param_hdr.param_size;
+ packed_data = kzalloc(total_size, GFP_KERNEL);
+ if (!packed_data)
+ return -ENOMEM;
+
+ ret = q6common_pack_pp_params(packed_data, &param_hdr, param_data,
+ &total_size);
+ if (ret) {
+ pr_err("%s: Failed to pack params, error %d", __func__, ret);
+ goto done;
+ }
+
+ ret = voice_set_cvp_param(v, NULL, (u32 *) packed_data, total_size);
+
+done:
+ kfree(packed_data);
+ return ret;
+}
+
+/*
+ * Out of band is not supported and there are currently no pre-packed cases,
+ * so pack and set in the same function. When needed, split up.
+ */
+static int voice_pack_and_set_cvs_ui_property(struct voice_data *v,
+ struct param_hdr_v3 param_hdr,
+ u8 *param_data)
+{
+ struct vss_icommon_cmd_set_ui_property *set_ui_property = NULL;
+ u32 total_size = 0;
+ u32 pkt_size = 0;
+ bool iid_supported = q6common_is_instance_id_supported();
+ void *apr_cvs;
+ int ret = 0;
+
+ apr_cvs = common.apr_q6_cvs;
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ total_size = sizeof(struct vss_icommon_cmd_set_ui_property) +
+ sizeof(union param_hdrs) + param_hdr.param_size;
+ set_ui_property = kzalloc(total_size, GFP_KERNEL);
+ if (!set_ui_property)
+ return -ENOMEM;
+
+ ret = q6common_pack_pp_params(set_ui_property->param_data, &param_hdr,
+ param_data, &pkt_size);
+ if (ret) {
+ pr_err("%s: Failed to pack params, error %d", __func__, ret);
+ goto done;
+ }
+
+ /*
+ * Pack the APR header after packing the data so we have the actual
+ * total size of the payload
+ */
+ set_ui_property->apr_hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ set_ui_property->apr_hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE, pkt_size);
+ set_ui_property->apr_hdr.src_svc = 0;
+ set_ui_property->apr_hdr.src_domain = APR_DOMAIN_APPS;
+ set_ui_property->apr_hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ set_ui_property->apr_hdr.dest_svc = 0;
+ set_ui_property->apr_hdr.dest_domain = APR_DOMAIN_ADSP;
+ set_ui_property->apr_hdr.dest_port = voice_get_cvs_handle(v);
+ set_ui_property->apr_hdr.token = 0;
+
+ set_ui_property->apr_hdr.opcode =
+ iid_supported ? VSS_ICOMMON_CMD_SET_UI_PROPERTY_V2 :
+ VSS_ICOMMON_CMD_SET_UI_PROPERTY;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ v->async_err = 0;
+ ret = apr_send_pkt(apr_cvs, (u32 *) set_ui_property);
+ if (ret < 0) {
+ pr_err("%s: Failed to send apr packet, error %d\n", __func__,
+ ret);
+ goto done;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ v->cvs_state == CMD_STATUS_SUCCESS,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (v->async_err > 0) {
+ pr_err("%s: DSP returned error[%s]\n", __func__,
+ adsp_err_get_err_str(v->async_err));
+ ret = adsp_err_get_lnx_err_code(v->async_err);
+ goto done;
+ }
+ ret = 0;
+done:
+ kfree(set_ui_property);
+ return ret;
+}
+
+int is_voc_initialized(void)
+{
+ return module_initialized;
+}
+
+static int __init voice_init(void)
+{
+ int rc = 0, i = 0;
+
+ memset(&common, 0, sizeof(struct common_data));
+
+ /* set default value */
+ common.default_mute_val = 0; /* default is un-mute */
+ common.default_sample_val = 8000;
+ common.default_vol_step_val = 0;
+ common.default_vol_ramp_duration_ms = DEFAULT_VOLUME_RAMP_DURATION;
+ common.default_mute_ramp_duration_ms = DEFAULT_MUTE_RAMP_DURATION;
+
+ /* Initialize EC Ref media format info */
+ common.ec_ref_ext = false;
+ common.ec_media_fmt_info.port_id = AFE_PORT_INVALID;
+ common.ec_media_fmt_info.num_channels = 0;
+ common.ec_media_fmt_info.bits_per_sample = 16;
+ common.ec_media_fmt_info.sample_rate = 8000;
+ memset(&common.ec_media_fmt_info.channel_mapping, 0,
+ VSS_CHANNEL_MAPPING_SIZE);
+
+ /* Initialize AFE Sidetone Enable */
+ common.sidetone_enable = false;
+
+ /* Initialize MVS info. */
+ common.mvs_info.network_type = VSS_NETWORK_ID_DEFAULT;
+
+ /* Initialize is low memory flag */
+ common.is_destroy_cvd = false;
+
+ /* Initialize CVD version */
+ strlcpy(common.cvd_version, CVD_VERSION_DEFAULT,
+ sizeof(common.cvd_version));
+ /* Initialize Per-Vocoder Calibration flag */
+ common.is_per_vocoder_cal_enabled = false;
+
+ mutex_init(&common.common_lock);
+
+ /* Initialize session id with vsid */
+ init_session_id();
+
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+
+ /* initialize dev_rx and dev_tx */
+ common.voice[i].dev_rx.dev_mute = common.default_mute_val;
+ common.voice[i].dev_tx.dev_mute = common.default_mute_val;
+ common.voice[i].dev_rx.volume_step_value =
+ common.default_vol_step_val;
+ common.voice[i].dev_rx.volume_ramp_duration_ms =
+ common.default_vol_ramp_duration_ms;
+ common.voice[i].dev_rx.dev_mute_ramp_duration_ms =
+ common.default_mute_ramp_duration_ms;
+ common.voice[i].dev_tx.dev_mute_ramp_duration_ms =
+ common.default_mute_ramp_duration_ms;
+ common.voice[i].stream_rx.stream_mute = common.default_mute_val;
+ common.voice[i].stream_tx.stream_mute = common.default_mute_val;
+
+ common.voice[i].dev_tx.port_id = 0x100B;
+ common.voice[i].dev_rx.port_id = 0x100A;
+ common.voice[i].dev_tx.dev_id = 0;
+ common.voice[i].dev_rx.dev_id = 0;
+ common.voice[i].dev_tx.no_of_channels = 0;
+ common.voice[i].dev_rx.no_of_channels = 0;
+ common.voice[i].dev_tx.sample_rate = 8000;
+ common.voice[i].dev_rx.sample_rate = 8000;
+ common.voice[i].dev_tx.bits_per_sample = 16;
+ common.voice[i].dev_rx.bits_per_sample = 16;
+ memset(&common.voice[i].dev_tx.channel_mapping, 0,
+ VSS_CHANNEL_MAPPING_SIZE);
+ memset(&common.voice[i].dev_rx.channel_mapping, 0,
+ VSS_CHANNEL_MAPPING_SIZE);
+ common.voice[i].sidetone_gain = 0x512;
+ common.voice[i].dtmf_rx_detect_en = 0;
+ common.voice[i].lch_mode = 0;
+ common.voice[i].disable_topology = false;
+
+ common.voice[i].voc_state = VOC_INIT;
+
+ init_waitqueue_head(&common.voice[i].mvm_wait);
+ init_waitqueue_head(&common.voice[i].cvs_wait);
+ init_waitqueue_head(&common.voice[i].cvp_wait);
+
+ mutex_init(&common.voice[i].lock);
+ }
+
+ if (voice_init_cal_data())
+ pr_err("%s: Could not init cal data!\n", __func__);
+
+ if (rc == 0)
+ module_initialized = true;
+
+ pr_debug("%s: rc=%d\n", __func__, rc);
+ return rc;
+}
+
+device_initcall(voice_init);
+
+static void __exit voice_exit(void)
+{
+ voice_delete_cal_data();
+ free_cal_map_table();
+}
+
+__exitcall(voice_exit);
diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h
new file mode 100644
index 000000000000..f448e701d564
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6voice.h
@@ -0,0 +1,1892 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __QDSP6VOICE_H__
+#define __QDSP6VOICE_H__
+
+#include <linux/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/rtac.h>
+#include <linux/msm_ion.h>
+#include <sound/voice_params.h>
+#include <linux/power_supply.h>
+
+#define MAX_VOC_PKT_SIZE 642
+#define SESSION_NAME_LEN 20
+#define NUM_OF_MEMORY_BLOCKS 1
+#define NUM_OF_BUFFERS 2
+#define VSS_NUM_CHANNELS_MAX 8
+#define VSS_CHANNEL_MAPPING_SIZE (sizeof(uint8_t) * VSS_NUM_CHANNELS_MAX)
+/*
+ * BUFFER BLOCK SIZE based on
+ * the supported page size
+ */
+#define BUFFER_BLOCK_SIZE 4096
+
+#define MAX_COL_INFO_SIZE 324
+
+#define VOC_REC_UPLINK 0x00
+#define VOC_REC_DOWNLINK 0x01
+#define VOC_REC_BOTH 0x02
+
+#define VSS_IVERSION_CMD_GET 0x00011378
+#define VSS_IVERSION_RSP_GET 0x00011379
+#define CVD_VERSION_STRING_MAX_SIZE 31
+#define CVD_VERSION_DEFAULT ""
+#define CVD_VERSION_0_0 "0.0"
+#define CVD_VERSION_2_1 "2.1"
+#define CVD_VERSION_2_2 "2.2"
+#define CVD_VERSION_2_3 "2.3"
+
+#define CVD_INT_VERSION_DEFAULT 0
+#define CVD_INT_VERSION_0_0 1
+#define CVD_INT_VERSION_2_1 2
+#define CVD_INT_VERSION_2_2 3
+#define CVD_INT_VERSION_2_3 4
+#define CVD_INT_VERSION_LAST CVD_INT_VERSION_2_3
+#define CVD_INT_VERSION_MAX (CVD_INT_VERSION_LAST + 1)
+
+struct cvd_version_table {
+ char cvd_ver[CVD_VERSION_STRING_MAX_SIZE];
+ int cvd_ver_int;
+};
+
+int voc_get_cvd_version(char *);
+
+/* Payload structure for the VSS_IVERSION_RSP_GET command response */
+struct vss_iversion_rsp_get_t {
+ char version[CVD_VERSION_STRING_MAX_SIZE];
+ /* NULL-terminated version string */
+};
+
+enum {
+ CVP_VOC_RX_TOPOLOGY_CAL = 0,
+ CVP_VOC_TX_TOPOLOGY_CAL,
+ CVP_VOCPROC_CAL,
+ CVP_VOCVOL_CAL,
+ CVP_VOCDEV_CFG_CAL,
+ CVP_VOCPROC_COL_CAL,
+ CVP_VOCVOL_COL_CAL,
+ CVS_VOCSTRM_CAL,
+ CVS_VOCSTRM_COL_CAL,
+ VOICE_RTAC_INFO_CAL,
+ VOICE_RTAC_APR_CAL,
+ MAX_VOICE_CAL_TYPES
+};
+
+struct voice_header {
+ uint32_t id;
+ uint32_t data_len;
+};
+
+struct voice_init {
+ struct voice_header hdr;
+ void *cb_handle;
+};
+
+/* Stream information payload structure */
+struct stream_data {
+ uint32_t stream_mute;
+ uint32_t stream_mute_ramp_duration_ms;
+};
+
+/* Device information payload structure */
+struct device_data {
+ uint32_t dev_mute;
+ uint32_t sample_rate;
+ uint16_t bits_per_sample;
+ uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX];
+ uint32_t enabled;
+ uint32_t dev_id;
+ uint32_t port_id;
+ uint32_t volume_step_value;
+ uint32_t volume_ramp_duration_ms;
+ uint32_t dev_mute_ramp_duration_ms;
+ uint32_t no_of_channels;
+};
+
+/*
+ * Format information structure to match
+ * vss_param_endpoint_media_format_info_t
+ */
+struct media_format_info {
+ uint32_t port_id;
+ uint16_t num_channels;
+ uint16_t bits_per_sample;
+ uint32_t sample_rate;
+ uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX];
+};
+
+enum {
+ VOC_NO_SET_PARAM_TOKEN = 0,
+ VOC_RTAC_SET_PARAM_TOKEN,
+ VOC_SET_MEDIA_FORMAT_PARAM_TOKEN,
+ VOC_SET_PARAM_TOKEN_MAX
+};
+
+struct voice_dev_route_state {
+ u16 rx_route_flag;
+ u16 tx_route_flag;
+};
+
+struct voice_rec_route_state {
+ u16 ul_flag;
+ u16 dl_flag;
+};
+
+enum {
+ VOC_INIT = 0,
+ VOC_RUN,
+ VOC_CHANGE,
+ VOC_RELEASE,
+ VOC_ERROR,
+ VOC_STANDBY,
+};
+
+struct mem_buffer {
+ dma_addr_t phys;
+ void *data;
+ uint32_t size; /* size of buffer */
+};
+
+struct share_mem_buf {
+ struct ion_handle *handle;
+ struct ion_client *client;
+ struct mem_buffer buf[NUM_OF_BUFFERS];
+};
+
+struct mem_map_table {
+ dma_addr_t phys;
+ void *data;
+ size_t size; /* size of buffer */
+ struct ion_handle *handle;
+ struct ion_client *client;
+};
+
+/* Common */
+#define VSS_ICOMMON_CMD_SET_UI_PROPERTY 0x00011103
+#define VSS_ICOMMON_CMD_SET_UI_PROPERTY_V2 0x00013248
+/* Set a UI property */
+#define VSS_ICOMMON_CMD_MAP_MEMORY 0x00011025
+#define VSS_ICOMMON_CMD_UNMAP_MEMORY 0x00011026
+/* General shared memory; byte-accessible, 4 kB-aligned. */
+#define VSS_ICOMMON_MAP_MEMORY_SHMEM8_4K_POOL 3
+
+struct vss_icommon_cmd_map_memory_t {
+ uint32_t phys_addr;
+ /* Physical address of a memory region; must be at least
+ * 4 kB aligned.
+ */
+
+ uint32_t mem_size;
+ /* Number of bytes in the region; should be a multiple of 32. */
+
+ uint16_t mem_pool_id;
+ /* Type of memory being provided. The memory ID implicitly defines
+ * the characteristics of the memory. The characteristics might include
+ * alignment type, permissions, etc.
+ * Memory pool ID. Possible values:
+ * 3 -- VSS_ICOMMON_MEM_TYPE_SHMEM8_4K_POOL.
+ */
+} __packed;
+
+struct vss_icommon_cmd_unmap_memory_t {
+ uint32_t phys_addr;
+ /* Physical address of a memory region; must be at least
+ * 4 kB aligned.
+ */
+} __packed;
+
+struct vss_map_memory_cmd {
+ struct apr_hdr hdr;
+ struct vss_icommon_cmd_map_memory_t vss_map_mem;
+} __packed;
+
+struct vss_unmap_memory_cmd {
+ struct apr_hdr hdr;
+ struct vss_icommon_cmd_unmap_memory_t vss_unmap_mem;
+} __packed;
+
+struct vss_param_endpoint_media_format_info {
+ /* AFE port ID to which this media format corresponds to. */
+ uint32_t port_id;
+ /*
+ * Number of channels of data.
+ * Supported values: 1 to 8
+ */
+ uint16_t num_channels;
+ /*
+ * Bits per sample of data.
+ * Supported values: 16 and 24
+ */
+ uint16_t bits_per_sample;
+ /*
+ * Sampling rate in Hz.
+ * Supported values: 8000, 11025, 16000, 22050, 24000, 32000,
+ * 44100, 48000, 88200, 96000, 176400, and 192000
+ */
+ uint32_t sample_rate;
+ /*
+ * The channel[i] mapping describes channel i. Each element i
+ * of the array describes channel i inside the data buffer. An
+ * unused or unknown channel is set to 0.
+ */
+ uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX];
+} __packed;
+
+struct vss_icommon_mem_mapping_hdr {
+ /*
+ * Pointer to the unique identifier for an address (physical/virtual).
+ *
+ * If the parameter data payload is within the message payload
+ * (in-band), set this field to 0. The parameter data begins at the
+ * specified data payload address.
+ *
+ * If the parameter data is out-of-band, this field is the handle to
+ * the physical address in the shared memory that holds the parameter
+ * data.
+ */
+ uint32_t mem_handle;
+
+ /*
+ * Location of the parameter data payload.
+ *
+ * The payload is an array of vss_icommon_param_data_t. If the
+ * mem_handle is 0, this field is ignored.
+ */
+ uint64_t mem_address;
+
+} __packed;
+
+struct vss_icommon_cmd_set_param {
+ /* APR Header */
+ struct apr_hdr apr_hdr;
+
+ /* The memory mapping header to be used when sending outband */
+ struct vss_icommon_mem_mapping_hdr mem_hdr;
+
+ /* Size of the parameter data payload in bytes. */
+ uint32_t payload_size;
+
+ /*
+ * Parameter data payload when inband. Should have size param_size.
+ * Bit size of payload must be a multiple of 4.
+ */
+ uint8_t param_data[0];
+} __packed;
+/* TO MVM commands */
+#define VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x000110FF
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL 0x00011327
+/*
+ * VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL
+ * Description: This command is required to let MVM know
+ * who is in control of session.
+ * Payload: Defined by vss_imvm_cmd_set_policy_dual_control_t.
+ * Result: Wait for APRV2_IBASIC_RSP_RESULT response.
+ */
+
+#define VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110FE
+/* Create a new full control MVM session. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_ATTACH_STREAM 0x0001123C
+/* Attach a stream to the MVM. */
+
+#define VSS_IMVM_CMD_DETACH_STREAM 0x0001123D
+/* Detach a stream from the MVM. */
+
+#define VSS_IMVM_CMD_ATTACH_VOCPROC 0x0001123E
+/* Attach a vocproc to the MVM. The MVM will symmetrically connect this vocproc
+ * to all the streams currently attached to it.
+ */
+
+#define VSS_IMVM_CMD_DETACH_VOCPROC 0x0001123F
+/* Detach a vocproc from the MVM. The MVM will symmetrically disconnect this
+ * vocproc from all the streams to which it is currently attached.
+*/
+
+#define VSS_IMVM_CMD_START_VOICE 0x00011190
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_STANDBY_VOICE 0x00011191
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_STOP_VOICE 0x00011192
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_PAUSE_VOICE 0x0001137D
+/* No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_ATTACH_VOCPROC 0x000110F8
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_DETACH_VOCPROC 0x000110F9
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+
+#define VSS_ISTREAM_CMD_SET_TTY_MODE 0x00011196
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ICOMMON_CMD_SET_NETWORK 0x0001119C
+/* Set the network type. */
+
+#define VSS_ICOMMON_CMD_SET_VOICE_TIMING 0x000111E0
+/* Set the voice timing parameters. */
+
+#define VSS_IMEMORY_CMD_MAP_PHYSICAL 0x00011334
+#define VSS_IMEMORY_RSP_MAP 0x00011336
+#define VSS_IMEMORY_CMD_UNMAP 0x00011337
+#define VSS_IMVM_CMD_SET_CAL_NETWORK 0x0001137A
+#define VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE 0x0001137B
+#define VSS_IHDVOICE_CMD_ENABLE 0x000130A2
+#define VSS_IHDVOICE_CMD_DISABLE 0x000130A3
+
+enum msm_audio_voc_rate {
+ VOC_0_RATE, /* Blank frame */
+ VOC_8_RATE, /* 1/8 rate */
+ VOC_4_RATE, /* 1/4 rate */
+ VOC_2_RATE, /* 1/2 rate */
+ VOC_1_RATE, /* Full rate */
+ VOC_8_RATE_NC /* Noncritical 1/8 rate */
+};
+
+struct vss_istream_cmd_set_tty_mode_t {
+ uint32_t mode;
+ /**<
+ * TTY mode.
+ *
+ * 0 : TTY disabled
+ * 1 : HCO
+ * 2 : VCO
+ * 3 : FULL
+ */
+} __packed;
+
+struct vss_istream_cmd_attach_vocproc_t {
+ uint16_t handle;
+ /**< Handle of vocproc being attached. */
+} __packed;
+
+struct vss_istream_cmd_detach_vocproc_t {
+ uint16_t handle;
+ /**< Handle of vocproc being detached. */
+} __packed;
+
+struct vss_imvm_cmd_attach_stream_t {
+ uint16_t handle;
+ /* The stream handle to attach. */
+} __packed;
+
+struct vss_imvm_cmd_detach_stream_t {
+ uint16_t handle;
+ /* The stream handle to detach. */
+} __packed;
+
+struct vss_icommon_cmd_set_network_t {
+ uint32_t network_id;
+ /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */
+} __packed;
+
+struct vss_icommon_cmd_set_voice_timing_t {
+ uint16_t mode;
+ /*
+ * The vocoder frame synchronization mode.
+ *
+ * 0 : No frame sync.
+ * 1 : Hard VFR (20ms Vocoder Frame Reference interrupt).
+ */
+ uint16_t enc_offset;
+ /*
+ * The offset in microseconds from the VFR to deliver a Tx vocoder
+ * packet. The offset should be less than 20000us.
+ */
+ uint16_t dec_req_offset;
+ /*
+ * The offset in microseconds from the VFR to request for an Rx vocoder
+ * packet. The offset should be less than 20000us.
+ */
+ uint16_t dec_offset;
+ /*
+ * The offset in microseconds from the VFR to indicate the deadline to
+ * receive an Rx vocoder packet. The offset should be less than 20000us.
+ * Rx vocoder packets received after this deadline are not guaranteed to
+ * be processed.
+ */
+} __packed;
+
+struct vss_imvm_cmd_create_control_session_t {
+ char name[SESSION_NAME_LEN];
+ /*
+ * A variable-sized stream name.
+ *
+ * The stream name size is the payload size minus the size of the other
+ * fields.
+ */
+} __packed;
+
+
+struct vss_imvm_cmd_set_policy_dual_control_t {
+ bool enable_flag;
+ /* Set to TRUE to enable modem state machine control */
+} __packed;
+
+struct mvm_attach_vocproc_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_attach_vocproc_t mvm_attach_cvp_handle;
+} __packed;
+
+struct mvm_detach_vocproc_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_detach_vocproc_t mvm_detach_cvp_handle;
+} __packed;
+
+struct mvm_create_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_imvm_cmd_create_control_session_t mvm_session;
+} __packed;
+
+struct mvm_modem_dual_control_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_imvm_cmd_set_policy_dual_control_t voice_ctl;
+} __packed;
+
+struct mvm_set_tty_mode_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_tty_mode_t tty_mode;
+} __packed;
+
+struct mvm_attach_stream_cmd {
+ struct apr_hdr hdr;
+ struct vss_imvm_cmd_attach_stream_t attach_stream;
+} __packed;
+
+struct mvm_detach_stream_cmd {
+ struct apr_hdr hdr;
+ struct vss_imvm_cmd_detach_stream_t detach_stream;
+} __packed;
+
+struct mvm_set_network_cmd {
+ struct apr_hdr hdr;
+ struct vss_icommon_cmd_set_network_t network;
+} __packed;
+
+struct mvm_set_voice_timing_cmd {
+ struct apr_hdr hdr;
+ struct vss_icommon_cmd_set_voice_timing_t timing;
+} __packed;
+
+struct mvm_set_hd_enable_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct vss_imemory_table_descriptor_t {
+ uint32_t mem_address_lsw;
+ uint32_t mem_address_msw;
+ /*
+ * Base physical address of the table. The address must be aligned
+ * to LCM( cache_line_size, page_align, max_data_width ), where the
+ * attributes are specified in #VSS_IMEMORY_CMD_MAP_PHYSICAL, and
+ * LCM = Least Common Multiple. The table at the address must have
+ * the format specified by #vss_imemory_table_t.
+ */
+ uint32_t mem_size;
+ /* Size in bytes of the table. */
+} __packed;
+
+struct vss_imemory_block_t {
+ uint64_t mem_address;
+ /*
+ * Base address of the memory block. The address is virtual for virtual
+ * memory and physical for physical memory. The address must be aligned
+ * to LCM( cache_line_size, page_align, max_data_width ), where the
+ * attributes are specified in VSS_IMEMORY_CMD_MAP_VIRTUAL or
+ * VSS_IMEMORY_CMD_MAP_PHYSICAL, and LCM = Least Common Multiple.
+ */
+ uint32_t mem_size;
+ /*
+ * Size in bytes of the memory block. The size must be multiple of
+ * page_align, where page_align is specified in
+ * VSS_IMEMORY_CMD_MAP_VIRTUAL or #VSS_IMEMORY_CMD_MAP_PHYSICAL.
+ */
+} __packed;
+
+struct vss_imemory_table_t {
+ struct vss_imemory_table_descriptor_t next_table_descriptor;
+ /*
+ * Specifies the next table. If there is no next table,
+ * set the size of the table to 0 and the table address is ignored.
+ */
+ struct vss_imemory_block_t blocks[NUM_OF_MEMORY_BLOCKS];
+ /* Specifies one ore more memory blocks. */
+} __packed;
+
+struct vss_imemory_cmd_map_physical_t {
+ struct apr_hdr hdr;
+ struct vss_imemory_table_descriptor_t table_descriptor;
+ bool is_cached;
+ /*
+ * Indicates cached or uncached memory. Supported values:
+ * TRUE - Cached.
+ */
+ uint16_t cache_line_size;
+ /* Cache line size in bytes. Supported values: 128 */
+ uint32_t access_mask;
+ /*
+ * CVD's access permission to the memory while it is mapped.
+ * Supported values:
+ * bit 0 - If set, the memory is readable.
+ * bit 1 - If set, the memory is writable.
+ */
+ uint32_t page_align;
+ /* Page frame alignment in bytes. Supported values: 4096 */
+ uint8_t min_data_width;
+ /*
+ * Minimum native data type width in bits that can be accessed.
+ * Supported values: 8
+ */
+ uint8_t max_data_width;
+ /*
+ * Maximum native data type width in bits that can be accessed.
+ * Supported values: 64
+ */
+} __packed;
+
+struct vss_imvm_cmd_set_cal_network_t {
+ struct apr_hdr hdr;
+ uint32_t network_id;
+} __packed;
+
+struct vss_imvm_cmd_set_cal_media_type_t {
+ struct apr_hdr hdr;
+ uint32_t media_id;
+} __packed;
+
+struct vss_imemory_cmd_unmap_t {
+ struct apr_hdr hdr;
+ uint32_t mem_handle;
+} __packed;
+
+/* TO CVS commands */
+#define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x00011140
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110F7
+/* Create a new full control stream session. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
+
+/*
+ * This command changes the mute setting. The new mute setting will
+ * be applied over the specified ramp duration.
+ */
+#define VSS_IVOLUME_CMD_MUTE_V2 0x0001138B
+
+#define VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2 0x00011369
+
+#define VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA 0x0001127A
+
+#define VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA 0x0001307D
+#define VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA 0x0001307E
+
+#define VSS_ISTREAM_CMD_SET_MEDIA_TYPE 0x00011186
+/* Set media type on the stream. */
+
+#define VSS_ISTREAM_EVT_SEND_ENC_BUFFER 0x00011015
+/* Event sent by the stream to its client to provide an encoded packet. */
+
+#define VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER 0x00011017
+/* Event sent by the stream to its client requesting for a decoder packet.
+ * The client should respond with a VSS_ISTREAM_EVT_SEND_DEC_BUFFER event.
+ */
+
+#define VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_REQUEST 0x0001136E
+
+#define VSS_ISTREAM_EVT_SEND_DEC_BUFFER 0x00011016
+/* Event sent by the client to the stream in response to a
+ * VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER event, providing a decoder packet.
+ */
+
+#define VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE 0x0001113E
+/* Set AMR encoder rate. */
+
+#define VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE 0x0001113F
+/* Set AMR-WB encoder rate. */
+
+#define VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE 0x00011019
+/* Set encoder minimum and maximum rate. */
+
+#define VSS_ISTREAM_CMD_SET_ENC_DTX_MODE 0x0001101D
+/* Set encoder DTX mode. */
+
+#define MODULE_ID_VOICE_MODULE_ST 0x00010EE3
+#define VOICE_PARAM_MOD_ENABLE 0x00010E00
+
+#define VSS_IPLAYBACK_CMD_START 0x000112BD
+/* Start in-call music delivery on the Tx voice path. */
+
+#define VSS_IPLAYBACK_CMD_STOP 0x00011239
+/* Stop the in-call music delivery on the Tx voice path. */
+
+#define VSS_IPLAYBACK_PORT_ID_DEFAULT 0x0000FFFF
+/* Default AFE port ID. */
+
+#define VSS_IPLAYBACK_PORT_ID_VOICE 0x00008005
+/* AFE port ID for VOICE 1. */
+
+#define VSS_IPLAYBACK_PORT_ID_VOICE2 0x00008002
+/* AFE port ID for VOICE 2. */
+
+#define VSS_IRECORD_CMD_START 0x000112BE
+/* Start in-call conversation recording. */
+#define VSS_IRECORD_CMD_STOP 0x00011237
+/* Stop in-call conversation recording. */
+
+#define VSS_IRECORD_PORT_ID_DEFAULT 0x0000FFFF
+/* Default AFE port ID. */
+
+#define VSS_IRECORD_TAP_POINT_NONE 0x00010F78
+/* Indicates no tapping for specified path. */
+
+#define VSS_IRECORD_TAP_POINT_STREAM_END 0x00010F79
+/* Indicates that specified path should be tapped at the end of the stream. */
+
+#define VSS_IRECORD_MODE_TX_RX_STEREO 0x00010F7A
+/* Select Tx on left channel and Rx on right channel. */
+
+#define VSS_IRECORD_MODE_TX_RX_MIXING 0x00010F7B
+/* Select mixed Tx and Rx paths. */
+
+#define VSS_PARAM_TX_PORT_ENDPOINT_MEDIA_INFO 0x00013253
+
+#define VSS_PARAM_RX_PORT_ENDPOINT_MEDIA_INFO 0x00013254
+
+#define VSS_PARAM_EC_REF_PORT_ENDPOINT_MEDIA_INFO 0x00013255
+
+#define VSS_MODULE_CVD_GENERIC 0x0001316E
+
+#define VSS_ISTREAM_EVT_NOT_READY 0x000110FD
+
+#define VSS_ISTREAM_EVT_READY 0x000110FC
+
+#define VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_READY 0x0001136F
+/*notify dsp that decoder buffer is ready*/
+
+#define VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_READY 0x0001136C
+/*dsp notifying client that encoder buffer is ready*/
+
+#define VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_CONSUMED 0x0001136D
+/*notify dsp that encoder buffer is consumed*/
+
+#define VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG 0x0001136B
+
+#define VSS_ISTREAM_PACKET_EXCHANGE_MODE_INBAND 0
+/* In-band packet exchange mode. */
+
+#define VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND 1
+/* Out-of-band packet exchange mode. */
+
+#define VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE 0x0001136A
+
+struct vss_iplayback_cmd_start_t {
+ uint16_t port_id;
+ /*
+ * AFE Port ID from which the audio samples are available.
+ * To use the default AFE pseudo port (0x8005), set this value to
+ * #VSS_IPLAYBACK_PORT_ID_DEFAULT.
+ */
+} __packed;
+
+struct vss_irecord_cmd_start_t {
+ uint32_t rx_tap_point;
+ /* Tap point to use on the Rx path. Supported values are:
+ * VSS_IRECORD_TAP_POINT_NONE : Do not record Rx path.
+ * VSS_IRECORD_TAP_POINT_STREAM_END : Rx tap point is at the end of
+ * the stream.
+ */
+ uint32_t tx_tap_point;
+ /* Tap point to use on the Tx path. Supported values are:
+ * VSS_IRECORD_TAP_POINT_NONE : Do not record tx path.
+ * VSS_IRECORD_TAP_POINT_STREAM_END : Tx tap point is at the end of
+ * the stream.
+ */
+ uint16_t port_id;
+ /* AFE Port ID to whcih the conversation recording stream needs to be
+ * sent. Set this to #VSS_IRECORD_PORT_ID_DEFAULT to use default AFE
+ * pseudo ports (0x8003 for Rx and 0x8004 for Tx).
+ */
+ uint32_t mode;
+ /* Recording Mode. The mode parameter value is ignored if the port_id
+ * is set to #VSS_IRECORD_PORT_ID_DEFAULT.
+ * The supported values:
+ * #VSS_IRECORD_MODE_TX_RX_STEREO
+ * #VSS_IRECORD_MODE_TX_RX_MIXING
+ */
+} __packed;
+
+struct vss_istream_cmd_create_passive_control_session_t {
+ char name[SESSION_NAME_LEN];
+ /**<
+ * A variable-sized stream name.
+ *
+ * The stream name size is the payload size minus the size of the other
+ * fields.
+ */
+} __packed;
+
+#define VSS_IVOLUME_DIRECTION_TX 0
+#define VSS_IVOLUME_DIRECTION_RX 1
+
+#define VSS_IVOLUME_MUTE_OFF 0
+#define VSS_IVOLUME_MUTE_ON 1
+
+#define DEFAULT_MUTE_RAMP_DURATION 500
+#define DEFAULT_VOLUME_RAMP_DURATION 20
+#define MAX_RAMP_DURATION 5000
+
+struct vss_ivolume_cmd_mute_v2_t {
+ uint16_t direction;
+ /*
+ * The direction field sets the direction to apply the mute command.
+ * The Supported values:
+ * VSS_IVOLUME_DIRECTION_TX
+ * VSS_IVOLUME_DIRECTION_RX
+ */
+ uint16_t mute_flag;
+ /*
+ * Turn mute on or off. The Supported values:
+ * VSS_IVOLUME_MUTE_OFF
+ * VSS_IVOLUME_MUTE_ON
+ */
+ uint16_t ramp_duration_ms;
+ /*
+ * Mute change ramp duration in milliseconds.
+ * The Supported values: 0 to 5000.
+ */
+} __packed;
+
+struct vss_istream_cmd_create_full_control_session_t {
+ uint16_t direction;
+ /*
+ * Stream direction.
+ *
+ * 0 : TX only
+ * 1 : RX only
+ * 2 : TX and RX
+ * 3 : TX and RX loopback
+ */
+ uint32_t enc_media_type;
+ /* Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+ uint32_t dec_media_type;
+ /* Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+ uint32_t network_id;
+ /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */
+ char name[SESSION_NAME_LEN];
+ /*
+ * A variable-sized stream name.
+ *
+ * The stream name size is the payload size minus the size of the other
+ * fields.
+ */
+} __packed;
+
+struct vss_istream_cmd_set_media_type_t {
+ uint32_t rx_media_id;
+ /* Set the Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+ uint32_t tx_media_id;
+ /* Set the Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+} __packed;
+
+struct vss_istream_evt_send_enc_buffer_t {
+ uint32_t media_id;
+ /* Media ID of the packet. */
+ uint8_t packet_data[MAX_VOC_PKT_SIZE];
+ /* Packet data buffer. */
+} __packed;
+
+struct vss_istream_evt_send_dec_buffer_t {
+ uint32_t media_id;
+ /* Media ID of the packet. */
+ uint8_t packet_data[MAX_VOC_PKT_SIZE];
+ /* Packet data. */
+} __packed;
+
+struct vss_istream_cmd_voc_amr_set_enc_rate_t {
+ uint32_t mode;
+ /* Set the AMR encoder rate.
+ *
+ * 0x00000000 : 4.75 kbps
+ * 0x00000001 : 5.15 kbps
+ * 0x00000002 : 5.90 kbps
+ * 0x00000003 : 6.70 kbps
+ * 0x00000004 : 7.40 kbps
+ * 0x00000005 : 7.95 kbps
+ * 0x00000006 : 10.2 kbps
+ * 0x00000007 : 12.2 kbps
+ */
+} __packed;
+
+struct vss_istream_cmd_voc_amrwb_set_enc_rate_t {
+ uint32_t mode;
+ /* Set the AMR-WB encoder rate.
+ *
+ * 0x00000000 : 6.60 kbps
+ * 0x00000001 : 8.85 kbps
+ * 0x00000002 : 12.65 kbps
+ * 0x00000003 : 14.25 kbps
+ * 0x00000004 : 15.85 kbps
+ * 0x00000005 : 18.25 kbps
+ * 0x00000006 : 19.85 kbps
+ * 0x00000007 : 23.05 kbps
+ * 0x00000008 : 23.85 kbps
+ */
+} __packed;
+
+struct vss_istream_cmd_cdma_set_enc_minmax_rate_t {
+ uint16_t min_rate;
+ /* Set the lower bound encoder rate.
+ *
+ * 0x0000 : Blank frame
+ * 0x0001 : Eighth rate
+ * 0x0002 : Quarter rate
+ * 0x0003 : Half rate
+ * 0x0004 : Full rate
+ */
+ uint16_t max_rate;
+ /* Set the upper bound encoder rate.
+ *
+ * 0x0000 : Blank frame
+ * 0x0001 : Eighth rate
+ * 0x0002 : Quarter rate
+ * 0x0003 : Half rate
+ * 0x0004 : Full rate
+ */
+} __packed;
+
+struct vss_istream_cmd_set_enc_dtx_mode_t {
+ uint32_t enable;
+ /* Toggle DTX on or off.
+ *
+ * 0 : Disables DTX
+ * 1 : Enables DTX
+ */
+} __packed;
+
+struct vss_istream_cmd_register_calibration_data_v2_t {
+ uint32_t cal_mem_handle;
+ /* Handle to the shared memory that holds the calibration data. */
+ uint32_t cal_mem_address_lsw;
+ uint32_t cal_mem_address_msw;
+ /* Location of calibration data. */
+ uint32_t cal_mem_size;
+ /* Size of the calibration data in bytes. */
+ uint8_t column_info[MAX_COL_INFO_SIZE];
+ /*
+ * Column info contains the number of columns and the array of columns
+ * in the calibration table. The order in which the columns are provided
+ * here must match the order in which they exist in the calibration
+ * table provided.
+ */
+} __packed;
+
+struct enable_param {
+ uint16_t enable;
+ uint16_t reserved_field;
+ /* Reserved, set to 0. */
+};
+
+struct vss_icommon_cmd_set_ui_property {
+ /* APR Header */
+ struct apr_hdr apr_hdr;
+
+ /* The parameter data to be filled when sent inband */
+ u8 param_data[0];
+} __packed;
+
+/*
+ * Event sent by the stream to the client that enables Rx DTMF
+ * detection whenever DTMF is detected in the Rx path.
+ *
+ * The DTMF detection feature can only be used to detect DTMF
+ * frequencies as listed in the vss_istream_evt_rx_dtmf_detected_t
+ * structure.
+ */
+
+#define VSS_ISTREAM_EVT_RX_DTMF_DETECTED 0x0001101A
+
+struct vss_istream_cmd_set_rx_dtmf_detection {
+ /*
+ * Enables/disables Rx DTMF detection
+ *
+ * Possible values are
+ * 0 - disable
+ * 1 - enable
+ *
+ */
+ uint32_t enable;
+};
+
+#define VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION 0x00011027
+
+struct vss_istream_evt_rx_dtmf_detected {
+ uint16_t low_freq;
+ /*
+ * Detected low frequency. Possible values:
+ * 697 Hz
+ * 770 Hz
+ * 852 Hz
+ * 941 Hz
+ */
+ uint16_t high_freq;
+ /*
+ * Detected high frequency. Possible values:
+ * 1209 Hz
+ * 1336 Hz
+ * 1477 Hz
+ * 1633 Hz
+ */
+};
+
+struct cvs_set_rx_dtmf_detection_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_rx_dtmf_detection cvs_dtmf_det;
+} __packed;
+
+
+struct cvs_create_passive_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_create_passive_control_session_t cvs_session;
+} __packed;
+
+struct cvs_create_full_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_create_full_control_session_t cvs_session;
+} __packed;
+
+struct cvs_destroy_session_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct cvs_set_mute_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivolume_cmd_mute_v2_t cvs_set_mute;
+} __packed;
+
+struct cvs_set_media_type_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_media_type_t media_type;
+} __packed;
+
+struct cvs_send_dec_buf_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_evt_send_dec_buffer_t dec_buf;
+} __packed;
+
+struct cvs_set_amr_enc_rate_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_voc_amr_set_enc_rate_t amr_rate;
+} __packed;
+
+struct cvs_set_amrwb_enc_rate_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_voc_amrwb_set_enc_rate_t amrwb_rate;
+} __packed;
+
+struct cvs_set_cdma_enc_minmax_rate_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_cdma_set_enc_minmax_rate_t cdma_rate;
+} __packed;
+
+struct cvs_set_enc_dtx_mode_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_enc_dtx_mode_t dtx_mode;
+} __packed;
+
+struct cvs_register_cal_data_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_register_calibration_data_v2_t cvs_cal_data;
+} __packed;
+
+struct cvs_deregister_cal_data_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct cvs_start_record_cmd {
+ struct apr_hdr hdr;
+ struct vss_irecord_cmd_start_t rec_mode;
+} __packed;
+
+struct cvs_start_playback_cmd {
+ struct apr_hdr hdr;
+ struct vss_iplayback_cmd_start_t playback_mode;
+} __packed;
+
+struct cvs_dec_buffer_ready_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct cvs_enc_buffer_consumed_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct vss_istream_cmd_set_oob_packet_exchange_config_t {
+ struct apr_hdr hdr;
+ uint32_t mem_handle;
+ uint32_t enc_buf_addr_lsw;
+ uint32_t enc_buf_addr_msw;
+ uint32_t enc_buf_size;
+ uint32_t dec_buf_addr_lsw;
+ uint32_t dec_buf_addr_msw;
+ uint32_t dec_buf_size;
+} __packed;
+
+struct vss_istream_cmd_set_packet_exchange_mode_t {
+ struct apr_hdr hdr;
+ uint32_t mode;
+} __packed;
+
+/* TO CVP commands */
+
+#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION 0x000100C3
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
+
+#define VSS_IVOCPROC_CMD_SET_DEVICE_V2 0x000112C6
+
+#define VSS_IVOCPROC_CMD_SET_DEVICE_V3 0x0001316A
+
+#define VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS 0x00013199
+
+#define VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT 0x00013198
+
+#define VSS_IVOCPROC_CMD_SET_VP3_DATA 0x000110EB
+
+#define VSS_IVOLUME_CMD_SET_STEP 0x000112C2
+
+#define VSS_IVOCPROC_CMD_ENABLE 0x000100C6
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IVOCPROC_CMD_DISABLE 0x000110E1
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+/*
+ * Registers the memory that contains device specific configuration data with
+ * the vocproc. The client must register device configuration data with the
+ * vocproc that corresponds with the device being set on the vocproc.
+ */
+#define VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG 0x00011371
+
+/*
+ * Deregisters the memory that holds device configuration data from the
+ vocproc.
+*/
+#define VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG 0x00011372
+
+#define CVD_CAL_DATA_FORMAT_MINOR_VERSION_V0 0x00000000
+#define CVD_CAL_DATA_FORMAT_MINOR_VERSION_V1 0x00000001
+#define VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2 0x00011373
+#define VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA 0x00011276
+
+#define VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA 0x00011374
+#define VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA 0x00011375
+
+#define VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA 0x00013079
+#define VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA 0x0001307A
+
+#define VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA 0x0001307B
+#define VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA 0x0001307C
+
+#define VSS_IVOCPROC_TOPOLOGY_ID_NONE 0x00010F70
+#define VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS 0x00010F71
+#define VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE 0x00010F72
+
+#define VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT 0x00010F77
+
+/* Newtwork IDs */
+#define VSS_ICOMMON_CAL_NETWORK_ID_NONE 0x0001135E
+
+/* Select internal mixing mode. */
+#define VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING 0x00010F7C
+
+/* Select external mixing mode. */
+#define VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING 0x00010F7D
+
+/* Default AFE port ID. Applicable to Tx and Rx. */
+#define VSS_IVOCPROC_PORT_ID_NONE 0xFFFF
+
+#define VSS_NETWORK_ID_DEFAULT 0x00010037
+
+/* Voice over Internet Protocol (VoIP) network ID. Common for all bands.*/
+#define VSS_NETWORK_ID_VOIP 0x00011362
+
+/* Media types */
+#define VSS_MEDIA_ID_EVRC_MODEM 0x00010FC2
+/* 80-VF690-47 CDMA enhanced variable rate vocoder modem format. */
+#define VSS_MEDIA_ID_AMR_NB_MODEM 0x00010FC6
+/* 80-VF690-47 UMTS AMR-NB vocoder modem format. */
+#define VSS_MEDIA_ID_AMR_WB_MODEM 0x00010FC7
+/* 80-VF690-47 UMTS AMR-WB vocoder modem format. */
+
+#define VSS_MEDIA_ID_PCM_8_KHZ 0x00010FCB
+#define VSS_MEDIA_ID_PCM_16_KHZ 0x00010FCC
+#define VSS_MEDIA_ID_PCM_32_KHZ 0x00010FD9
+#define VSS_MEDIA_ID_PCM_48_KHZ 0x00010FD6
+
+/* Linear PCM (16-bit, little-endian). */
+#define VSS_MEDIA_ID_G711_ALAW 0x00010FCD
+/* G.711 a-law (contains two 10ms vocoder frames). */
+#define VSS_MEDIA_ID_G711_MULAW 0x00010FCE
+/* G.711 mu-law (contains two 10ms vocoder frames). */
+#define VSS_MEDIA_ID_G729 0x00010FD0
+/* G.729AB (contains two 10ms vocoder frames. */
+#define VSS_MEDIA_ID_4GV_NB_MODEM 0x00010FC3
+/*CDMA EVRC-B vocoder modem format */
+#define VSS_MEDIA_ID_4GV_WB_MODEM 0x00010FC4
+/*CDMA EVRC-WB vocoder modem format */
+#define VSS_MEDIA_ID_4GV_NW_MODEM 0x00010FC5
+/*CDMA EVRC-NW vocoder modem format */
+
+#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2 0x000112BF
+#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3 0x00013169
+
+#define VSS_NUM_DEV_CHANNELS_1 1
+#define VSS_NUM_DEV_CHANNELS_2 2
+#define VSS_NUM_DEV_CHANNELS_3 3
+#define VSS_NUM_DEV_CHANNELS_4 4
+
+struct vss_ivocproc_cmd_create_full_control_session_v2_t {
+ uint16_t direction;
+ /*
+ * Vocproc direction. The supported values:
+ * VSS_IVOCPROC_DIRECTION_RX
+ * VSS_IVOCPROC_DIRECTION_TX
+ * VSS_IVOCPROC_DIRECTION_RX_TX
+ */
+ uint16_t tx_port_id;
+ /*
+ * Tx device port ID to which the vocproc connects. If a port ID is
+ * not being supplied, set this to #VSS_IVOCPROC_PORT_ID_NONE.
+ */
+ uint32_t tx_topology_id;
+ /*
+ * Tx path topology ID. If a topology ID is not being supplied, set
+ * this to #VSS_IVOCPROC_TOPOLOGY_ID_NONE.
+ */
+ uint16_t rx_port_id;
+ /*
+ * Rx device port ID to which the vocproc connects. If a port ID is
+ * not being supplied, set this to #VSS_IVOCPROC_PORT_ID_NONE.
+ */
+ uint32_t rx_topology_id;
+ /*
+ * Rx path topology ID. If a topology ID is not being supplied, set
+ * this to #VSS_IVOCPROC_TOPOLOGY_ID_NONE.
+ */
+ uint32_t profile_id;
+ /* Voice calibration profile ID. */
+ uint32_t vocproc_mode;
+ /*
+ * Vocproc mode. The supported values:
+ * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING
+ * VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING
+ */
+ uint16_t ec_ref_port_id;
+ /*
+ * Port ID to which the vocproc connects for receiving echo
+ * cancellation reference signal. If a port ID is not being supplied,
+ * set this to #VSS_IVOCPROC_PORT_ID_NONE. This parameter value is
+ * ignored when the vocproc_mode parameter is set to
+ * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING.
+ */
+ char name[SESSION_NAME_LEN];
+ /*
+ * Session name string used to identify a session that can be shared
+ * with passive controllers (optional). The string size, including the
+ * NULL termination character, is limited to 31 characters.
+ */
+} __packed;
+
+struct vss_ivocproc_cmd_set_volume_index_t {
+ uint16_t vol_index;
+ /*
+ * Volume index utilized by the vocproc to index into the volume table
+ * provided in VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE and set
+ * volume on the VDSP.
+ */
+} __packed;
+
+struct vss_ivolume_cmd_set_step_t {
+ uint16_t direction;
+ /*
+ * The direction field sets the direction to apply the volume command.
+ * The supported values:
+ * #VSS_IVOLUME_DIRECTION_RX
+ */
+ uint32_t value;
+ /*
+ * Volume step used to find the corresponding linear volume and
+ * the best match index in the registered volume calibration table.
+ */
+ uint16_t ramp_duration_ms;
+ /*
+ * Volume change ramp duration in milliseconds.
+ * The supported values: 0 to 5000.
+ */
+} __packed;
+
+struct vss_ivocproc_cmd_set_device_v2_t {
+ uint16_t tx_port_id;
+ /*
+ * TX device port ID which vocproc will connect to.
+ * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
+ */
+ uint32_t tx_topology_id;
+ /*
+ * TX leg topology ID.
+ * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
+ * pre/post-processing blocks and is pass-through.
+ */
+ uint16_t rx_port_id;
+ /*
+ * RX device port ID which vocproc will connect to.
+ * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
+ */
+ uint32_t rx_topology_id;
+ /*
+ * RX leg topology ID.
+ * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
+ * pre/post-processing blocks and is pass-through.
+ */
+ uint32_t vocproc_mode;
+ /* Vocproc mode. The supported values:
+ * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING - 0x00010F7C
+ * VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING - 0x00010F7D
+ */
+ uint16_t ec_ref_port_id;
+ /* Port ID to which the vocproc connects for receiving
+ * echo
+ */
+} __packed;
+
+struct vss_ivocproc_cmd_register_device_config_t {
+ uint32_t mem_handle;
+ /*
+ * Handle to the shared memory that holds the per-network calibration
+ * data.
+ */
+ uint32_t mem_address_lsw;
+ uint32_t mem_address_msw;
+ /* Location of calibration data. */
+ uint32_t mem_size;
+ /* Size of the calibration data in bytes. */
+} __packed;
+
+struct vss_ivocproc_cmd_register_calibration_data_v2_t {
+ uint32_t cal_mem_handle;
+ /*
+ * Handle to the shared memory that holds the per-network calibration
+ * data.
+ */
+ uint32_t cal_mem_address_lsw;
+ uint32_t cal_mem_address_msw;
+ /* Location of calibration data. */
+ uint32_t cal_mem_size;
+ /* Size of the calibration data in bytes. */
+ uint8_t column_info[MAX_COL_INFO_SIZE];
+ /*
+ * Column info contains the number of columns and the array of columns
+ * in the calibration table. The order in which the columns are provided
+ * here must match the order in which they exist in the calibration
+ * table provided.
+ */
+} __packed;
+
+struct vss_ivocproc_cmd_register_volume_cal_data_t {
+ uint32_t cal_mem_handle;
+ /*
+ * Handle to the shared memory that holds the volume calibration
+ * data.
+ */
+ uint32_t cal_mem_address_lsw;
+ uint32_t cal_mem_address_msw;
+ /* Location of volume calibration data. */
+ uint32_t cal_mem_size;
+ /* Size of the volume calibration data in bytes. */
+ uint8_t column_info[MAX_COL_INFO_SIZE];
+ /*
+ * Column info contains the number of columns and the array of columns
+ * in the calibration table. The order in which the columns are provided
+ * here must match the order in which they exist in the calibration
+ * table provided.
+ */
+} __packed;
+
+struct vss_ivocproc_cmd_topology_set_dev_channels_t {
+ uint16_t tx_num_channels;
+ /*
+ * Number of Mics.
+ * Supported values
+ * 1 VSS_NUM_DEV_CHANNELS_1
+ * 2 VSS_NUM_DEV_CHANNELS_2
+ * 3 VSS_NUM_DEV_CHANNELS_3
+ * 4 VSS_NUM_DEV_CHANNELS_4
+ */
+ uint16_t rx_num_channels;
+ /*
+ * Number of speaker channels.
+ * Supported values
+ * 1 VSS_NUM_DEV_CHANNELS_1
+ */
+} __packed;
+
+/* Starts a vocoder PCM session */
+#define VSS_IVPCM_CMD_START_V2 0x00011339
+
+/* Default tap point location on the TX path. */
+#define VSS_IVPCM_TAP_POINT_TX_DEFAULT 0x00011289
+
+/* Default tap point location on the RX path. */
+#define VSS_IVPCM_TAP_POINT_RX_DEFAULT 0x0001128A
+
+/* Indicates tap point direction is output. */
+#define VSS_IVPCM_TAP_POINT_DIR_OUT 0
+
+/* Indicates tap point direction is input. */
+#define VSS_IVPCM_TAP_POINT_DIR_IN 1
+
+/* Indicates tap point direction is output and input. */
+#define VSS_IVPCM_TAP_POINT_DIR_OUT_IN 2
+
+
+#define VSS_IVPCM_SAMPLING_RATE_AUTO 0
+
+/* Indicates 8 KHz vocoder PCM sampling rate. */
+#define VSS_IVPCM_SAMPLING_RATE_8K 8000
+
+/* Indicates 16 KHz vocoder PCM sampling rate. */
+#define VSS_IVPCM_SAMPLING_RATE_16K 16000
+
+/* RX and TX */
+#define MAX_TAP_POINTS_SUPPORTED 2
+
+struct vss_ivpcm_tap_point {
+ uint32_t tap_point;
+ uint16_t direction;
+ uint16_t sampling_rate;
+ uint16_t duration;
+} __packed;
+
+
+struct vss_ivpcm_cmd_start_v2_t {
+ uint32_t mem_handle;
+ uint32_t num_tap_points;
+ struct vss_ivpcm_tap_point tap_points[MAX_TAP_POINTS_SUPPORTED];
+} __packed;
+
+#define VSS_IVPCM_EVT_PUSH_BUFFER_V2 0x0001133A
+
+/* Push buffer event mask indicating output buffer is filled. */
+#define VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER 1
+
+/* Push buffer event mask indicating input buffer is consumed. */
+#define VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER 2
+
+
+struct vss_ivpcm_evt_push_buffer_v2_t {
+ uint32_t tap_point;
+ uint32_t push_buf_mask;
+ uint64_t out_buf_mem_address;
+ uint16_t out_buf_mem_size;
+ uint64_t in_buf_mem_address;
+ uint16_t in_buf_mem_size;
+ uint16_t sampling_rate;
+ uint16_t num_in_channels;
+} __packed;
+
+#define VSS_IVPCM_EVT_NOTIFY_V2 0x0001133B
+
+/* Notify event mask indicates output buffer is filled. */
+#define VSS_IVPCM_NOTIFY_MASK_OUTPUT_BUFFER 1
+
+/* Notify event mask indicates input buffer is consumed. */
+#define VSS_IVPCM_NOTIFY_MASK_INPUT_BUFFER 2
+
+/* Notify event mask indicates a timetick */
+#define VSS_IVPCM_NOTIFY_MASK_TIMETICK 4
+
+/* Notify event mask indicates an error occured in output buffer operation */
+#define VSS_IVPCM_NOTIFY_MASK_OUTPUT_ERROR 8
+
+/* Notify event mask indicates an error occured in input buffer operation */
+#define VSS_IVPCM_NOTIFY_MASK_INPUT_ERROR 16
+
+
+struct vss_ivpcm_evt_notify_v2_t {
+ uint32_t tap_point;
+ uint32_t notify_mask;
+ uint64_t out_buff_addr;
+ uint64_t in_buff_addr;
+ uint16_t filled_out_size;
+ uint16_t request_buf_size;
+ uint16_t sampling_rate;
+ uint16_t num_out_channels;
+} __packed;
+
+struct cvp_start_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivpcm_cmd_start_v2_t vpcm_start_cmd;
+} __packed;
+
+struct cvp_push_buf_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivpcm_evt_push_buffer_v2_t vpcm_evt_push_buffer;
+} __packed;
+
+#define VSS_IVPCM_CMD_STOP 0x0001100B
+
+struct cvp_create_full_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_create_full_control_session_v2_t cvp_session;
+} __packed;
+
+struct cvp_command {
+ struct apr_hdr hdr;
+} __packed;
+
+struct cvp_set_device_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_set_device_v2_t cvp_set_device_v2;
+} __packed;
+
+struct cvp_set_dev_channels_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_topology_set_dev_channels_t cvp_set_channels;
+} __packed;
+
+struct cvp_set_vp3_data_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct cvp_set_rx_volume_index_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_set_volume_index_t cvp_set_vol_idx;
+} __packed;
+
+struct cvp_set_rx_volume_step_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivolume_cmd_set_step_t cvp_set_vol_step;
+} __packed;
+
+struct cvp_register_dev_cfg_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_register_device_config_t cvp_dev_cfg_data;
+} __packed;
+
+struct cvp_deregister_dev_cfg_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct cvp_register_cal_data_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_register_calibration_data_v2_t cvp_cal_data;
+} __packed;
+
+struct cvp_deregister_cal_data_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct cvp_register_vol_cal_data_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_register_volume_cal_data_t cvp_vol_cal_data;
+} __packed;
+
+struct cvp_deregister_vol_cal_data_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct cvp_set_mute_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivolume_cmd_mute_v2_t cvp_set_mute;
+} __packed;
+
+/* CB for up-link packets. */
+typedef void (*ul_cb_fn)(uint8_t *voc_pkt,
+ uint32_t pkt_len,
+ uint32_t timestamp,
+ void *private_data);
+
+/* CB for down-link packets. */
+typedef void (*dl_cb_fn)(uint8_t *voc_pkt,
+ void *private_data);
+
+/* CB for DTMF RX Detection */
+typedef void (*dtmf_rx_det_cb_fn)(uint8_t *pkt,
+ char *session,
+ void *private_data);
+
+typedef void (*voip_ssr_cb) (uint32_t opcode,
+ void *private_data);
+
+typedef void (*hostpcm_cb_fn)(uint8_t *data,
+ char *session,
+ void *private_data);
+
+struct mvs_driver_info {
+ uint32_t media_type;
+ uint32_t rate;
+ uint32_t network_type;
+ uint32_t dtx_mode;
+ ul_cb_fn ul_cb;
+ dl_cb_fn dl_cb;
+ voip_ssr_cb ssr_cb;
+ void *private_data;
+ uint32_t evrc_min_rate;
+ uint32_t evrc_max_rate;
+};
+
+struct dtmf_driver_info {
+ dtmf_rx_det_cb_fn dtmf_rx_ul_cb;
+ void *private_data;
+};
+
+struct hostpcm_driver_info {
+ hostpcm_cb_fn hostpcm_evt_cb;
+ void *private_data;
+};
+
+struct incall_rec_info {
+ uint32_t rec_enable;
+ uint32_t rec_mode;
+ uint32_t recording;
+};
+
+struct incall_music_info {
+ uint32_t play_enable;
+ uint32_t playing;
+ int count;
+ int force;
+ uint16_t port_id;
+};
+
+struct share_memory_info {
+ u32 mem_handle;
+ struct share_mem_buf sh_buf;
+ struct mem_map_table memtbl;
+};
+
+#define VSS_ISOUNDFOCUS_CMD_SET_SECTORS 0x00013133
+#define VSS_ISOUNDFOCUS_CMD_GET_SECTORS 0x00013134
+#define VSS_ISOUNDFOCUS_RSP_GET_SECTORS 0x00013135
+#define VSS_ISOURCETRACK_CMD_GET_ACTIVITY 0x00013136
+
+struct vss_isoundfocus_cmd_set_sectors_t {
+ uint16_t start_angles[8];
+ uint8_t enables[8];
+ uint16_t gain_step;
+} __packed;
+
+/* Payload of the VSS_ISOUNDFOCUS_RSP_GET_SECTORS response */
+struct vss_isoundfocus_rsp_get_sectors_t {
+ uint16_t start_angles[8];
+ uint8_t enables[8];
+ uint16_t gain_step;
+} __packed;
+
+struct cvp_set_sound_focus_param_cmd_t {
+ struct apr_hdr hdr;
+ struct vss_isoundfocus_cmd_set_sectors_t cvp_set_sound_focus_param;
+} __packed;
+
+/* Payload structure for the VSS_ISOURCETRACK_CMD_GET_ACTIVITY command */
+struct vss_isourcetrack_cmd_get_activity_t {
+ uint32_t mem_handle;
+ uint32_t mem_address_lsw;
+ uint32_t mem_address_msw;
+ uint32_t mem_size;
+} __packed;
+
+struct cvp_get_source_tracking_param_cmd_t {
+ struct apr_hdr hdr;
+ struct vss_isourcetrack_cmd_get_activity_t
+ cvp_get_source_tracking_param;
+} __packed;
+
+/* Structure for the sound activity data */
+struct vss_isourcetrack_activity_data_t {
+ uint8_t voice_active[8];
+ uint16_t talker_doa;
+ uint16_t interferer_doa[3];
+ uint8_t sound_strength[360];
+} __packed;
+
+struct shared_mem_info {
+ uint32_t mem_handle;
+ struct mem_map_table sh_mem_block;
+ struct mem_map_table sh_mem_table;
+};
+
+struct voice_data {
+ int voc_state;/*INIT, CHANGE, RELEASE, RUN */
+
+ /* Shared mem to store decoder and encoder packets */
+ struct share_memory_info shmem_info;
+
+ wait_queue_head_t mvm_wait;
+ wait_queue_head_t cvs_wait;
+ wait_queue_head_t cvp_wait;
+
+ /* Cache the values related to Rx and Tx devices */
+ struct device_data dev_rx;
+ struct device_data dev_tx;
+
+ /* Cache the values related to Rx and Tx streams */
+ struct stream_data stream_rx;
+ struct stream_data stream_tx;
+
+ u32 mvm_state;
+ u32 cvs_state;
+ u32 cvp_state;
+
+ u32 async_err;
+
+ /* Handle to MVM in the Q6 */
+ u16 mvm_handle;
+ /* Handle to CVS in the Q6 */
+ u16 cvs_handle;
+ /* Handle to CVP in the Q6 */
+ u16 cvp_handle;
+
+ struct mutex lock;
+
+ bool disable_topology;
+
+ uint16_t sidetone_gain;
+ uint8_t tty_mode;
+ /* slowtalk enable value */
+ uint32_t st_enable;
+ uint32_t hd_enable;
+ uint32_t dtmf_rx_detect_en;
+ /* Local Call Hold mode */
+ uint8_t lch_mode;
+
+ struct voice_dev_route_state voc_route_state;
+
+ u32 session_id;
+
+ struct incall_rec_info rec_info;
+
+ struct incall_music_info music_info;
+
+ struct voice_rec_route_state rec_route_state;
+
+ struct power_supply *psy;
+};
+
+struct cal_mem {
+ struct ion_handle *handle;
+ uint32_t phy;
+ void *buf;
+};
+
+#define MAX_VOC_SESSIONS 8
+
+struct common_data {
+ /* these default values are for all devices */
+ uint32_t default_mute_val;
+ uint32_t default_sample_val;
+ uint32_t default_vol_step_val;
+ uint32_t default_vol_ramp_duration_ms;
+ uint32_t default_mute_ramp_duration_ms;
+ bool ec_ref_ext;
+ struct media_format_info ec_media_fmt_info;
+
+ /* APR to MVM in the Q6 */
+ void *apr_q6_mvm;
+ /* APR to CVS in the Q6 */
+ void *apr_q6_cvs;
+ /* APR to CVP in the Q6 */
+ void *apr_q6_cvp;
+
+ struct cal_type_data *cal_data[MAX_VOICE_CAL_TYPES];
+
+ struct mem_map_table cal_mem_map_table;
+ uint32_t cal_mem_handle;
+
+ struct mem_map_table rtac_mem_map_table;
+ uint32_t rtac_mem_handle;
+
+ uint32_t voice_host_pcm_mem_handle;
+
+ struct cal_mem cvp_cal;
+ struct cal_mem cvs_cal;
+
+ struct mutex common_lock;
+
+ struct mvs_driver_info mvs_info;
+
+ struct dtmf_driver_info dtmf_info;
+
+ struct hostpcm_driver_info hostpcm_info;
+
+ struct voice_data voice[MAX_VOC_SESSIONS];
+
+ bool srvcc_rec_flag;
+ bool is_destroy_cvd;
+ bool is_vote_bms;
+ char cvd_version[CVD_VERSION_STRING_MAX_SIZE];
+ bool is_per_vocoder_cal_enabled;
+ bool is_sound_focus_resp_success;
+ bool is_source_tracking_resp_success;
+ struct vss_isoundfocus_rsp_get_sectors_t soundFocusResponse;
+ struct shared_mem_info source_tracking_sh_mem;
+ struct vss_isourcetrack_activity_data_t sourceTrackingResponse;
+ bool sidetone_enable;
+};
+
+struct voice_session_itr {
+ int cur_idx;
+ int session_idx;
+};
+
+void voc_register_mvs_cb(ul_cb_fn ul_cb,
+ dl_cb_fn dl_cb,
+ voip_ssr_cb ssr_cb,
+ void *private_data);
+
+void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb,
+ void *private_data);
+
+void voc_config_vocoder(uint32_t media_type,
+ uint32_t rate,
+ uint32_t network_type,
+ uint32_t dtx_mode,
+ uint32_t evrc_min_rate,
+ uint32_t evrc_max_rate);
+
+enum {
+ DEV_RX = 0,
+ DEV_TX,
+};
+
+enum {
+ RX_PATH = 0,
+ TX_PATH,
+ EC_REF_PATH,
+};
+
+#define VOC_PATH_PASSIVE 0
+#define VOC_PATH_FULL 1
+#define VOC_PATH_VOLTE_PASSIVE 2
+#define VOC_PATH_VOICE2_PASSIVE 3
+#define VOC_PATH_QCHAT_PASSIVE 4
+#define VOC_PATH_VOWLAN_PASSIVE 5
+#define VOC_PATH_VOICEMMODE1_PASSIVE 6
+#define VOC_PATH_VOICEMMODE2_PASSIVE 7
+
+#define MAX_SESSION_NAME_LEN 32
+#define VOICE_SESSION_NAME "Voice session"
+#define VOIP_SESSION_NAME "VoIP session"
+#define VOLTE_SESSION_NAME "VoLTE session"
+#define VOICE2_SESSION_NAME "Voice2 session"
+#define QCHAT_SESSION_NAME "QCHAT session"
+#define VOWLAN_SESSION_NAME "VoWLAN session"
+#define VOICEMMODE1_NAME "VoiceMMode1"
+#define VOICEMMODE2_NAME "VoiceMMode2"
+
+#define VOICE2_SESSION_VSID_STR "10DC1000"
+#define QCHAT_SESSION_VSID_STR "10803000"
+#define VOWLAN_SESSION_VSID_STR "10002000"
+#define VOICEMMODE1_VSID_STR "11C05000"
+#define VOICEMMODE2_VSID_STR "11DC5000"
+#define VOICE_SESSION_VSID 0x10C01000
+#define VOICE2_SESSION_VSID 0x10DC1000
+#define VOLTE_SESSION_VSID 0x10C02000
+#define VOIP_SESSION_VSID 0x10004000
+#define QCHAT_SESSION_VSID 0x10803000
+#define VOWLAN_SESSION_VSID 0x10002000
+#define VOICEMMODE1_VSID 0x11C05000
+#define VOICEMMODE2_VSID 0x11DC5000
+#define ALL_SESSION_VSID 0xFFFFFFFF
+#define VSID_MAX ALL_SESSION_VSID
+
+/* called by alsa driver */
+int voc_set_pp_enable(uint32_t session_id,
+ struct module_instance_info mod_inst_info,
+ uint32_t enable);
+int voc_get_pp_enable(uint32_t session_id,
+ struct module_instance_info mod_inst_info);
+int voc_set_hd_enable(uint32_t session_id, uint32_t enable);
+uint8_t voc_get_tty_mode(uint32_t session_id);
+int voc_set_tty_mode(uint32_t session_id, uint8_t tty_mode);
+int voc_start_voice_call(uint32_t session_id);
+int voc_end_voice_call(uint32_t session_id);
+int voc_standby_voice_call(uint32_t session_id);
+int voc_resume_voice_call(uint32_t session_id);
+int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode);
+int voc_set_rx_vol_step(uint32_t session_id, uint32_t dir, uint32_t vol_step,
+ uint32_t ramp_duration);
+int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+ uint32_t ramp_duration);
+int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+ uint32_t ramp_duration);
+int voc_get_rx_device_mute(uint32_t session_id);
+int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set);
+uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir);
+int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable);
+void voc_disable_dtmf_det_on_active_sessions(void);
+int voc_alloc_cal_shared_memory(void);
+int voc_alloc_voip_shared_memory(void);
+int is_voc_initialized(void);
+int voc_register_vocproc_vol_table(void);
+int voc_deregister_vocproc_vol_table(void);
+int voc_send_cvp_map_vocpcm_memory(uint32_t session_id,
+ struct mem_map_table *tp_mem_table,
+ phys_addr_t paddr, uint32_t bufsize);
+int voc_send_cvp_unmap_vocpcm_memory(uint32_t session_id);
+int voc_send_cvp_start_vocpcm(uint32_t session_id,
+ struct vss_ivpcm_tap_point *vpcm_tp,
+ uint32_t no_of_tp);
+int voc_send_cvp_vocpcm_push_buf_evt(uint32_t session_id,
+ struct vss_ivpcm_evt_push_buffer_v2_t *push_buff_evt);
+int voc_send_cvp_stop_vocpcm(uint32_t session_id);
+void voc_register_hpcm_evt_cb(hostpcm_cb_fn hostpcm_cb,
+ void *private_data);
+void voc_deregister_hpcm_evt_cb(void);
+
+int voc_map_rtac_block(struct rtac_cal_block_data *cal_block);
+int voc_unmap_rtac_block(uint32_t *mem_map_handle);
+
+uint32_t voc_get_session_id(char *name);
+
+int voc_start_playback(uint32_t set, uint16_t port_id);
+int voc_start_record(uint32_t port_id, uint32_t set, uint32_t session_id);
+int voice_get_idx_for_session(u32 session_id);
+int voc_set_ext_ec_ref_port_id(uint16_t port_id, bool state);
+int voc_get_ext_ec_ref_port_id(void);
+int voc_set_ext_ec_ref_media_fmt_info(struct media_format_info *finfo);
+int voc_update_amr_vocoder_rate(uint32_t session_id);
+int voc_disable_device(uint32_t session_id);
+int voc_enable_device(uint32_t session_id);
+void voc_set_destroy_cvd_flag(bool is_destroy_cvd);
+void voc_set_vote_bms_flag(bool is_vote_bms);
+int voc_disable_topology(uint32_t session_id, uint32_t disable);
+int voc_set_device_config(uint32_t session_id, uint8_t path_dir,
+ struct media_format_info *finfo);
+uint32_t voice_get_topology(uint32_t topology_idx);
+int voc_set_sound_focus(struct sound_focus_param sound_focus_param);
+int voc_get_sound_focus(struct sound_focus_param *soundFocusData);
+int voc_get_source_tracking(struct source_tracking_param *sourceTrackingData);
+int voc_set_afe_sidetone(uint32_t session_id, bool sidetone_enable);
+bool voc_get_afe_sidetone(void);
+#endif
diff --git a/sound/soc/msm/qdsp6v2/rtac.c b/sound/soc/msm/qdsp6v2/rtac.c
new file mode 100644
index 000000000000..5572a3dfadc1
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/rtac.c
@@ -0,0 +1,2075 @@
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/msm_audio_calibration.h>
+#include <linux/atomic.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/qdsp6v2/rtac.h>
+#include <linux/compat.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6adm-v2.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6common.h>
+#include "q6voice.h"
+#include "msm-pcm-routing-v2.h"
+#include <sound/adsp_err.h>
+
+
+/* Max size of payload (buf size - apr header) */
+#define MAX_PAYLOAD_SIZE 4076
+#define RTAC_MAX_ACTIVE_VOICE_COMBOS 2
+#define RTAC_MAX_ACTIVE_POPP 8
+#define RTAC_BUF_SIZE 163840
+
+#define TIMEOUT_MS 1000
+
+struct rtac_cal_block_data rtac_cal[MAX_RTAC_BLOCKS] = {
+/* ADM_RTAC_CAL */
+ {{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} },
+/* ASM_RTAC_CAL */
+ {{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} },
+/* VOICE_RTAC_CAL */
+ {{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} },
+/* AFE_RTAC_CAL */
+ {{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} }
+};
+
+struct rtac_common_data {
+ atomic_t usage_count;
+ atomic_t apr_err_code;
+ struct mutex rtac_fops_mutex;
+};
+
+static struct rtac_common_data rtac_common;
+
+/* APR data */
+struct rtac_apr_data {
+ void *apr_handle;
+ atomic_t cmd_state;
+ wait_queue_head_t cmd_wait;
+};
+
+static struct rtac_apr_data rtac_adm_apr_data;
+static struct rtac_apr_data rtac_asm_apr_data[ASM_ACTIVE_STREAMS_ALLOWED + 1];
+static struct rtac_apr_data rtac_afe_apr_data;
+static struct rtac_apr_data rtac_voice_apr_data[RTAC_VOICE_MODES];
+
+/* ADM info & APR */
+static struct rtac_adm rtac_adm_data;
+static u32 *rtac_adm_buffer;
+
+
+/* ASM APR */
+static u32 *rtac_asm_buffer;
+
+static u32 *rtac_afe_buffer;
+
+/* Voice info & APR */
+struct rtac_voice_data_t {
+ uint32_t tx_topology_id;
+ uint32_t rx_topology_id;
+ uint32_t tx_afe_topology;
+ uint32_t rx_afe_topology;
+ uint32_t tx_afe_port;
+ uint32_t rx_afe_port;
+ uint16_t cvs_handle;
+ uint16_t cvp_handle;
+ uint32_t tx_acdb_id;
+ uint32_t rx_acdb_id;
+};
+
+struct rtac_voice {
+ uint32_t num_of_voice_combos;
+ struct rtac_voice_data_t voice[RTAC_MAX_ACTIVE_VOICE_COMBOS];
+};
+
+struct rtac_afe_user_data {
+ uint32_t buf_size;
+ uint32_t cmd_size;
+ uint32_t port_id;
+ union {
+ struct afe_rtac_user_data_set_v2 v2_set;
+ struct afe_rtac_user_data_set_v3 v3_set;
+ struct afe_rtac_user_data_get_v2 v2_get;
+ struct afe_rtac_user_data_get_v3 v3_get;
+ };
+} __packed;
+
+static struct rtac_voice rtac_voice_data;
+static u32 *rtac_voice_buffer;
+static u32 voice_session_id[RTAC_MAX_ACTIVE_VOICE_COMBOS];
+
+
+struct mutex rtac_adm_mutex;
+struct mutex rtac_adm_apr_mutex;
+struct mutex rtac_asm_apr_mutex;
+struct mutex rtac_voice_mutex;
+struct mutex rtac_voice_apr_mutex;
+struct mutex rtac_afe_apr_mutex;
+
+int rtac_clear_mapping(uint32_t cal_type)
+{
+ int result = 0;
+ pr_debug("%s\n", __func__);
+
+ if (cal_type >= MAX_RTAC_BLOCKS) {
+ pr_debug("%s: invalid cal type %d\n", __func__, cal_type);
+ result = -EINVAL;
+ goto done;
+ }
+
+ rtac_cal[cal_type].map_data.map_handle = 0;
+done:
+ return result;
+}
+
+int rtac_allocate_cal_buffer(uint32_t cal_type)
+{
+ int result = 0;
+ size_t len;
+ pr_debug("%s\n", __func__);
+
+ if (cal_type >= MAX_RTAC_BLOCKS) {
+ pr_err("%s: cal_type %d is invalid!\n",
+ __func__, cal_type);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (rtac_cal[cal_type].cal_data.paddr != 0) {
+ pr_err("%s: memory already allocated! cal_type %d, paddr 0x%pK\n",
+ __func__, cal_type, &rtac_cal[cal_type].cal_data.paddr);
+ result = -EPERM;
+ goto done;
+ }
+
+ result = msm_audio_ion_alloc("rtac_client",
+ &rtac_cal[cal_type].map_data.ion_client,
+ &rtac_cal[cal_type].map_data.ion_handle,
+ rtac_cal[cal_type].map_data.map_size,
+ &rtac_cal[cal_type].cal_data.paddr,
+ &len,
+ &rtac_cal[cal_type].cal_data.kvaddr);
+ if (result < 0) {
+ pr_err("%s: ION create client for RTAC failed\n",
+ __func__);
+ goto done;
+ }
+
+ pr_debug("%s: cal_type %d, paddr 0x%pK, kvaddr 0x%pK, map_size 0x%x\n",
+ __func__, cal_type,
+ &rtac_cal[cal_type].cal_data.paddr,
+ rtac_cal[cal_type].cal_data.kvaddr,
+ rtac_cal[cal_type].map_data.map_size);
+done:
+ return result;
+}
+
+int rtac_free_cal_buffer(uint32_t cal_type)
+{
+ int result = 0;
+ pr_debug("%s\n", __func__);
+
+ if (cal_type >= MAX_RTAC_BLOCKS) {
+ pr_err("%s: cal_type %d is invalid!\n",
+ __func__, cal_type);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (rtac_cal[cal_type].map_data.ion_client == NULL) {
+ pr_debug("%s: cal_type %d not allocated!\n",
+ __func__, cal_type);
+ goto done;
+ }
+
+ result = msm_audio_ion_free(rtac_cal[cal_type].map_data.ion_client,
+ rtac_cal[cal_type].map_data.ion_handle);
+ if (result < 0) {
+ pr_err("%s: ION free for RTAC failed! cal_type %d, paddr 0x%pK\n",
+ __func__, cal_type, &rtac_cal[cal_type].cal_data.paddr);
+ goto done;
+ }
+
+ rtac_cal[cal_type].map_data.map_handle = 0;
+ rtac_cal[cal_type].map_data.ion_client = NULL;
+ rtac_cal[cal_type].map_data.ion_handle = NULL;
+ rtac_cal[cal_type].cal_data.size = 0;
+ rtac_cal[cal_type].cal_data.kvaddr = 0;
+ rtac_cal[cal_type].cal_data.paddr = 0;
+done:
+ return result;
+}
+
+int rtac_map_cal_buffer(uint32_t cal_type)
+{
+ int result = 0;
+ pr_debug("%s\n", __func__);
+
+ if (cal_type >= MAX_RTAC_BLOCKS) {
+ pr_err("%s: cal_type %d is invalid!\n",
+ __func__, cal_type);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (rtac_cal[cal_type].map_data.map_handle != 0) {
+ pr_err("%s: already mapped cal_type %d\n",
+ __func__, cal_type);
+ result = -EPERM;
+ goto done;
+ }
+
+ if (rtac_cal[cal_type].cal_data.paddr == 0) {
+ pr_err("%s: physical address is NULL cal_type %d\n",
+ __func__, cal_type);
+ result = -EPERM;
+ goto done;
+ }
+
+ switch (cal_type) {
+ case ADM_RTAC_CAL:
+ result = adm_map_rtac_block(&rtac_cal[cal_type]);
+ break;
+ case ASM_RTAC_CAL:
+ result = q6asm_map_rtac_block(&rtac_cal[cal_type]);
+ break;
+ case VOICE_RTAC_CAL:
+ result = voc_map_rtac_block(&rtac_cal[cal_type]);
+ break;
+ case AFE_RTAC_CAL:
+ result = afe_map_rtac_block(&rtac_cal[cal_type]);
+ break;
+ }
+ if (result < 0) {
+ pr_err("%s: map RTAC failed! cal_type %d\n",
+ __func__, cal_type);
+ goto done;
+ }
+done:
+ return result;
+}
+
+int rtac_unmap_cal_buffer(uint32_t cal_type)
+{
+ int result = 0;
+ pr_debug("%s\n", __func__);
+
+ if (cal_type >= MAX_RTAC_BLOCKS) {
+ pr_err("%s: cal_type %d is invalid!\n",
+ __func__, cal_type);
+ result = -EINVAL;
+ goto done;
+ }
+
+ if (rtac_cal[cal_type].map_data.map_handle == 0) {
+ pr_debug("%s: nothing to unmap cal_type %d\n",
+ __func__, cal_type);
+ goto done;
+ }
+
+ switch (cal_type) {
+ case ADM_RTAC_CAL:
+ result = adm_unmap_rtac_block(
+ &rtac_cal[cal_type].map_data.map_handle);
+ break;
+ case ASM_RTAC_CAL:
+ result = q6asm_unmap_rtac_block(
+ &rtac_cal[cal_type].map_data.map_handle);
+ break;
+ case VOICE_RTAC_CAL:
+ result = voc_unmap_rtac_block(
+ &rtac_cal[cal_type].map_data.map_handle);
+ break;
+ case AFE_RTAC_CAL:
+ result = afe_unmap_rtac_block(
+ &rtac_cal[cal_type].map_data.map_handle);
+ break;
+ }
+ if (result < 0) {
+ pr_err("%s: unmap RTAC failed! cal_type %d\n",
+ __func__, cal_type);
+ goto done;
+ }
+done:
+ return result;
+}
+
+static int rtac_open(struct inode *inode, struct file *f)
+{
+ int result = 0;
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&rtac_common.rtac_fops_mutex);
+ atomic_inc(&rtac_common.usage_count);
+ mutex_unlock(&rtac_common.rtac_fops_mutex);
+ return result;
+}
+
+static int rtac_release(struct inode *inode, struct file *f)
+{
+ int result = 0;
+ int result2 = 0;
+ int i;
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&rtac_common.rtac_fops_mutex);
+ atomic_dec(&rtac_common.usage_count);
+ pr_debug("%s: ref count %d!\n", __func__,
+ atomic_read(&rtac_common.usage_count));
+
+ if (atomic_read(&rtac_common.usage_count) > 0) {
+ mutex_unlock(&rtac_common.rtac_fops_mutex);
+ goto done;
+ }
+
+ for (i = 0; i < MAX_RTAC_BLOCKS; i++) {
+ result2 = rtac_unmap_cal_buffer(i);
+ if (result2 < 0) {
+ pr_err("%s: unmap buffer failed! error %d!\n",
+ __func__, result2);
+ result = result2;
+ }
+
+ result2 = rtac_free_cal_buffer(i);
+ if (result2 < 0) {
+ pr_err("%s: free buffer failed! error %d!\n",
+ __func__, result2);
+ result = result2;
+ }
+ }
+ mutex_unlock(&rtac_common.rtac_fops_mutex);
+done:
+ return result;
+}
+
+
+/* ADM Info */
+void add_popp(u32 dev_idx, u32 port_id, u32 popp_id)
+{
+ u32 i = 0;
+
+ for (; i < rtac_adm_data.device[dev_idx].num_of_popp; i++)
+ if (rtac_adm_data.device[dev_idx].popp[i].popp == popp_id)
+ goto done;
+
+ if (rtac_adm_data.device[dev_idx].num_of_popp ==
+ RTAC_MAX_ACTIVE_POPP) {
+ pr_err("%s, Max POPP!\n", __func__);
+ goto done;
+ }
+ rtac_adm_data.device[dev_idx].popp[
+ rtac_adm_data.device[dev_idx].num_of_popp].popp = popp_id;
+ rtac_adm_data.device[dev_idx].popp[
+ rtac_adm_data.device[dev_idx].num_of_popp].popp_topology =
+ q6asm_get_asm_topology(popp_id);
+ rtac_adm_data.device[dev_idx].popp[
+ rtac_adm_data.device[dev_idx].num_of_popp++].app_type =
+ q6asm_get_asm_app_type(popp_id);
+
+ pr_debug("%s: popp_id = %d, popp topology = 0x%x, popp app type = 0x%x\n",
+ __func__,
+ rtac_adm_data.device[dev_idx].popp[
+ rtac_adm_data.device[dev_idx].num_of_popp - 1].popp,
+ rtac_adm_data.device[dev_idx].popp[
+ rtac_adm_data.device[dev_idx].num_of_popp - 1].popp_topology,
+ rtac_adm_data.device[dev_idx].popp[
+ rtac_adm_data.device[dev_idx].num_of_popp - 1].app_type);
+done:
+ return;
+}
+
+void rtac_update_afe_topology(u32 port_id)
+{
+ u32 i = 0;
+
+ mutex_lock(&rtac_adm_mutex);
+ for (i = 0; i < rtac_adm_data.num_of_dev; i++) {
+ if (rtac_adm_data.device[i].afe_port == port_id) {
+ rtac_adm_data.device[i].afe_topology =
+ afe_get_topology(port_id);
+ pr_debug("%s: port_id = 0x%x topology_id = 0x%x copp_id = %d\n",
+ __func__, port_id,
+ rtac_adm_data.device[i].afe_topology,
+ rtac_adm_data.device[i].copp);
+ }
+ }
+ mutex_unlock(&rtac_adm_mutex);
+}
+
+void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id,
+ u32 app_type, u32 acdb_id)
+{
+ u32 i = 0;
+ pr_debug("%s: num rtac devices %d port_id = %d, copp_id = %d\n",
+ __func__, rtac_adm_data.num_of_dev, port_id, copp_id);
+
+ mutex_lock(&rtac_adm_mutex);
+ if (rtac_adm_data.num_of_dev == RTAC_MAX_ACTIVE_DEVICES) {
+ pr_err("%s, Can't add anymore RTAC devices!\n", __func__);
+ goto done;
+ }
+
+ /* Check if device already added */
+ if (rtac_adm_data.num_of_dev != 0) {
+ for (; i < rtac_adm_data.num_of_dev; i++) {
+ if (rtac_adm_data.device[i].afe_port == port_id &&
+ rtac_adm_data.device[i].copp == copp_id) {
+ add_popp(i, port_id, popp_id);
+ goto done;
+ }
+ if (rtac_adm_data.device[i].num_of_popp ==
+ RTAC_MAX_ACTIVE_POPP) {
+ pr_err("%s, Max POPP!\n", __func__);
+ goto done;
+ }
+ }
+ }
+
+ /* Add device */
+ rtac_adm_data.num_of_dev++;
+
+ rtac_adm_data.device[i].topology_id =
+ adm_get_topology_for_port_from_copp_id(port_id, copp_id);
+ rtac_adm_data.device[i].afe_topology =
+ afe_get_topology(port_id);
+ rtac_adm_data.device[i].afe_port = port_id;
+ rtac_adm_data.device[i].copp = copp_id;
+ rtac_adm_data.device[i].app_type = app_type;
+ rtac_adm_data.device[i].acdb_dev_id = acdb_id;
+ rtac_adm_data.device[i].popp[
+ rtac_adm_data.device[i].num_of_popp].popp = popp_id;
+ rtac_adm_data.device[i].popp[
+ rtac_adm_data.device[i].num_of_popp].popp_topology =
+ q6asm_get_asm_topology(popp_id);
+ rtac_adm_data.device[i].popp[
+ rtac_adm_data.device[i].num_of_popp++].app_type =
+ q6asm_get_asm_app_type(popp_id);
+
+ pr_debug("%s: topology = 0x%x, afe_topology = 0x%x, port_id = %d, copp_id = %d, app id = 0x%x, acdb id = %d, popp_id = %d, popp topology = 0x%x, popp app type = 0x%x\n",
+ __func__,
+ rtac_adm_data.device[i].topology_id,
+ rtac_adm_data.device[i].afe_topology,
+ rtac_adm_data.device[i].afe_port,
+ rtac_adm_data.device[i].copp,
+ rtac_adm_data.device[i].app_type,
+ rtac_adm_data.device[i].acdb_dev_id,
+ rtac_adm_data.device[i].popp[
+ rtac_adm_data.device[i].num_of_popp - 1].popp,
+ rtac_adm_data.device[i].popp[
+ rtac_adm_data.device[i].num_of_popp - 1].popp_topology,
+ rtac_adm_data.device[i].popp[
+ rtac_adm_data.device[i].num_of_popp - 1].app_type);
+done:
+ mutex_unlock(&rtac_adm_mutex);
+ return;
+}
+
+static void shift_adm_devices(u32 dev_idx)
+{
+ for (; dev_idx < rtac_adm_data.num_of_dev; dev_idx++) {
+ memcpy(&rtac_adm_data.device[dev_idx],
+ &rtac_adm_data.device[dev_idx + 1],
+ sizeof(rtac_adm_data.device[dev_idx]));
+ memset(&rtac_adm_data.device[dev_idx + 1], 0,
+ sizeof(rtac_adm_data.device[dev_idx]));
+ }
+}
+
+static void shift_popp(u32 copp_idx, u32 popp_idx)
+{
+ for (; popp_idx < rtac_adm_data.device[copp_idx].num_of_popp;
+ popp_idx++) {
+ memcpy(&rtac_adm_data.device[copp_idx].popp[popp_idx].popp,
+ &rtac_adm_data.device[copp_idx].popp[popp_idx + 1].
+ popp, sizeof(uint32_t));
+ memcpy(&rtac_adm_data.device[copp_idx].popp[popp_idx].
+ popp_topology,
+ &rtac_adm_data.device[copp_idx].popp[popp_idx + 1].
+ popp_topology,
+ sizeof(uint32_t));
+ memset(&rtac_adm_data.device[copp_idx].popp[popp_idx + 1].
+ popp, 0, sizeof(uint32_t));
+ memset(&rtac_adm_data.device[copp_idx].popp[popp_idx + 1].
+ popp_topology, 0, sizeof(uint32_t));
+ }
+}
+
+void rtac_remove_adm_device(u32 port_id, u32 copp_id)
+{
+ s32 i;
+ pr_debug("%s: num rtac devices %d port_id = %d, copp_id = %d\n",
+ __func__, rtac_adm_data.num_of_dev, port_id, copp_id);
+
+ mutex_lock(&rtac_adm_mutex);
+ /* look for device */
+ for (i = 0; i < rtac_adm_data.num_of_dev; i++) {
+ if (rtac_adm_data.device[i].afe_port == port_id &&
+ rtac_adm_data.device[i].copp == copp_id) {
+ memset(&rtac_adm_data.device[i], 0,
+ sizeof(rtac_adm_data.device[i]));
+ rtac_adm_data.num_of_dev--;
+
+ if (rtac_adm_data.num_of_dev >= 1) {
+ shift_adm_devices(i);
+ break;
+ }
+ }
+ }
+
+ mutex_unlock(&rtac_adm_mutex);
+ return;
+}
+
+void rtac_remove_popp_from_adm_devices(u32 popp_id)
+{
+ s32 i, j;
+ pr_debug("%s: popp_id = %d\n", __func__, popp_id);
+
+ mutex_lock(&rtac_adm_mutex);
+ for (i = 0; i < rtac_adm_data.num_of_dev; i++) {
+ for (j = 0; j < rtac_adm_data.device[i].num_of_popp; j++) {
+ if (rtac_adm_data.device[i].popp[j].popp ==
+ popp_id) {
+ rtac_adm_data.device[i].popp[j].popp = 0;
+ rtac_adm_data.device[i].popp[j].
+ popp_topology = 0;
+ rtac_adm_data.device[i].num_of_popp--;
+ shift_popp(i, j);
+ }
+ }
+ }
+ mutex_unlock(&rtac_adm_mutex);
+}
+
+
+/* Voice Info */
+static void set_rtac_voice_data(int idx, u32 cvs_handle, u32 cvp_handle,
+ u32 rx_afe_port, u32 tx_afe_port,
+ u32 rx_acdb_id, u32 tx_acdb_id,
+ u32 session_id)
+{
+ rtac_voice_data.voice[idx].tx_topology_id =
+ voice_get_topology(CVP_VOC_TX_TOPOLOGY_CAL);
+ rtac_voice_data.voice[idx].rx_topology_id =
+ voice_get_topology(CVP_VOC_RX_TOPOLOGY_CAL);
+ rtac_voice_data.voice[idx].tx_afe_topology =
+ afe_get_topology(tx_afe_port);
+ rtac_voice_data.voice[idx].rx_afe_topology =
+ afe_get_topology(rx_afe_port);
+ rtac_voice_data.voice[idx].tx_afe_port = tx_afe_port;
+ rtac_voice_data.voice[idx].rx_afe_port = rx_afe_port;
+ rtac_voice_data.voice[idx].tx_acdb_id = tx_acdb_id;
+ rtac_voice_data.voice[idx].rx_acdb_id = rx_acdb_id;
+ rtac_voice_data.voice[idx].cvs_handle = cvs_handle;
+ rtac_voice_data.voice[idx].cvp_handle = cvp_handle;
+ pr_debug("%s\n%s: %x\n%s: %d %s: %d\n%s: %d %s: %d\n %s: %d\n %s: %d\n%s: %d %s: %d\n%s",
+ "<---- Voice Data Info ---->", "Session id", session_id,
+ "cvs_handle", cvs_handle, "cvp_handle", cvp_handle,
+ "rx_afe_topology", rtac_voice_data.voice[idx].rx_afe_topology,
+ "tx_afe_topology", rtac_voice_data.voice[idx].tx_afe_topology,
+ "rx_afe_port", rx_afe_port, "tx_afe_port", tx_afe_port,
+ "rx_acdb_id", rx_acdb_id, "tx_acdb_id", tx_acdb_id,
+ "<-----------End----------->");
+
+ /* Store session ID for voice RTAC */
+ voice_session_id[idx] = session_id;
+}
+
+void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port,
+ u32 tx_afe_port, u32 rx_acdb_id, u32 tx_acdb_id,
+ u32 session_id)
+{
+ u32 i = 0;
+ pr_debug("%s\n", __func__);
+ mutex_lock(&rtac_voice_mutex);
+
+ if (rtac_voice_data.num_of_voice_combos ==
+ RTAC_MAX_ACTIVE_VOICE_COMBOS) {
+ pr_err("%s, Can't add anymore RTAC devices!\n", __func__);
+ goto done;
+ }
+
+ /* Check if device already added */
+ if (rtac_voice_data.num_of_voice_combos != 0) {
+ for (; i < rtac_voice_data.num_of_voice_combos; i++) {
+ if (rtac_voice_data.voice[i].cvs_handle ==
+ cvs_handle) {
+ set_rtac_voice_data(i, cvs_handle, cvp_handle,
+ rx_afe_port, tx_afe_port, rx_acdb_id,
+ tx_acdb_id, session_id);
+ goto done;
+ }
+ }
+ }
+
+ /* Add device */
+ rtac_voice_data.num_of_voice_combos++;
+ set_rtac_voice_data(i, cvs_handle, cvp_handle,
+ rx_afe_port, tx_afe_port,
+ rx_acdb_id, tx_acdb_id,
+ session_id);
+done:
+ mutex_unlock(&rtac_voice_mutex);
+ return;
+}
+
+static void shift_voice_devices(u32 idx)
+{
+ for (; idx < rtac_voice_data.num_of_voice_combos - 1; idx++) {
+ memcpy(&rtac_voice_data.voice[idx],
+ &rtac_voice_data.voice[idx + 1],
+ sizeof(rtac_voice_data.voice[idx]));
+ voice_session_id[idx] = voice_session_id[idx + 1];
+ }
+}
+
+void rtac_remove_voice(u32 cvs_handle)
+{
+ u32 i = 0;
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&rtac_voice_mutex);
+ /* look for device */
+ for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+ if (rtac_voice_data.voice[i].cvs_handle == cvs_handle) {
+ shift_voice_devices(i);
+ rtac_voice_data.num_of_voice_combos--;
+ memset(&rtac_voice_data.voice[
+ rtac_voice_data.num_of_voice_combos], 0,
+ sizeof(rtac_voice_data.voice
+ [rtac_voice_data.num_of_voice_combos]));
+ voice_session_id[rtac_voice_data.num_of_voice_combos]
+ = 0;
+ break;
+ }
+ }
+ mutex_unlock(&rtac_voice_mutex);
+ return;
+}
+
+static u32 get_voice_session_id_cvs(u32 cvs_handle)
+{
+ u32 i;
+
+ for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+ if (rtac_voice_data.voice[i].cvs_handle == cvs_handle)
+ return voice_session_id[i];
+ }
+
+ pr_err("%s: No voice index for CVS handle %d found returning 0\n",
+ __func__, cvs_handle);
+ return 0;
+}
+
+static u32 get_voice_session_id_cvp(u32 cvp_handle)
+{
+ u32 i;
+
+ for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+ if (rtac_voice_data.voice[i].cvp_handle == cvp_handle)
+ return voice_session_id[i];
+ }
+
+ pr_err("%s: No voice index for CVP handle %d found returning 0\n",
+ __func__, cvp_handle);
+ return 0;
+}
+
+static int get_voice_index(u32 mode, u32 handle)
+{
+ if (mode == RTAC_CVP)
+ return voice_get_idx_for_session(
+ get_voice_session_id_cvp(handle));
+ if (mode == RTAC_CVS)
+ return voice_get_idx_for_session(
+ get_voice_session_id_cvs(handle));
+
+ pr_err("%s: Invalid mode %d, returning 0\n",
+ __func__, mode);
+ return 0;
+}
+
+
+/* ADM APR */
+void rtac_set_adm_handle(void *handle)
+{
+ pr_debug("%s: handle = %pK\n", __func__, handle);
+
+ mutex_lock(&rtac_adm_apr_mutex);
+ rtac_adm_apr_data.apr_handle = handle;
+ mutex_unlock(&rtac_adm_apr_mutex);
+}
+
+bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size)
+{
+ pr_debug("%s:cmd_state = %d\n", __func__,
+ atomic_read(&rtac_adm_apr_data.cmd_state));
+ if (atomic_read(&rtac_adm_apr_data.cmd_state) != 1)
+ return false;
+
+ pr_debug("%s\n", __func__);
+ if (payload_size == sizeof(uint32_t))
+ atomic_set(&rtac_common.apr_err_code, payload[0]);
+ else if (payload_size == (2*sizeof(uint32_t)))
+ atomic_set(&rtac_common.apr_err_code, payload[1]);
+
+ atomic_set(&rtac_adm_apr_data.cmd_state, 0);
+ wake_up(&rtac_adm_apr_data.cmd_wait);
+ return true;
+}
+
+int send_adm_apr(void *buf, u32 opcode)
+{
+ s32 result;
+ u32 user_buf_size = 0;
+ u32 bytes_returned = 0;
+ u32 copp_id;
+ u32 payload_size;
+ u32 data_size = 0;
+ int copp_idx;
+ int port_idx;
+ struct apr_hdr adm_params;
+ pr_debug("%s\n", __func__);
+
+ if (rtac_cal[ADM_RTAC_CAL].map_data.ion_handle == NULL) {
+ result = rtac_allocate_cal_buffer(ADM_RTAC_CAL);
+ if (result < 0) {
+ pr_err("%s: allocate buffer failed!",
+ __func__);
+ goto done;
+ }
+ }
+
+ if (rtac_cal[ADM_RTAC_CAL].map_data.map_handle == 0) {
+ result = rtac_map_cal_buffer(ADM_RTAC_CAL);
+ if (result < 0) {
+ pr_err("%s: map buffer failed!",
+ __func__);
+ goto done;
+ }
+ }
+
+ if (copy_from_user(&user_buf_size, (void *)buf,
+ sizeof(user_buf_size))) {
+ pr_err("%s: Copy from user failed! buf = 0x%pK\n",
+ __func__, buf);
+ goto done;
+ }
+ if (user_buf_size <= 0) {
+ pr_err("%s: Invalid buffer size = %d\n",
+ __func__, user_buf_size);
+ goto done;
+ }
+
+ if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy payload size from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ if (copy_from_user(&copp_id, buf + 2 * sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy port id from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ if (adm_get_indexes_from_copp_id(copp_id, &copp_idx, &port_idx) != 0) {
+ pr_err("%s: Copp Id-%d is not active\n", __func__, copp_id);
+ goto done;
+ }
+
+ mutex_lock(&rtac_adm_apr_mutex);
+ if (rtac_adm_apr_data.apr_handle == NULL) {
+ pr_err("%s: APR not initialized\n", __func__);
+ result = -EINVAL;
+ goto err;
+ }
+
+ switch (opcode) {
+ case ADM_CMD_SET_PP_PARAMS_V5:
+ case ADM_CMD_SET_PP_PARAMS_V6:
+ /* set payload size to in-band payload */
+ /* set data size to actual out of band payload size */
+ data_size = payload_size - 4 * sizeof(u32);
+ if (data_size > rtac_cal[ADM_RTAC_CAL].map_data.map_size) {
+ pr_err("%s: Invalid data size = %d\n",
+ __func__, data_size);
+ result = -EINVAL;
+ goto err;
+ }
+ payload_size = 4 * sizeof(u32);
+
+ /* Copy buffer to out-of-band payload */
+ if (copy_from_user((void *)
+ rtac_cal[ADM_RTAC_CAL].cal_data.kvaddr,
+ buf + 7 * sizeof(u32), data_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ result = -EFAULT;
+ goto err;
+ }
+
+ /* set payload size in packet */
+ rtac_adm_buffer[8] = data_size;
+ break;
+ case ADM_CMD_GET_PP_PARAMS_V5:
+ case ADM_CMD_GET_PP_PARAMS_V6:
+ if (payload_size > MAX_PAYLOAD_SIZE) {
+ pr_err("%s: Invalid payload size = %d\n",
+ __func__, payload_size);
+ result = -EINVAL;
+ goto err;
+ }
+
+ /* Copy buffer to in-band payload */
+ if (copy_from_user(rtac_adm_buffer +
+ sizeof(adm_params)/sizeof(u32),
+ buf + 3 * sizeof(u32), payload_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ result = -EFAULT;
+ goto err;
+ }
+ break;
+ default:
+ pr_err("%s: Invalid opcode %d\n", __func__, opcode);
+ result = -EINVAL;
+ goto err;
+ }
+
+ /* Pack header */
+ adm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ adm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ payload_size);
+ adm_params.src_svc = APR_SVC_ADM;
+ adm_params.src_domain = APR_DOMAIN_APPS;
+ adm_params.src_port = copp_id;
+ adm_params.dest_svc = APR_SVC_ADM;
+ adm_params.dest_domain = APR_DOMAIN_ADSP;
+ adm_params.dest_port = copp_id;
+ adm_params.token = port_idx << 16 | copp_idx;
+ adm_params.opcode = opcode;
+
+ /* fill for out-of-band */
+ rtac_adm_buffer[5] =
+ lower_32_bits(rtac_cal[ADM_RTAC_CAL].cal_data.paddr);
+ rtac_adm_buffer[6] =
+ msm_audio_populate_upper_32_bits(
+ rtac_cal[ADM_RTAC_CAL].cal_data.paddr);
+ rtac_adm_buffer[7] = rtac_cal[ADM_RTAC_CAL].map_data.map_handle;
+
+ memcpy(rtac_adm_buffer, &adm_params, sizeof(adm_params));
+ atomic_set(&rtac_adm_apr_data.cmd_state, 1);
+
+ pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n",
+ __func__, opcode,
+ &rtac_cal[ADM_RTAC_CAL].cal_data.paddr);
+
+ result = apr_send_pkt(rtac_adm_apr_data.apr_handle,
+ (uint32_t *)rtac_adm_buffer);
+ if (result < 0) {
+ pr_err("%s: Set params failed copp = %d\n", __func__, copp_id);
+ goto err;
+ }
+ /* Wait for the callback */
+ result = wait_event_timeout(rtac_adm_apr_data.cmd_wait,
+ (atomic_read(&rtac_adm_apr_data.cmd_state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!result) {
+ pr_err("%s: Set params timed out copp = %d\n", __func__,
+ copp_id);
+ goto err;
+ }
+ if (atomic_read(&rtac_common.apr_err_code)) {
+ pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n",
+ __func__, adsp_err_get_err_str(atomic_read(
+ &rtac_common.apr_err_code)),
+ opcode);
+ result = adsp_err_get_lnx_err_code(
+ atomic_read(
+ &rtac_common.apr_err_code));
+ goto err;
+ }
+
+ if (opcode == ADM_CMD_GET_PP_PARAMS_V5) {
+ bytes_returned = ((u32 *)rtac_cal[ADM_RTAC_CAL].cal_data.
+ kvaddr)[2] + 3 * sizeof(u32);
+ } else if (opcode == ADM_CMD_GET_PP_PARAMS_V6) {
+ bytes_returned =
+ ((u32 *) rtac_cal[ADM_RTAC_CAL].cal_data.kvaddr)[3] +
+ 4 * sizeof(u32);
+ } else {
+ bytes_returned = data_size;
+ goto unlock;
+ }
+
+ if (bytes_returned > rtac_cal[ADM_RTAC_CAL].map_data.map_size) {
+ pr_err("%s: Invalid data size = %d\n", __func__,
+ bytes_returned);
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (bytes_returned > user_buf_size) {
+ pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n",
+ __func__, user_buf_size, bytes_returned);
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (copy_to_user((void __user *) buf,
+ rtac_cal[ADM_RTAC_CAL].cal_data.kvaddr,
+ bytes_returned)) {
+ pr_err("%s: Could not copy buffer to user,size = %d\n",
+ __func__, bytes_returned);
+ result = -EFAULT;
+ goto err;
+ }
+
+unlock:
+ mutex_unlock(&rtac_adm_apr_mutex);
+done:
+ return bytes_returned;
+err:
+ mutex_unlock(&rtac_adm_apr_mutex);
+ return result;
+}
+
+
+/* ASM APR */
+void rtac_set_asm_handle(u32 session_id, void *handle)
+{
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&rtac_asm_apr_mutex);
+ if (rtac_asm_apr_data[session_id].apr_handle)
+ rtac_asm_apr_data[session_id].apr_handle = handle;
+ mutex_unlock(&rtac_asm_apr_mutex);
+}
+
+bool rtac_make_asm_callback(u32 session_id, uint32_t *payload,
+ u32 payload_size)
+{
+ if (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) != 1)
+ return false;
+
+ pr_debug("%s\n", __func__);
+ if (payload_size == sizeof(uint32_t))
+ atomic_set(&rtac_common.apr_err_code, payload[0]);
+ else if (payload_size == (2*sizeof(uint32_t)))
+ atomic_set(&rtac_common.apr_err_code, payload[1]);
+
+ atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 0);
+ wake_up(&rtac_asm_apr_data[session_id].cmd_wait);
+ return true;
+}
+
+int send_rtac_asm_apr(void *buf, u32 opcode)
+{
+ s32 result;
+ u32 user_buf_size = 0;
+ u32 bytes_returned = 0;
+ u32 session_id = 0;
+ u32 payload_size;
+ u32 data_size = 0;
+ struct apr_hdr asm_params;
+ pr_debug("%s\n", __func__);
+
+ if (rtac_cal[ASM_RTAC_CAL].map_data.ion_handle == NULL) {
+ result = rtac_allocate_cal_buffer(ASM_RTAC_CAL);
+ if (result < 0) {
+ pr_err("%s: allocate buffer failed!",
+ __func__);
+ goto done;
+ }
+ }
+
+ if (rtac_cal[ASM_RTAC_CAL].map_data.map_handle == 0) {
+ result = rtac_map_cal_buffer(ASM_RTAC_CAL);
+ if (result < 0) {
+ pr_err("%s: map buffer failed!",
+ __func__);
+ goto done;
+ }
+ }
+
+ if (copy_from_user(&user_buf_size, (void *)buf,
+ sizeof(user_buf_size))) {
+ pr_err("%s: Copy from user failed! buf = 0x%pK\n",
+ __func__, buf);
+ goto done;
+ }
+ if (user_buf_size <= 0) {
+ pr_err("%s: Invalid buffer size = %d\n",
+ __func__, user_buf_size);
+ goto done;
+ }
+
+ if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy payload size from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ if (copy_from_user(&session_id, buf + 2 * sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy session id from user buffer\n",
+ __func__);
+ goto done;
+ }
+ if (session_id >= (ASM_ACTIVE_STREAMS_ALLOWED + 1)) {
+ pr_err("%s: Invalid Session = %d\n", __func__, session_id);
+ goto done;
+ }
+
+ mutex_lock(&rtac_asm_apr_mutex);
+ if (rtac_asm_apr_data[session_id].apr_handle == NULL) {
+ pr_err("%s: APR not initialized\n", __func__);
+ result = -EINVAL;
+ goto err;
+ }
+
+ switch (opcode) {
+ case ASM_STREAM_CMD_SET_PP_PARAMS_V2:
+ case ASM_STREAM_CMD_SET_PP_PARAMS_V3:
+ /* set payload size to in-band payload */
+ /* set data size to actual out of band payload size */
+ data_size = payload_size - 4 * sizeof(u32);
+ if (data_size > rtac_cal[ASM_RTAC_CAL].map_data.map_size) {
+ pr_err("%s: Invalid data size = %d\n",
+ __func__, data_size);
+ result = -EINVAL;
+ goto err;
+ }
+ payload_size = 4 * sizeof(u32);
+
+ /* Copy buffer to out-of-band payload */
+ if (copy_from_user((void *)
+ rtac_cal[ASM_RTAC_CAL].cal_data.kvaddr,
+ buf + 7 * sizeof(u32), data_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ result = -EFAULT;
+ goto err;
+ }
+ /* set payload size in packet */
+ rtac_asm_buffer[8] = data_size;
+ break;
+ case ASM_STREAM_CMD_GET_PP_PARAMS_V2:
+ case ASM_STREAM_CMD_GET_PP_PARAMS_V3:
+ if (payload_size > MAX_PAYLOAD_SIZE) {
+ pr_err("%s: Invalid payload size = %d\n",
+ __func__, payload_size);
+ result = -EINVAL;
+ goto err;
+ }
+
+ /* Copy buffer to in-band payload */
+ if (copy_from_user(rtac_asm_buffer +
+ sizeof(asm_params)/sizeof(u32),
+ buf + 3 * sizeof(u32), payload_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ result = -EFAULT;
+ goto err;
+ }
+
+ break;
+ default:
+ pr_err("%s: Invalid opcode %d\n", __func__, opcode);
+ result = -EINVAL;
+ goto err;
+ }
+
+ /* Pack header */
+ asm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ asm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ payload_size);
+ asm_params.src_svc = q6asm_get_apr_service_id(session_id);
+ asm_params.src_domain = APR_DOMAIN_APPS;
+ asm_params.src_port = (session_id << 8) | 0x0001;
+ asm_params.dest_svc = APR_SVC_ASM;
+ asm_params.dest_domain = APR_DOMAIN_ADSP;
+ asm_params.dest_port = (session_id << 8) | 0x0001;
+ asm_params.token = session_id;
+ asm_params.opcode = opcode;
+
+ /* fill for out-of-band */
+ rtac_asm_buffer[5] =
+ lower_32_bits(rtac_cal[ASM_RTAC_CAL].cal_data.paddr);
+ rtac_asm_buffer[6] =
+ msm_audio_populate_upper_32_bits(
+ rtac_cal[ASM_RTAC_CAL].cal_data.paddr);
+ rtac_asm_buffer[7] = rtac_cal[ASM_RTAC_CAL].map_data.map_handle;
+
+ memcpy(rtac_asm_buffer, &asm_params, sizeof(asm_params));
+ atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 1);
+
+ pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n",
+ __func__, opcode,
+ &rtac_cal[ASM_RTAC_CAL].cal_data.paddr);
+
+ result = apr_send_pkt(rtac_asm_apr_data[session_id].apr_handle,
+ (uint32_t *)rtac_asm_buffer);
+ if (result < 0) {
+ pr_err("%s: Set params failed session = %d\n",
+ __func__, session_id);
+ goto err;
+ }
+
+ /* Wait for the callback */
+ result = wait_event_timeout(rtac_asm_apr_data[session_id].cmd_wait,
+ (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) == 0),
+ 5 * HZ);
+ if (!result) {
+ pr_err("%s: Set params timed out session = %d\n",
+ __func__, session_id);
+ goto err;
+ }
+ if (atomic_read(&rtac_common.apr_err_code)) {
+ pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n",
+ __func__, adsp_err_get_err_str(atomic_read(
+ &rtac_common.apr_err_code)),
+ opcode);
+ result = adsp_err_get_lnx_err_code(
+ atomic_read(
+ &rtac_common.apr_err_code));
+ goto err;
+ }
+
+ if (opcode == ASM_STREAM_CMD_GET_PP_PARAMS_V2) {
+ bytes_returned = ((u32 *)rtac_cal[ASM_RTAC_CAL].cal_data.
+ kvaddr)[2] + 3 * sizeof(u32);
+ } else if (opcode == ASM_STREAM_CMD_GET_PP_PARAMS_V3) {
+ bytes_returned =
+ ((u32 *) rtac_cal[ASM_RTAC_CAL].cal_data.kvaddr)[3] +
+ 4 * sizeof(u32);
+ } else {
+ bytes_returned = data_size;
+ goto unlock;
+ }
+
+ if (bytes_returned > rtac_cal[ASM_RTAC_CAL].map_data.map_size) {
+ pr_err("%s: Invalid data size = %d\n", __func__,
+ bytes_returned);
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (bytes_returned > user_buf_size) {
+ pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n",
+ __func__, user_buf_size, bytes_returned);
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (copy_to_user((void __user *) buf,
+ rtac_cal[ASM_RTAC_CAL].cal_data.kvaddr,
+ bytes_returned)) {
+ pr_err("%s: Could not copy buffer to user,size = %d\n",
+ __func__, bytes_returned);
+ result = -EFAULT;
+ goto err;
+ }
+
+unlock:
+ mutex_unlock(&rtac_asm_apr_mutex);
+done:
+ return bytes_returned;
+err:
+ mutex_unlock(&rtac_asm_apr_mutex);
+ return result;
+}
+
+/* AFE APR */
+void rtac_set_afe_handle(void *handle)
+{
+ mutex_lock(&rtac_afe_apr_mutex);
+ rtac_afe_apr_data.apr_handle = handle;
+ mutex_unlock(&rtac_afe_apr_mutex);
+}
+
+bool rtac_make_afe_callback(uint32_t *payload, uint32_t payload_size)
+{
+ pr_debug("%s:cmd_state = %d\n", __func__,
+ atomic_read(&rtac_afe_apr_data.cmd_state));
+ if (atomic_read(&rtac_afe_apr_data.cmd_state) != 1)
+ return false;
+
+ if (payload_size == sizeof(uint32_t))
+ atomic_set(&rtac_common.apr_err_code, payload[0]);
+ else if (payload_size == (2*sizeof(uint32_t)))
+ atomic_set(&rtac_common.apr_err_code, payload[1]);
+
+ atomic_set(&rtac_afe_apr_data.cmd_state, 0);
+ wake_up(&rtac_afe_apr_data.cmd_wait);
+ return true;
+}
+
+static int fill_afe_apr_hdr(struct apr_hdr *apr_hdr, uint32_t port,
+ uint32_t opcode, uint32_t apr_msg_size)
+{
+ if (apr_hdr == NULL) {
+ pr_err("%s: invalid APR pointer", __func__);
+ return -EINVAL;
+ }
+
+ apr_hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ apr_hdr->pkt_size = apr_msg_size;
+ apr_hdr->src_svc = APR_SVC_AFE;
+ apr_hdr->src_domain = APR_DOMAIN_APPS;
+ apr_hdr->src_port = 0;
+ apr_hdr->dest_svc = APR_SVC_AFE;
+ apr_hdr->dest_domain = APR_DOMAIN_ADSP;
+ apr_hdr->dest_port = 0;
+ apr_hdr->token = port;
+ apr_hdr->opcode = opcode;
+
+ return 0;
+
+}
+static int send_rtac_afe_apr(void __user *buf, uint32_t opcode)
+{
+ int32_t result;
+ uint32_t bytes_returned = 0;
+ uint32_t payload_size = 0;
+ uint32_t port_index = 0;
+ uint32_t *afe_cmd = NULL;
+ uint32_t apr_msg_size = 0;
+ struct rtac_afe_user_data user_afe_buf;
+ struct mem_mapping_hdr *mem_hdr = NULL;
+ struct param_hdr_v1 *get_resp_v2;
+ struct param_hdr_v3 *get_resp_v3;
+
+ pr_debug("%s\n", __func__);
+
+ if (rtac_cal[AFE_RTAC_CAL].map_data.ion_handle == NULL) {
+ result = rtac_allocate_cal_buffer(AFE_RTAC_CAL);
+ if (result < 0) {
+ pr_err("%s: allocate buffer failed! ret = %d\n",
+ __func__, result);
+ goto done;
+ }
+ }
+
+ if (rtac_cal[AFE_RTAC_CAL].map_data.map_handle == 0) {
+ result = rtac_map_cal_buffer(AFE_RTAC_CAL);
+ if (result < 0) {
+ pr_err("%s: map buffer failed! ret = %d\n",
+ __func__, result);
+ goto done;
+ }
+ }
+
+ if (copy_from_user(&user_afe_buf, (void *)buf,
+ sizeof(struct rtac_afe_user_data))) {
+ pr_err("%s: Copy from user failed! buf = 0x%pK\n",
+ __func__, buf);
+ goto done;
+ }
+
+ if (user_afe_buf.buf_size <= 0) {
+ pr_err("%s: Invalid buffer size = %d\n",
+ __func__, user_afe_buf.buf_size);
+ goto done;
+ }
+
+ port_index = q6audio_get_port_index(user_afe_buf.port_id);
+ if (port_index >= AFE_MAX_PORTS) {
+ pr_err("%s: Invalid AFE port = 0x%x\n",
+ __func__, user_afe_buf.port_id);
+ goto done;
+ }
+
+ mutex_lock(&rtac_afe_apr_mutex);
+ if (rtac_afe_apr_data.apr_handle == NULL) {
+ pr_err("%s: APR not initialized\n", __func__);
+ result = -EINVAL;
+ goto err;
+ }
+
+ afe_cmd =
+ (u32 *) rtac_afe_buffer + sizeof(struct apr_hdr) / sizeof(u32);
+
+ switch (opcode) {
+ case AFE_PORT_CMD_SET_PARAM_V2:
+ apr_msg_size = sizeof(struct afe_port_cmd_set_param_v2);
+ payload_size = user_afe_buf.v2_set.payload_size;
+ if (payload_size > rtac_cal[AFE_RTAC_CAL].map_data.map_size) {
+ pr_err("%s: Invalid payload size = %d\n", __func__,
+ payload_size);
+ result = -EINVAL;
+ goto err;
+ }
+
+ /* Copy the command to the rtac buffer */
+ memcpy(afe_cmd, &user_afe_buf.v2_set,
+ sizeof(user_afe_buf.v2_set));
+
+ /* Copy the param data to the out-of-band location */
+ if (copy_from_user(rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr,
+ (void __user *) buf +
+ offsetof(struct rtac_afe_user_data,
+ v2_set.param_hdr),
+ payload_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ result = -EFAULT;
+ goto err;
+ }
+ break;
+ case AFE_PORT_CMD_SET_PARAM_V3:
+ apr_msg_size = sizeof(struct afe_port_cmd_set_param_v3);
+ payload_size = user_afe_buf.v3_set.payload_size;
+ if (payload_size > rtac_cal[AFE_RTAC_CAL].map_data.map_size) {
+ pr_err("%s: Invalid payload size = %d\n", __func__,
+ payload_size);
+ result = -EINVAL;
+ goto err;
+ }
+
+ /* Copy the command to the rtac buffer */
+ memcpy(afe_cmd, &user_afe_buf.v3_set,
+ sizeof(user_afe_buf.v3_set));
+
+ /* Copy the param data to the out-of-band location */
+ if (copy_from_user(rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr,
+ (void __user *) buf +
+ offsetof(struct rtac_afe_user_data,
+ v3_set.param_hdr),
+ payload_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ result = -EFAULT;
+ goto err;
+ }
+ break;
+ case AFE_PORT_CMD_GET_PARAM_V2:
+ apr_msg_size = sizeof(struct afe_port_cmd_get_param_v2);
+
+ if (user_afe_buf.cmd_size > MAX_PAYLOAD_SIZE) {
+ pr_err("%s: Invalid payload size = %d\n", __func__,
+ user_afe_buf.cmd_size);
+ result = -EINVAL;
+ goto err;
+ }
+
+ /* Copy the command and param data in-band */
+ if (copy_from_user(afe_cmd,
+ (void __user *) buf +
+ offsetof(struct rtac_afe_user_data,
+ v2_get),
+ user_afe_buf.cmd_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ result = -EFAULT;
+ goto err;
+ }
+ break;
+ case AFE_PORT_CMD_GET_PARAM_V3:
+ apr_msg_size = sizeof(struct afe_port_cmd_get_param_v3);
+
+ if (user_afe_buf.cmd_size > MAX_PAYLOAD_SIZE) {
+ pr_err("%s: Invalid payload size = %d\n", __func__,
+ user_afe_buf.cmd_size);
+ result = -EINVAL;
+ goto err;
+ }
+
+ /* Copy the command and param data in-band */
+ if (copy_from_user(afe_cmd,
+ (void __user *) buf +
+ offsetof(struct rtac_afe_user_data,
+ v3_get),
+ user_afe_buf.cmd_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ result = -EFAULT;
+ goto err;
+ }
+ break;
+ default:
+ pr_err("%s: Invalid opcode %d\n", __func__, opcode);
+ result = -EINVAL;
+ goto err;
+ }
+
+ /*
+ * The memory header is in the same location in all commands. Therefore,
+ * it doesn't matter what command the buffer is cast into.
+ */
+ mem_hdr = &((struct afe_port_cmd_set_param_v3 *) rtac_afe_buffer)
+ ->mem_hdr;
+ mem_hdr->data_payload_addr_lsw =
+ lower_32_bits(rtac_cal[AFE_RTAC_CAL].cal_data.paddr);
+ mem_hdr->data_payload_addr_msw = msm_audio_populate_upper_32_bits(
+ rtac_cal[AFE_RTAC_CAL].cal_data.paddr);
+ mem_hdr->mem_map_handle = rtac_cal[AFE_RTAC_CAL].map_data.map_handle;
+
+ /* Fill the APR header at the end so we have the correct message size */
+ fill_afe_apr_hdr((struct apr_hdr *) rtac_afe_buffer,
+ port_index, opcode, apr_msg_size);
+
+ atomic_set(&rtac_afe_apr_data.cmd_state, 1);
+
+ pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n",
+ __func__, opcode,
+ &rtac_cal[AFE_RTAC_CAL].cal_data.paddr);
+
+ result = apr_send_pkt(rtac_afe_apr_data.apr_handle,
+ (uint32_t *)rtac_afe_buffer);
+ if (result < 0) {
+ pr_err("%s: Set params failed port = 0x%x, ret = %d\n",
+ __func__, user_afe_buf.port_id, result);
+ goto err;
+ }
+ /* Wait for the callback */
+ result = wait_event_timeout(rtac_afe_apr_data.cmd_wait,
+ (atomic_read(&rtac_afe_apr_data.cmd_state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!result) {
+ pr_err("%s: Set params timed out port = 0x%x, ret = %d\n",
+ __func__, user_afe_buf.port_id, result);
+ goto err;
+ }
+ if (atomic_read(&rtac_common.apr_err_code)) {
+ pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n",
+ __func__, adsp_err_get_err_str(atomic_read(
+ &rtac_common.apr_err_code)),
+ opcode);
+ result = adsp_err_get_lnx_err_code(
+ atomic_read(
+ &rtac_common.apr_err_code));
+ goto err;
+ }
+
+ if (opcode == AFE_PORT_CMD_GET_PARAM_V2) {
+ get_resp_v2 = (struct param_hdr_v1 *) rtac_cal[AFE_RTAC_CAL]
+ .cal_data.kvaddr;
+ bytes_returned =
+ get_resp_v2->param_size + sizeof(struct param_hdr_v1);
+ } else if (opcode == AFE_PORT_CMD_GET_PARAM_V3) {
+ get_resp_v3 = (struct param_hdr_v3 *) rtac_cal[AFE_RTAC_CAL]
+ .cal_data.kvaddr;
+ bytes_returned =
+ get_resp_v3->param_size + sizeof(struct param_hdr_v3);
+ } else {
+ bytes_returned = payload_size;
+ goto unlock;
+ }
+
+ if (bytes_returned > rtac_cal[AFE_RTAC_CAL].map_data.map_size) {
+ pr_err("%s: Invalid data size = %d\n", __func__,
+ bytes_returned);
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (bytes_returned > user_afe_buf.buf_size) {
+ pr_err("%s: user size = 0x%x, returned size = 0x%x\n", __func__,
+ user_afe_buf.buf_size, bytes_returned);
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (copy_to_user((void __user *) buf,
+ rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr,
+ bytes_returned)) {
+ pr_err("%s: Could not copy buffer to user,size = %d\n",
+ __func__, bytes_returned);
+ result = -EFAULT;
+ goto err;
+ }
+
+unlock:
+ mutex_unlock(&rtac_afe_apr_mutex);
+done:
+ return bytes_returned;
+err:
+ mutex_unlock(&rtac_afe_apr_mutex);
+ return result;
+}
+
+/* Voice APR */
+void rtac_set_voice_handle(u32 mode, void *handle)
+{
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&rtac_voice_apr_mutex);
+ rtac_voice_apr_data[mode].apr_handle = handle;
+ mutex_unlock(&rtac_voice_apr_mutex);
+}
+
+bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size)
+{
+ if ((atomic_read(&rtac_voice_apr_data[mode].cmd_state) != 1) ||
+ (mode >= RTAC_VOICE_MODES))
+ return false;
+
+ pr_debug("%s\n", __func__);
+ if (payload_size == sizeof(uint32_t))
+ atomic_set(&rtac_common.apr_err_code, payload[0]);
+ else if (payload_size == (2*sizeof(uint32_t)))
+ atomic_set(&rtac_common.apr_err_code, payload[1]);
+
+ atomic_set(&rtac_voice_apr_data[mode].cmd_state, 0);
+ wake_up(&rtac_voice_apr_data[mode].cmd_wait);
+ return true;
+}
+
+int send_voice_apr(u32 mode, void *buf, u32 opcode)
+{
+ s32 result;
+ u32 user_buf_size = 0;
+ u32 bytes_returned = 0;
+ u32 payload_size;
+ u32 dest_port;
+ u32 data_size = 0;
+ struct apr_hdr voice_params;
+ pr_debug("%s\n", __func__);
+
+ if (rtac_cal[VOICE_RTAC_CAL].map_data.ion_handle == NULL) {
+ result = rtac_allocate_cal_buffer(VOICE_RTAC_CAL);
+ if (result < 0) {
+ pr_err("%s: allocate buffer failed!",
+ __func__);
+ goto done;
+ }
+ }
+
+ if (rtac_cal[VOICE_RTAC_CAL].map_data.map_handle == 0) {
+ result = rtac_map_cal_buffer(VOICE_RTAC_CAL);
+ if (result < 0) {
+ pr_err("%s: map buffer failed!",
+ __func__);
+ goto done;
+ }
+ }
+
+ if (copy_from_user(&user_buf_size, (void *)buf,
+ sizeof(user_buf_size))) {
+ pr_err("%s: Copy from user failed! buf = 0x%pK\n",
+ __func__, buf);
+ goto done;
+ }
+ if (user_buf_size <= 0) {
+ pr_err("%s: Invalid buffer size = %d\n",
+ __func__, user_buf_size);
+ goto done;
+ }
+
+ if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy payload size from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ if (copy_from_user(&dest_port, buf + 2 * sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy port id from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ if ((mode != RTAC_CVP) && (mode != RTAC_CVS)) {
+ pr_err("%s: Invalid Mode for APR, mode = %d\n",
+ __func__, mode);
+ goto done;
+ }
+
+ mutex_lock(&rtac_voice_apr_mutex);
+ if (rtac_voice_apr_data[mode].apr_handle == NULL) {
+ pr_err("%s: APR not initialized\n", __func__);
+ result = -EINVAL;
+ goto err;
+ }
+
+ switch (opcode) {
+ case VSS_ICOMMON_CMD_SET_PARAM_V2:
+ case VSS_ICOMMON_CMD_SET_PARAM_V3:
+ /* set payload size to in-band payload */
+ /* set data size to actual out of band payload size */
+ data_size = payload_size - 4 * sizeof(u32);
+ if (data_size > rtac_cal[VOICE_RTAC_CAL].map_data.map_size) {
+ pr_err("%s: Invalid data size = %d\n",
+ __func__, data_size);
+ result = -EINVAL;
+ goto err;
+ }
+ payload_size = 4 * sizeof(u32);
+
+ /* Copy buffer to out-of-band payload */
+ if (copy_from_user((void *)
+ rtac_cal[VOICE_RTAC_CAL].cal_data.kvaddr,
+ buf + 7 * sizeof(u32), data_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ result = -EFAULT;
+ goto err;
+ }
+ /* set payload size in packet */
+ rtac_voice_buffer[8] = data_size;
+ /* set token for set param case */
+ voice_params.token = VOC_RTAC_SET_PARAM_TOKEN;
+ break;
+ case VSS_ICOMMON_CMD_GET_PARAM_V2:
+ case VSS_ICOMMON_CMD_GET_PARAM_V3:
+ if (payload_size > MAX_PAYLOAD_SIZE) {
+ pr_err("%s: Invalid payload size = %d\n",
+ __func__, payload_size);
+ result = -EINVAL;
+ goto err;
+ }
+
+ /* Copy buffer to in-band payload */
+ if (copy_from_user(rtac_voice_buffer +
+ sizeof(voice_params)/sizeof(u32),
+ buf + 3 * sizeof(u32), payload_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ result = -EFAULT;
+ goto err;
+ }
+ /* set token for get param case */
+ voice_params.token = 0;
+ break;
+ default:
+ pr_err("%s: Invalid opcode %d\n", __func__, opcode);
+ result = -EINVAL;
+ goto err;
+ }
+
+ /* Pack header */
+ voice_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ voice_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ payload_size);
+ voice_params.src_svc = 0;
+ voice_params.src_domain = APR_DOMAIN_APPS;
+ voice_params.src_port = get_voice_index(mode, dest_port);
+ voice_params.dest_svc = 0;
+ voice_params.dest_domain = APR_DOMAIN_MODEM;
+ voice_params.dest_port = (u16)dest_port;
+ voice_params.opcode = opcode;
+
+ /* fill for out-of-band */
+ rtac_voice_buffer[5] = rtac_cal[VOICE_RTAC_CAL].map_data.map_handle;
+ rtac_voice_buffer[6] =
+ lower_32_bits(rtac_cal[VOICE_RTAC_CAL].cal_data.paddr);
+ rtac_voice_buffer[7] = msm_audio_populate_upper_32_bits(
+ rtac_cal[VOICE_RTAC_CAL].cal_data.paddr);
+
+ memcpy(rtac_voice_buffer, &voice_params, sizeof(voice_params));
+ atomic_set(&rtac_voice_apr_data[mode].cmd_state, 1);
+
+ pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n",
+ __func__, opcode,
+ &rtac_cal[VOICE_RTAC_CAL].cal_data.paddr);
+
+ result = apr_send_pkt(rtac_voice_apr_data[mode].apr_handle,
+ (uint32_t *)rtac_voice_buffer);
+ if (result < 0) {
+ pr_err("%s: apr_send_pkt failed opcode = %x\n",
+ __func__, opcode);
+ goto err;
+ }
+ /* Wait for the callback */
+ result = wait_event_timeout(rtac_voice_apr_data[mode].cmd_wait,
+ (atomic_read(&rtac_voice_apr_data[mode].cmd_state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!result) {
+ pr_err("%s: apr_send_pkt timed out opcode = %x\n",
+ __func__, opcode);
+ goto err;
+ }
+ if (atomic_read(&rtac_common.apr_err_code)) {
+ pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n",
+ __func__, adsp_err_get_err_str(atomic_read(
+ &rtac_common.apr_err_code)),
+ opcode);
+ result = adsp_err_get_lnx_err_code(
+ atomic_read(
+ &rtac_common.apr_err_code));
+ goto err;
+ }
+
+ if (opcode == VSS_ICOMMON_CMD_GET_PARAM_V2) {
+ bytes_returned = ((u32 *)rtac_cal[VOICE_RTAC_CAL].cal_data.
+ kvaddr)[2] + 3 * sizeof(u32);
+ } else if (opcode == VSS_ICOMMON_CMD_GET_PARAM_V3) {
+ bytes_returned =
+ ((u32 *) rtac_cal[VOICE_RTAC_CAL].cal_data.kvaddr)[3] +
+ 4 * sizeof(u32);
+ } else {
+ bytes_returned = data_size;
+ goto unlock;
+ }
+
+ if (bytes_returned > rtac_cal[VOICE_RTAC_CAL].map_data.map_size) {
+ pr_err("%s: Invalid data size = %d\n", __func__,
+ bytes_returned);
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (bytes_returned > user_buf_size) {
+ pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n",
+ __func__, user_buf_size, bytes_returned);
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (copy_to_user((void __user *) buf,
+ rtac_cal[VOICE_RTAC_CAL].cal_data.kvaddr,
+ bytes_returned)) {
+ pr_err("%s: Could not copy buffer to user, size = %d\n",
+ __func__, bytes_returned);
+ result = -EFAULT;
+ goto err;
+ }
+
+unlock:
+ mutex_unlock(&rtac_voice_apr_mutex);
+done:
+ return bytes_returned;
+err:
+ mutex_unlock(&rtac_voice_apr_mutex);
+ return result;
+}
+
+void get_rtac_adm_data(struct rtac_adm *adm_data)
+{
+ mutex_lock(&rtac_adm_mutex);
+ memcpy(adm_data, &rtac_adm_data, sizeof(struct rtac_adm));
+ mutex_unlock(&rtac_adm_mutex);
+}
+
+
+static long rtac_ioctl_shared(struct file *f,
+ unsigned int cmd, void *arg)
+{
+ u32 opcode;
+ int result = 0;
+ if (!arg) {
+ pr_err("%s: No data sent to driver!\n", __func__);
+ result = -EFAULT;
+ goto done;
+ }
+
+ switch (cmd) {
+ case AUDIO_GET_RTAC_ADM_INFO: {
+ mutex_lock(&rtac_adm_mutex);
+ if (copy_to_user((void *)arg, &rtac_adm_data,
+ sizeof(rtac_adm_data))) {
+ pr_err("%s: copy_to_user failed for AUDIO_GET_RTAC_ADM_INFO\n",
+ __func__);
+ mutex_unlock(&rtac_adm_mutex);
+ return -EFAULT;
+ } else {
+ result = sizeof(rtac_adm_data);
+ }
+ mutex_unlock(&rtac_adm_mutex);
+ break;
+ }
+ case AUDIO_GET_RTAC_VOICE_INFO: {
+ mutex_lock(&rtac_voice_mutex);
+ if (copy_to_user((void *)arg, &rtac_voice_data,
+ sizeof(rtac_voice_data))) {
+ pr_err("%s: copy_to_user failed for AUDIO_GET_RTAC_VOICE_INFO\n",
+ __func__);
+ mutex_unlock(&rtac_voice_mutex);
+ return -EFAULT;
+ } else {
+ result = sizeof(rtac_voice_data);
+ }
+ mutex_unlock(&rtac_voice_mutex);
+ break;
+ }
+
+ case AUDIO_GET_RTAC_ADM_CAL:
+ opcode = q6common_is_instance_id_supported() ?
+ ADM_CMD_GET_PP_PARAMS_V6 :
+ ADM_CMD_GET_PP_PARAMS_V5;
+ result = send_adm_apr((void *) arg, opcode);
+ break;
+ case AUDIO_SET_RTAC_ADM_CAL:
+ opcode = q6common_is_instance_id_supported() ?
+ ADM_CMD_SET_PP_PARAMS_V6 :
+ ADM_CMD_SET_PP_PARAMS_V5;
+ result = send_adm_apr((void *) arg, opcode);
+ break;
+ case AUDIO_GET_RTAC_ASM_CAL:
+ opcode = q6common_is_instance_id_supported() ?
+ ASM_STREAM_CMD_GET_PP_PARAMS_V3 :
+ ASM_STREAM_CMD_GET_PP_PARAMS_V2;
+ result = send_rtac_asm_apr((void *) arg, opcode);
+ break;
+ case AUDIO_SET_RTAC_ASM_CAL:
+ opcode = q6common_is_instance_id_supported() ?
+ ASM_STREAM_CMD_SET_PP_PARAMS_V3 :
+ ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+ result = send_rtac_asm_apr((void *) arg, opcode);
+ break;
+ case AUDIO_GET_RTAC_CVS_CAL:
+ opcode = q6common_is_instance_id_supported() ?
+ VSS_ICOMMON_CMD_GET_PARAM_V3 :
+ VSS_ICOMMON_CMD_GET_PARAM_V2;
+ result = send_voice_apr(RTAC_CVS, (void *) arg, opcode);
+ break;
+ case AUDIO_SET_RTAC_CVS_CAL:
+ opcode = q6common_is_instance_id_supported() ?
+ VSS_ICOMMON_CMD_SET_PARAM_V3 :
+ VSS_ICOMMON_CMD_SET_PARAM_V2;
+ result = send_voice_apr(RTAC_CVS, (void *) arg, opcode);
+ break;
+ case AUDIO_GET_RTAC_CVP_CAL:
+ opcode = q6common_is_instance_id_supported() ?
+ VSS_ICOMMON_CMD_GET_PARAM_V3 :
+ VSS_ICOMMON_CMD_GET_PARAM_V2;
+ result = send_voice_apr(RTAC_CVP, (void *) arg, opcode);
+ break;
+ case AUDIO_SET_RTAC_CVP_CAL:
+ opcode = q6common_is_instance_id_supported() ?
+ VSS_ICOMMON_CMD_SET_PARAM_V3 :
+ VSS_ICOMMON_CMD_SET_PARAM_V2;
+ result = send_voice_apr(RTAC_CVP, (void *) arg, opcode);
+ break;
+ case AUDIO_GET_RTAC_AFE_CAL:
+ opcode = q6common_is_instance_id_supported() ?
+ AFE_PORT_CMD_GET_PARAM_V3 :
+ AFE_PORT_CMD_GET_PARAM_V2;
+ result = send_rtac_afe_apr((void __user *) arg, opcode);
+ break;
+ case AUDIO_SET_RTAC_AFE_CAL:
+ opcode = q6common_is_instance_id_supported() ?
+ AFE_PORT_CMD_SET_PARAM_V3 :
+ AFE_PORT_CMD_SET_PARAM_V2;
+ result = send_rtac_afe_apr((void __user *) arg, opcode);
+ break;
+ default:
+ pr_err("%s: Invalid IOCTL, command = %d!\n",
+ __func__, cmd);
+ result = -EINVAL;
+ }
+done:
+ return result;
+}
+
+static long rtac_ioctl(struct file *f,
+ unsigned int cmd, unsigned long arg)
+{
+ int result = 0;
+
+ mutex_lock(&rtac_common.rtac_fops_mutex);
+ if (!arg) {
+ pr_err("%s: No data sent to driver!\n", __func__);
+ result = -EFAULT;
+ } else {
+ result = rtac_ioctl_shared(f, cmd, (void __user *)arg);
+ }
+
+ mutex_unlock(&rtac_common.rtac_fops_mutex);
+ return result;
+}
+
+#ifdef CONFIG_COMPAT
+#define AUDIO_GET_RTAC_ADM_INFO_32 _IOR(CAL_IOCTL_MAGIC, 207, compat_uptr_t)
+#define AUDIO_GET_RTAC_VOICE_INFO_32 _IOR(CAL_IOCTL_MAGIC, 208, compat_uptr_t)
+#define AUDIO_GET_RTAC_ADM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 209, compat_uptr_t)
+#define AUDIO_SET_RTAC_ADM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 210, compat_uptr_t)
+#define AUDIO_GET_RTAC_ASM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 211, compat_uptr_t)
+#define AUDIO_SET_RTAC_ASM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 212, compat_uptr_t)
+#define AUDIO_GET_RTAC_CVS_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 213, compat_uptr_t)
+#define AUDIO_SET_RTAC_CVS_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 214, compat_uptr_t)
+#define AUDIO_GET_RTAC_CVP_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 215, compat_uptr_t)
+#define AUDIO_SET_RTAC_CVP_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 216, compat_uptr_t)
+#define AUDIO_GET_RTAC_AFE_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 217, compat_uptr_t)
+#define AUDIO_SET_RTAC_AFE_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 218, compat_uptr_t)
+
+static long rtac_compat_ioctl(struct file *f,
+ unsigned int cmd, unsigned long arg)
+{
+ int result = 0;
+
+ mutex_lock(&rtac_common.rtac_fops_mutex);
+ if (!arg) {
+ pr_err("%s: No data sent to driver!\n", __func__);
+ result = -EINVAL;
+ goto done;
+ }
+
+ switch (cmd) {
+ case AUDIO_GET_RTAC_ADM_INFO_32:
+ cmd = AUDIO_GET_RTAC_ADM_INFO;
+ goto process;
+ case AUDIO_GET_RTAC_VOICE_INFO_32:
+ cmd = AUDIO_GET_RTAC_VOICE_INFO;
+ goto process;
+ case AUDIO_GET_RTAC_AFE_CAL_32:
+ cmd = AUDIO_GET_RTAC_AFE_CAL;
+ goto process;
+ case AUDIO_SET_RTAC_AFE_CAL_32:
+ cmd = AUDIO_SET_RTAC_AFE_CAL;
+ goto process;
+ case AUDIO_GET_RTAC_ADM_CAL_32:
+ cmd = AUDIO_GET_RTAC_ADM_CAL;
+ goto process;
+ case AUDIO_SET_RTAC_ADM_CAL_32:
+ cmd = AUDIO_SET_RTAC_ADM_CAL;
+ goto process;
+ case AUDIO_GET_RTAC_ASM_CAL_32:
+ cmd = AUDIO_GET_RTAC_ASM_CAL;
+ goto process;
+ case AUDIO_SET_RTAC_ASM_CAL_32:
+ cmd = AUDIO_SET_RTAC_ASM_CAL;
+ goto process;
+ case AUDIO_GET_RTAC_CVS_CAL_32:
+ cmd = AUDIO_GET_RTAC_CVS_CAL;
+ goto process;
+ case AUDIO_SET_RTAC_CVS_CAL_32:
+ cmd = AUDIO_SET_RTAC_CVS_CAL;
+ goto process;
+ case AUDIO_GET_RTAC_CVP_CAL_32:
+ cmd = AUDIO_GET_RTAC_CVP_CAL;
+ goto process;
+ case AUDIO_SET_RTAC_CVP_CAL_32:
+ cmd = AUDIO_SET_RTAC_CVP_CAL;
+process:
+ result = rtac_ioctl_shared(f, cmd, compat_ptr(arg));
+ break;
+ default:
+ result = -EINVAL;
+ pr_err("%s: Invalid IOCTL, command = %d!\n",
+ __func__, cmd);
+ break;
+ }
+done:
+ mutex_unlock(&rtac_common.rtac_fops_mutex);
+ return result;
+}
+#else
+#define rtac_compat_ioctl NULL
+#endif
+
+static const struct file_operations rtac_fops = {
+ .owner = THIS_MODULE,
+ .open = rtac_open,
+ .release = rtac_release,
+ .unlocked_ioctl = rtac_ioctl,
+ .compat_ioctl = rtac_compat_ioctl,
+};
+
+struct miscdevice rtac_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_rtac",
+ .fops = &rtac_fops,
+};
+
+static int __init rtac_init(void)
+{
+ int i = 0;
+
+ /* Driver */
+ atomic_set(&rtac_common.usage_count, 0);
+ atomic_set(&rtac_common.apr_err_code, 0);
+ mutex_init(&rtac_common.rtac_fops_mutex);
+
+ /* ADM */
+ memset(&rtac_adm_data, 0, sizeof(rtac_adm_data));
+ rtac_adm_apr_data.apr_handle = NULL;
+ atomic_set(&rtac_adm_apr_data.cmd_state, 0);
+ init_waitqueue_head(&rtac_adm_apr_data.cmd_wait);
+ mutex_init(&rtac_adm_mutex);
+ mutex_init(&rtac_adm_apr_mutex);
+
+ rtac_adm_buffer = kzalloc(
+ rtac_cal[ADM_RTAC_CAL].map_data.map_size, GFP_KERNEL);
+ if (rtac_adm_buffer == NULL) {
+ pr_err("%s: Could not allocate payload of size = %d\n",
+ __func__, rtac_cal[ADM_RTAC_CAL].map_data.map_size);
+ goto nomem;
+ }
+
+ /* ASM */
+ for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED+1; i++) {
+ rtac_asm_apr_data[i].apr_handle = NULL;
+ atomic_set(&rtac_asm_apr_data[i].cmd_state, 0);
+ init_waitqueue_head(&rtac_asm_apr_data[i].cmd_wait);
+ }
+ mutex_init(&rtac_asm_apr_mutex);
+
+ rtac_asm_buffer = kzalloc(
+ rtac_cal[ASM_RTAC_CAL].map_data.map_size, GFP_KERNEL);
+ if (rtac_asm_buffer == NULL) {
+ pr_err("%s: Could not allocate payload of size = %d\n",
+ __func__, rtac_cal[ASM_RTAC_CAL].map_data.map_size);
+ kzfree(rtac_adm_buffer);
+ goto nomem;
+ }
+
+ /* AFE */
+ rtac_afe_apr_data.apr_handle = NULL;
+ atomic_set(&rtac_afe_apr_data.cmd_state, 0);
+ init_waitqueue_head(&rtac_afe_apr_data.cmd_wait);
+ mutex_init(&rtac_afe_apr_mutex);
+
+ rtac_afe_buffer = kzalloc(
+ rtac_cal[AFE_RTAC_CAL].map_data.map_size, GFP_KERNEL);
+ if (rtac_afe_buffer == NULL) {
+ pr_err("%s: Could not allocate payload of size = %d\n",
+ __func__, rtac_cal[AFE_RTAC_CAL].map_data.map_size);
+ kzfree(rtac_adm_buffer);
+ kzfree(rtac_asm_buffer);
+ goto nomem;
+ }
+
+ /* Voice */
+ memset(&rtac_voice_data, 0, sizeof(rtac_voice_data));
+ for (i = 0; i < RTAC_VOICE_MODES; i++) {
+ rtac_voice_apr_data[i].apr_handle = NULL;
+ atomic_set(&rtac_voice_apr_data[i].cmd_state, 0);
+ init_waitqueue_head(&rtac_voice_apr_data[i].cmd_wait);
+ }
+ mutex_init(&rtac_voice_mutex);
+ mutex_init(&rtac_voice_apr_mutex);
+
+ rtac_voice_buffer = kzalloc(
+ rtac_cal[VOICE_RTAC_CAL].map_data.map_size, GFP_KERNEL);
+ if (rtac_voice_buffer == NULL) {
+ pr_err("%s: Could not allocate payload of size = %d\n",
+ __func__, rtac_cal[VOICE_RTAC_CAL].map_data.map_size);
+ kzfree(rtac_adm_buffer);
+ kzfree(rtac_asm_buffer);
+ kzfree(rtac_afe_buffer);
+ goto nomem;
+ }
+
+ return misc_register(&rtac_misc);
+nomem:
+ return -ENOMEM;
+}
+
+module_init(rtac_init);
+
+MODULE_DESCRIPTION("SoC QDSP6v2 Real-Time Audio Calibration driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/sdm660-common.c b/sound/soc/msm/sdm660-common.c
new file mode 100644
index 000000000000..a9aa9a9d82e8
--- /dev/null
+++ b/sound/soc/msm/sdm660-common.c
@@ -0,0 +1,3268 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/input.h>
+#include <linux/of_gpio.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <sound/pcm_params.h>
+#include <sound/q6afe-v2.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "sdm660-common.h"
+#include "sdm660-internal.h"
+#include "sdm660-external.h"
+#include "../codecs/sdm660_cdc/msm-analog-cdc.h"
+#include "../codecs/wsa881x.h"
+
+#define __CHIPSET__ "SDM660 "
+#define MSM_DAILINK_NAME(name) (__CHIPSET__#name)
+#define DRV_NAME "sdm660-asoc-snd"
+
+#define MSM_INT_DIGITAL_CODEC "msm-dig-codec"
+#define PMIC_INT_ANALOG_CODEC "analog-codec"
+
+#define DEV_NAME_STR_LEN 32
+#define DEFAULT_MCLK_RATE 9600000
+#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */
+
+struct dev_config {
+ u32 sample_rate;
+ u32 bit_format;
+ u32 channels;
+};
+
+enum {
+ DP_RX_IDX,
+ EXT_DISP_RX_IDX_MAX,
+};
+
+/* TDM default config */
+static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+ { /* PRI TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ },
+ { /* SEC TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ },
+ { /* TERT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ },
+ { /* QUAT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ }
+};
+
+/* TDM default config */
+static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+ { /* PRI TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ },
+ { /* SEC TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ },
+ { /* TERT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ },
+ { /* QUAT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ }
+};
+
+/* Default configuration of external display BE */
+static struct dev_config ext_disp_rx_cfg[] = {
+ [DP_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+static struct dev_config usb_rx_cfg = {
+ .sample_rate = SAMPLING_RATE_48KHZ,
+ .bit_format = SNDRV_PCM_FORMAT_S16_LE,
+ .channels = 2,
+};
+
+static struct dev_config usb_tx_cfg = {
+ .sample_rate = SAMPLING_RATE_48KHZ,
+ .bit_format = SNDRV_PCM_FORMAT_S16_LE,
+ .channels = 1,
+};
+
+enum {
+ PRIM_MI2S = 0,
+ SEC_MI2S,
+ TERT_MI2S,
+ QUAT_MI2S,
+ MI2S_MAX,
+};
+
+enum {
+ PRIM_AUX_PCM = 0,
+ SEC_AUX_PCM,
+ TERT_AUX_PCM,
+ QUAT_AUX_PCM,
+ AUX_PCM_MAX,
+};
+
+enum {
+ PCM_I2S_SEL_PRIM = 0,
+ PCM_I2S_SEL_SEC,
+ PCM_I2S_SEL_TERT,
+ PCM_I2S_SEL_QUAT,
+ PCM_I2S_SEL_MAX,
+};
+
+struct mi2s_conf {
+ struct mutex lock;
+ u32 ref_cnt;
+ u32 msm_is_mi2s_master;
+ u32 msm_is_ext_mclk;
+};
+
+static u32 mi2s_ebit_clk[MI2S_MAX] = {
+ Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT,
+ Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT,
+ Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT
+};
+
+struct msm_wsa881x_dev_info {
+ struct device_node *of_node;
+ u32 index;
+};
+static struct snd_soc_aux_dev *msm_aux_dev;
+static struct snd_soc_codec_conf *msm_codec_conf;
+
+static bool msm_swap_gnd_mic(struct snd_soc_codec *codec);
+
+static struct wcd_mbhc_config mbhc_cfg = {
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .detect_extn_cable = true,
+ .mono_stero_detection = false,
+ .swap_gnd_mic = NULL,
+ .hs_ext_micbias = true,
+ .key_code[0] = KEY_MEDIA,
+ .key_code[1] = KEY_VOICECOMMAND,
+ .key_code[2] = KEY_VOLUMEUP,
+ .key_code[3] = KEY_VOLUMEDOWN,
+ .key_code[4] = 0,
+ .key_code[5] = 0,
+ .key_code[6] = 0,
+ .key_code[7] = 0,
+ .linein_th = 5000,
+ .moisture_en = false,
+ .mbhc_micbias = 0,
+ .anc_micbias = 0,
+ .enable_anc_mic_detect = false,
+};
+
+static struct dev_config proxy_rx_cfg = {
+ .sample_rate = SAMPLING_RATE_48KHZ,
+ .bit_format = SNDRV_PCM_FORMAT_S16_LE,
+ .channels = 2,
+};
+
+/* Default configuration of MI2S channels */
+static struct dev_config mi2s_rx_cfg[] = {
+ [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static struct dev_config mi2s_tx_cfg[] = {
+ [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_rx_cfg[] = {
+ [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_tx_cfg[] = {
+ [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static char const *ch_text[] = {"Two", "Three", "Four", "Five",
+ "Six", "Seven", "Eight"};
+static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"};
+static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16",
+ "KHZ_32", "KHZ_44P1", "KHZ_48",
+ "KHZ_96", "KHZ_192"};
+static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven",
+ "Eight"};
+static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE",
+ "S32_LE"};
+static char const *mi2s_format_text[] = {"S16_LE", "S24_LE", "S24_3LE",
+ "S32_LE"};
+static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight"};
+static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"};
+static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32",
+ "KHZ_44P1", "KHZ_48", "KHZ_96",
+ "KHZ_192", "KHZ_352P8", "KHZ_384"};
+static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven",
+ "Eight"};
+static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025",
+ "KHZ_16", "KHZ_22P05",
+ "KHZ_32", "KHZ_44P1", "KHZ_48",
+ "KHZ_96", "KHZ_192", "KHZ_384"};
+static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE"};
+static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+ "KHZ_192"};
+static const char *const qos_text[] = {"Disable", "Enable"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_format, mi2s_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_format, mi2s_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_format, mi2s_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_format, mi2s_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_format, mi2s_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_format, mi2s_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_format, mi2s_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_format, mi2s_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate,
+ ext_disp_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(qos_vote, qos_text);
+
+static int qos_vote_status;
+
+static struct afe_clk_set mi2s_clk[MI2S_MAX] = {
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ }
+};
+
+static struct afe_clk_set mi2s_mclk[MI2S_MAX] = {
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_MCLK_3,
+ Q6AFE_LPASS_OSR_CLK_9_P600_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_MCLK_4,
+ Q6AFE_LPASS_OSR_CLK_9_P600_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_MCLK_1,
+ Q6AFE_LPASS_OSR_CLK_9_P600_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_MCLK_2,
+ Q6AFE_LPASS_OSR_CLK_9_P600_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ }
+};
+
+static struct mi2s_conf mi2s_intf_conf[MI2S_MAX];
+
+static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: proxy_rx channels = %d\n",
+ __func__, proxy_rx_cfg.channels);
+ ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2;
+
+ return 0;
+}
+
+static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2;
+ pr_debug("%s: proxy_rx channels = %d\n",
+ __func__, proxy_rx_cfg.channels);
+
+ return 1;
+}
+
+static int tdm_get_sample_rate(int value)
+{
+ int sample_rate = 0;
+
+ switch (value) {
+ case 0:
+ sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 6:
+ sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 7:
+ sample_rate = SAMPLING_RATE_352P8KHZ;
+ break;
+ case 8:
+ sample_rate = SAMPLING_RATE_384KHZ;
+ break;
+ default:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return sample_rate;
+}
+
+static int tdm_get_sample_rate_val(int sample_rate)
+{
+ int sample_rate_val = 0;
+
+ switch (sample_rate) {
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 0;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_352P8KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_384KHZ:
+ sample_rate_val = 8;
+ break;
+ default:
+ sample_rate_val = 4;
+ break;
+ }
+ return sample_rate_val;
+}
+
+static int tdm_get_port_idx(struct snd_kcontrol *kcontrol,
+ struct tdm_port *port)
+{
+ if (port) {
+ if (strnstr(kcontrol->id.name, "PRI",
+ sizeof(kcontrol->id.name))) {
+ port->mode = TDM_PRI;
+ } else if (strnstr(kcontrol->id.name, "SEC",
+ sizeof(kcontrol->id.name))) {
+ port->mode = TDM_SEC;
+ } else if (strnstr(kcontrol->id.name, "TERT",
+ sizeof(kcontrol->id.name))) {
+ port->mode = TDM_TERT;
+ } else if (strnstr(kcontrol->id.name, "QUAT",
+ sizeof(kcontrol->id.name))) {
+ port->mode = TDM_QUAT;
+ } else {
+ pr_err("%s: unsupported mode in: %s",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ if (strnstr(kcontrol->id.name, "RX_0",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_0",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_0;
+ } else if (strnstr(kcontrol->id.name, "RX_1",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_1",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_1;
+ } else if (strnstr(kcontrol->id.name, "RX_2",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_2",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_2;
+ } else if (strnstr(kcontrol->id.name, "RX_3",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_3",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_3;
+ } else if (strnstr(kcontrol->id.name, "RX_4",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_4",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_4;
+ } else if (strnstr(kcontrol->id.name, "RX_5",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_5",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_5;
+ } else if (strnstr(kcontrol->id.name, "RX_6",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_6",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_6;
+ } else if (strnstr(kcontrol->id.name, "RX_7",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_7",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_7;
+ } else {
+ pr_err("%s: unsupported channel in: %s",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+ } else
+ return -EINVAL;
+ return 0;
+}
+
+static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+ tdm_rx_cfg[port.mode][port.channel].sample_rate);
+
+ pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_rx_cfg[port.mode][port.channel].sample_rate =
+ tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+ tdm_tx_cfg[port.mode][port.channel].sample_rate);
+
+ pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_tx_cfg[port.mode][port.channel].sample_rate =
+ tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_get_format(int value)
+{
+ int format = 0;
+
+ switch (value) {
+ case 0:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 1:
+ format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 2:
+ format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ default:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return format;
+}
+
+static int tdm_get_format_val(int format)
+{
+ int value = 0;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ value = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ value = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ value = 2;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ return value;
+}
+
+static int mi2s_get_format(int value)
+{
+ int format = 0;
+
+ switch (value) {
+ case 0:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 1:
+ format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 2:
+ format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 3:
+ format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ default:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return format;
+}
+
+static int mi2s_get_format_value(int format)
+{
+ int value = 0;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ value = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ value = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ value = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ value = 3;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ return value;
+}
+
+static int tdm_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+ tdm_rx_cfg[port.mode][port.channel].bit_format);
+
+ pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_rx_cfg[port.mode][port.channel].bit_format =
+ tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+ tdm_tx_cfg[port.mode][port.channel].bit_format);
+
+ pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_tx_cfg[port.mode][port.channel].bit_format =
+ tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+
+ ucontrol->value.enumerated.item[0] =
+ tdm_rx_cfg[port.mode][port.channel].channels - 1;
+
+ pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].channels - 1,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_rx_cfg[port.mode][port.channel].channels =
+ ucontrol->value.enumerated.item[0] + 1;
+
+ pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].channels,
+ ucontrol->value.enumerated.item[0] + 1);
+ }
+ return ret;
+}
+
+static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] =
+ tdm_tx_cfg[port.mode][port.channel].channels - 1;
+
+ pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].channels - 1,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_tx_cfg[port.mode][port.channel].channels =
+ ucontrol->value.enumerated.item[0] + 1;
+
+ pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].channels,
+ ucontrol->value.enumerated.item[0] + 1);
+ }
+ return ret;
+}
+
+static int aux_pcm_get_sample_rate(int value)
+{
+ int sample_rate;
+
+ switch (value) {
+ case 1:
+ sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 0:
+ default:
+ sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ return sample_rate;
+}
+
+static int aux_pcm_get_sample_rate_val(int sample_rate)
+{
+ int sample_rate_val;
+
+ switch (sample_rate) {
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+ return sample_rate_val;
+}
+
+static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+ int idx;
+
+ if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM",
+ sizeof("PRIM_AUX_PCM")))
+ idx = PRIM_AUX_PCM;
+ else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM",
+ sizeof("SEC_AUX_PCM")))
+ idx = SEC_AUX_PCM;
+ else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM",
+ sizeof("TERT_AUX_PCM")))
+ idx = TERT_AUX_PCM;
+ else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM",
+ sizeof("QUAT_AUX_PCM")))
+ idx = QUAT_AUX_PCM;
+ else {
+ pr_err("%s: unsupported port: %s",
+ __func__, kcontrol->id.name);
+ idx = -EINVAL;
+ }
+
+ return idx;
+}
+
+static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ aux_pcm_rx_cfg[idx].sample_rate =
+ aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ aux_pcm_tx_cfg[idx].sample_rate =
+ aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+ int idx;
+
+ if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX",
+ sizeof("PRIM_MI2S_RX")))
+ idx = PRIM_MI2S;
+ else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX",
+ sizeof("SEC_MI2S_RX")))
+ idx = SEC_MI2S;
+ else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX",
+ sizeof("TERT_MI2S_RX")))
+ idx = TERT_MI2S;
+ else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX",
+ sizeof("QUAT_MI2S_RX")))
+ idx = QUAT_MI2S;
+ else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX",
+ sizeof("PRIM_MI2S_TX")))
+ idx = PRIM_MI2S;
+ else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX",
+ sizeof("SEC_MI2S_TX")))
+ idx = SEC_MI2S;
+ else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX",
+ sizeof("TERT_MI2S_TX")))
+ idx = TERT_MI2S;
+ else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX",
+ sizeof("QUAT_MI2S_TX")))
+ idx = QUAT_MI2S;
+ else {
+ pr_err("%s: unsupported channel: %s",
+ __func__, kcontrol->id.name);
+ idx = -EINVAL;
+ }
+
+ return idx;
+}
+
+static int mi2s_get_sample_rate_val(int sample_rate)
+{
+ int sample_rate_val;
+
+ switch (sample_rate) {
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 0;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 6;
+ break;
+ default:
+ sample_rate_val = 4;
+ break;
+ }
+ return sample_rate_val;
+}
+
+static int mi2s_get_sample_rate(int value)
+{
+ int sample_rate;
+
+ switch (value) {
+ case 0:
+ sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 6:
+ sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ default:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return sample_rate;
+}
+
+static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_rx_cfg[idx].sample_rate =
+ mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_tx_cfg[idx].sample_rate =
+ mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_tx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_tx_cfg[idx].bit_format =
+ mi2s_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d] _tx_format = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_tx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_format_value(mi2s_tx_cfg[idx].bit_format);
+
+ pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_rx_cfg[idx].bit_format =
+ mi2s_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d] _rx_format = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_format_value(mi2s_rx_cfg[idx].bit_format);
+
+ pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].channels);
+ ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1;
+
+ return 0;
+}
+
+static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+ pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].channels);
+
+ return 1;
+}
+
+static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].channels);
+ ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1;
+
+ return 0;
+}
+
+static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+ pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].channels);
+
+ return 1;
+}
+
+static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: usb_audio_rx_ch = %d\n", __func__,
+ usb_rx_cfg.channels);
+ ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1;
+ return 0;
+}
+
+static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels);
+ return 1;
+}
+
+static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val;
+
+ switch (usb_rx_cfg.sample_rate) {
+ case SAMPLING_RATE_384KHZ:
+ sample_rate_val = 9;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 8;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_22P05KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_11P025KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__,
+ usb_rx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 9:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+ break;
+ case 8:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 7:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 6:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+ break;
+ case 2:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 1:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+ break;
+ case 0:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ default:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+
+ pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n",
+ __func__, ucontrol->value.integer.value[0],
+ usb_rx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (usb_rx_cfg.bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_rx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 3:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ case 2:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 1:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_rx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return rc;
+}
+
+static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: usb_audio_tx_ch = %d\n", __func__,
+ usb_tx_cfg.channels);
+ ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1;
+ return 0;
+}
+
+static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels);
+ return 1;
+}
+
+static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val;
+
+ switch (usb_tx_cfg.sample_rate) {
+ case SAMPLING_RATE_384KHZ:
+ sample_rate_val = 9;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 8;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_22P05KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_11P025KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 0;
+ break;
+ default:
+ sample_rate_val = 6;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__,
+ usb_tx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 9:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+ break;
+ case 8:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 7:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 6:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+ break;
+ case 2:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 1:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+ break;
+ case 0:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ default:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+
+ pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n",
+ __func__, ucontrol->value.integer.value[0],
+ usb_tx_cfg.sample_rate);
+ return 0;
+}
+
+static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (usb_tx_cfg.bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ucontrol->value.integer.value[0] = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_tx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 3:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ case 2:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 1:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+ __func__, usb_tx_cfg.bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return rc;
+}
+
+static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+ int idx;
+
+ if (strnstr(kcontrol->id.name, "Display Port RX",
+ sizeof("Display Port RX")))
+ idx = DP_RX_IDX;
+ else {
+ pr_err("%s: unsupported BE: %s",
+ __func__, kcontrol->id.name);
+ idx = -EINVAL;
+ }
+
+ return idx;
+}
+
+static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ switch (ext_disp_rx_cfg[idx].bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n",
+ __func__, idx, ext_disp_rx_cfg[idx].bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n",
+ __func__, idx, ext_disp_rx_cfg[idx].bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.integer.value[0] =
+ ext_disp_rx_cfg[idx].channels - 2;
+
+ pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__,
+ idx, ext_disp_rx_cfg[idx].channels);
+
+ return 0;
+}
+
+static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ext_disp_rx_cfg[idx].channels =
+ ucontrol->value.integer.value[0] + 2;
+
+ pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__,
+ idx, ext_disp_rx_cfg[idx].channels);
+ return 1;
+}
+
+static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val;
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ switch (ext_disp_rx_cfg[idx].sample_rate) {
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 2;
+ break;
+
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 1;
+ break;
+
+ case SAMPLING_RATE_48KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__,
+ idx, ext_disp_rx_cfg[idx].sample_rate);
+
+ return 0;
+}
+
+static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 1:
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 0:
+ default:
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+
+ pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n",
+ __func__, ucontrol->value.integer.value[0], idx,
+ ext_disp_rx_cfg[idx].sample_rate);
+ return 0;
+}
+
+static int msm_qos_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = qos_vote_status;
+ return 0;
+}
+
+static int msm_qos_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_card *card = codec->component.card;
+ const char *fe_name = MSM_DAILINK_NAME(LowLatency);
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_pcm_substream *substream;
+ s32 usecs;
+
+ rtd = snd_soc_get_pcm_runtime(card, fe_name);
+ if (!rtd) {
+ pr_err("%s: fail to get pcm runtime for %s\n",
+ __func__, fe_name);
+ return -EINVAL;
+ }
+
+ substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (!substream) {
+ pr_err("%s: substream is null\n", __func__);
+ return -EINVAL;
+ }
+
+ qos_vote_status = ucontrol->value.enumerated.item[0];
+ if (qos_vote_status) {
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
+ if (!substream->runtime) {
+ pr_err("%s: runtime is null\n", __func__);
+ return -EINVAL;
+ }
+ usecs = MSM_LL_QOS_VALUE;
+ if (usecs >= 0)
+ pm_qos_add_request(&substream->latency_pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY, usecs);
+ } else {
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
+ }
+ return 0;
+}
+
+const struct snd_kcontrol_new msm_common_snd_controls[] = {
+ SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs,
+ proxy_rx_ch_get, proxy_rx_ch_put),
+ SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate,
+ aux_pcm_rx_sample_rate_get,
+ aux_pcm_rx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate,
+ aux_pcm_rx_sample_rate_get,
+ aux_pcm_rx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate,
+ aux_pcm_rx_sample_rate_get,
+ aux_pcm_rx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate,
+ aux_pcm_rx_sample_rate_get,
+ aux_pcm_rx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_MI2S_RX Format", prim_mi2s_rx_format,
+ mi2s_rx_format_get,
+ mi2s_rx_format_put),
+ SOC_ENUM_EXT("SEC_MI2S_RX Format", sec_mi2s_rx_format,
+ mi2s_rx_format_get,
+ mi2s_rx_format_put),
+ SOC_ENUM_EXT("TERT_MI2S_RX Format", tert_mi2s_rx_format,
+ mi2s_rx_format_get,
+ mi2s_rx_format_put),
+ SOC_ENUM_EXT("QUAT_MI2S_RX Format", quat_mi2s_rx_format,
+ mi2s_rx_format_get,
+ mi2s_rx_format_put),
+ SOC_ENUM_EXT("PRIM_MI2S_TX Format", prim_mi2s_tx_format,
+ mi2s_tx_format_get,
+ mi2s_tx_format_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX Format", sec_mi2s_tx_format,
+ mi2s_tx_format_get,
+ mi2s_tx_format_put),
+ SOC_ENUM_EXT("TERT_MI2S_TX Format", tert_mi2s_tx_format,
+ mi2s_tx_format_get,
+ mi2s_tx_format_put),
+ SOC_ENUM_EXT("QUAT_MI2S_TX Format", quat_mi2s_tx_format,
+ mi2s_tx_format_get,
+ mi2s_tx_format_put),
+ SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs,
+ usb_audio_rx_ch_get, usb_audio_rx_ch_put),
+ SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs,
+ usb_audio_tx_ch_get, usb_audio_tx_ch_put),
+ SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs,
+ ext_disp_rx_ch_get, ext_disp_rx_ch_put),
+ SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format,
+ usb_audio_rx_format_get, usb_audio_rx_format_put),
+ SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format,
+ usb_audio_tx_format_get, usb_audio_tx_format_put),
+ SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format,
+ ext_disp_rx_format_get, ext_disp_rx_format_put),
+ SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate,
+ usb_audio_rx_sample_rate_get,
+ usb_audio_rx_sample_rate_put),
+ SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate,
+ usb_audio_tx_sample_rate_get,
+ usb_audio_tx_sample_rate_put),
+ SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate,
+ ext_disp_rx_sample_rate_get,
+ ext_disp_rx_sample_rate_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+ tdm_rx_sample_rate_get,
+ tdm_rx_sample_rate_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format,
+ tdm_rx_format_get,
+ tdm_rx_format_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs,
+ tdm_rx_ch_get,
+ tdm_rx_ch_put),
+ SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+ tdm_rx_sample_rate_get,
+ tdm_rx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format,
+ tdm_rx_format_get,
+ tdm_rx_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs,
+ tdm_rx_ch_get,
+ tdm_rx_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+ tdm_rx_sample_rate_get,
+ tdm_rx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format,
+ tdm_rx_format_get,
+ tdm_rx_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs,
+ tdm_rx_ch_get,
+ tdm_rx_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+ tdm_rx_sample_rate_get,
+ tdm_rx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format,
+ tdm_rx_format_get,
+ tdm_rx_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs,
+ tdm_rx_ch_get,
+ tdm_rx_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
+
+ SOC_ENUM_EXT("MultiMedia5_RX QOS Vote", qos_vote, msm_qos_ctl_get,
+ msm_qos_ctl_put),
+
+};
+
+/**
+ * msm_common_snd_controls_size - to return controls size
+ *
+ * Return: returns size of common controls array
+ */
+int msm_common_snd_controls_size(void)
+{
+ return ARRAY_SIZE(msm_common_snd_controls);
+}
+EXPORT_SYMBOL(msm_common_snd_controls_size);
+
+static inline int param_is_mask(int p)
+{
+ return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p,
+ int n)
+{
+ return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
+{
+ if (bit >= SNDRV_MASK_MAX)
+ return;
+ if (param_is_mask(n)) {
+ struct snd_mask *m = param_to_mask(p, n);
+
+ m->bits[0] = 0;
+ m->bits[1] = 0;
+ m->bits[bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
+static int msm_ext_disp_get_idx_from_beid(int32_t be_id)
+{
+ int idx;
+
+ switch (be_id) {
+ case MSM_BACKEND_DAI_DISPLAY_PORT_RX:
+ idx = DP_RX_IDX;
+ break;
+ default:
+ pr_err("%s: Incorrect ext_disp be_id %d\n", __func__, be_id);
+ idx = -EINVAL;
+ break;
+ }
+
+ return idx;
+}
+
+/**
+ * msm_common_be_hw_params_fixup - updates settings of ALSA BE hw params.
+ *
+ * @rtd: runtime dailink instance
+ * @params: HW params of associated backend dailink.
+ *
+ * Returns 0.
+ */
+int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ int rc = 0;
+ int idx;
+
+ pr_debug("%s: format = %d, rate = %d\n",
+ __func__, params_format(params), params_rate(params));
+
+ switch (dai_link->be_id) {
+ case MSM_BACKEND_DAI_USB_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ usb_rx_cfg.bit_format);
+ rate->min = rate->max = usb_rx_cfg.sample_rate;
+ channels->min = channels->max = usb_rx_cfg.channels;
+ break;
+
+ case MSM_BACKEND_DAI_USB_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ usb_tx_cfg.bit_format);
+ rate->min = rate->max = usb_tx_cfg.sample_rate;
+ channels->min = channels->max = usb_tx_cfg.channels;
+ break;
+
+ case MSM_BACKEND_DAI_DISPLAY_PORT_RX:
+ idx = msm_ext_disp_get_idx_from_beid(dai_link->be_id);
+ if (IS_ERR_VALUE(idx)) {
+ pr_err("%s: Incorrect ext disp idx %d\n",
+ __func__, idx);
+ rc = idx;
+ break;
+ }
+
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ ext_disp_rx_cfg[idx].bit_format);
+ rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate;
+ channels->min = channels->max = ext_disp_rx_cfg[idx].channels;
+ break;
+
+ case MSM_BACKEND_DAI_AFE_PCM_RX:
+ channels->min = channels->max = proxy_rx_cfg.channels;
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ break;
+
+ case MSM_BACKEND_DAI_PRI_TDM_RX_0:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_PRI][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_PRI][TDM_0].bit_format);
+ rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_PRI_TDM_TX_0:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_PRI][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_PRI][TDM_0].bit_format);
+ rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_SEC_TDM_RX_0:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_SEC][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_SEC][TDM_0].bit_format);
+ rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_SEC_TDM_TX_0:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_SEC][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_SEC][TDM_0].bit_format);
+ rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_TERT_TDM_RX_0:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_TERT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_TERT][TDM_0].bit_format);
+ rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_TERT_TDM_TX_0:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_TERT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_TERT][TDM_0].bit_format);
+ rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_QUAT_TDM_RX_0:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_QUAT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format);
+ rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_QUAT_TDM_TX_0:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_QUAT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format);
+ rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_AUXPCM_RX:
+ rate->min = rate->max =
+ aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[PRIM_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[PRIM_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SEC_AUXPCM_RX:
+ rate->min = rate->max =
+ aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[SEC_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SEC_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[SEC_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_TERT_AUXPCM_RX:
+ rate->min = rate->max =
+ aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[TERT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_TERT_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[TERT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_QUAT_AUXPCM_RX:
+ rate->min = rate->max =
+ aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[QUAT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_QUAT_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[QUAT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_PRI_MI2S_RX:
+ rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[PRIM_MI2S].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[PRIM_MI2S].bit_format);
+ break;
+
+ case MSM_BACKEND_DAI_PRI_MI2S_TX:
+ rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[PRIM_MI2S].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[PRIM_MI2S].bit_format);
+ break;
+
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+ rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[SEC_MI2S].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[SEC_MI2S].bit_format);
+ break;
+
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+ rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[SEC_MI2S].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[SEC_MI2S].bit_format);
+ break;
+
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+ rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[TERT_MI2S].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[TERT_MI2S].bit_format);
+ break;
+
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+ rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[TERT_MI2S].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[TERT_MI2S].bit_format);
+ break;
+
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+ rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[QUAT_MI2S].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[QUAT_MI2S].bit_format);
+ break;
+
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+ rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[QUAT_MI2S].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[QUAT_MI2S].bit_format);
+ break;
+
+ default:
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_common_be_hw_params_fixup);
+
+/**
+ * msm_aux_pcm_snd_startup - startup ops of auxpcm.
+ *
+ * @substream: PCM stream pointer of associated backend dailink
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ dev_dbg(rtd->card->dev,
+ "%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
+ __func__, substream->name, substream->stream,
+ rtd->cpu_dai->name, rtd->cpu_dai->id);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_aux_pcm_snd_startup);
+
+/**
+ * msm_aux_pcm_snd_shutdown - shutdown ops of auxpcm.
+ *
+ * @substream: PCM stream pointer of associated backend dailink
+ */
+void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ dev_dbg(rtd->card->dev,
+ "%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
+ __func__,
+ substream->name, substream->stream,
+ rtd->cpu_dai->name, rtd->cpu_dai->id);
+}
+EXPORT_SYMBOL(msm_aux_pcm_snd_shutdown);
+
+static int msm_get_port_id(int be_id)
+{
+ int afe_port_id;
+
+ switch (be_id) {
+ case MSM_BACKEND_DAI_PRI_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_PRI_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+ break;
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+ break;
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+ break;
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+ break;
+ default:
+ pr_err("%s: Invalid be_id: %d\n", __func__, be_id);
+ afe_port_id = -EINVAL;
+ }
+
+ return afe_port_id;
+}
+
+static u32 get_mi2s_bits_per_sample(u32 bit_format)
+{
+ u32 bit_per_sample;
+
+ switch (bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bit_per_sample = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bit_per_sample = 16;
+ break;
+ }
+
+ return bit_per_sample;
+}
+
+static void update_mi2s_clk_val(int dai_id, int stream)
+{
+ u32 bit_per_sample;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ bit_per_sample =
+ get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format);
+ mi2s_clk[dai_id].clk_freq_in_hz =
+ mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+ } else {
+ bit_per_sample =
+ get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format);
+ mi2s_clk[dai_id].clk_freq_in_hz =
+ mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+ }
+}
+
+static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int port_id = 0;
+ int index = cpu_dai->id;
+
+ port_id = msm_get_port_id(rtd->dai_link->be_id);
+ if (IS_ERR_VALUE(port_id)) {
+ dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__);
+ ret = port_id;
+ goto done;
+ }
+
+ if (enable) {
+ update_mi2s_clk_val(index, substream->stream);
+ dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__,
+ mi2s_clk[index].clk_freq_in_hz);
+ }
+
+ mi2s_clk[index].enable = enable;
+ ret = afe_set_lpass_clock_v2(port_id,
+ &mi2s_clk[index]);
+ if (ret < 0) {
+ dev_err(rtd->card->dev,
+ "%s: afe lpass clock failed for port 0x%x , err:%d\n",
+ __func__, port_id, ret);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+/**
+ * msm_mi2s_snd_startup - startup ops of mi2s.
+ *
+ * @substream: PCM stream pointer of associated backend dailink
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int port_id = msm_get_port_id(rtd->dai_link->be_id);
+ int index = cpu_dai->id;
+ unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS;
+
+ dev_dbg(rtd->card->dev,
+ "%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
+ __func__, substream->name, substream->stream,
+ cpu_dai->name, cpu_dai->id);
+
+ if (index < PRIM_MI2S || index > QUAT_MI2S) {
+ ret = -EINVAL;
+ dev_err(rtd->card->dev,
+ "%s: CPU DAI id (%d) out of range\n",
+ __func__, cpu_dai->id);
+ goto done;
+ }
+ /*
+ * Muxtex protection in case the same MI2S
+ * interface using for both TX and RX so
+ * that the same clock won't be enable twice.
+ */
+ mutex_lock(&mi2s_intf_conf[index].lock);
+ if (++mi2s_intf_conf[index].ref_cnt == 1) {
+ /* Check if msm needs to provide the clock to the interface */
+ if (!mi2s_intf_conf[index].msm_is_mi2s_master) {
+ mi2s_clk[index].clk_id = mi2s_ebit_clk[index];
+ fmt = SND_SOC_DAIFMT_CBM_CFM;
+ }
+ ret = msm_mi2s_set_sclk(substream, true);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(rtd->card->dev,
+ "%s: afe lpass clock failed to enable MI2S clock, err:%d\n",
+ __func__, ret);
+ goto clean_up;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(rtd->card->dev,
+ "%s: set fmt cpu dai failed for MI2S (%d), err:%d\n",
+ __func__, index, ret);
+ goto clk_off;
+ }
+ if (mi2s_intf_conf[index].msm_is_ext_mclk) {
+ mi2s_mclk[index].enable = 1;
+ pr_debug("%s: Enabling mclk, clk_freq_in_hz = %u\n",
+ __func__, mi2s_mclk[index].clk_freq_in_hz);
+ ret = afe_set_lpass_clock_v2(port_id,
+ &mi2s_mclk[index]);
+ if (ret < 0) {
+ pr_err("%s: afe lpass mclk failed, err:%d\n",
+ __func__, ret);
+ goto clk_off;
+ }
+ }
+ }
+ mutex_unlock(&mi2s_intf_conf[index].lock);
+ return 0;
+clk_off:
+ if (IS_ERR_VALUE(ret))
+ msm_mi2s_set_sclk(substream, false);
+clean_up:
+ if (IS_ERR_VALUE(ret))
+ mi2s_intf_conf[index].ref_cnt--;
+ mutex_unlock(&mi2s_intf_conf[index].lock);
+done:
+ return ret;
+}
+EXPORT_SYMBOL(msm_mi2s_snd_startup);
+
+/**
+ * msm_mi2s_snd_shutdown - shutdown ops of mi2s.
+ *
+ * @substream: PCM stream pointer of associated backend dailink
+ */
+void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ int ret;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int port_id = msm_get_port_id(rtd->dai_link->be_id);
+ int index = rtd->cpu_dai->id;
+
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+ if (index < PRIM_MI2S || index > QUAT_MI2S) {
+ pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index);
+ return;
+ }
+
+ mutex_lock(&mi2s_intf_conf[index].lock);
+ if (--mi2s_intf_conf[index].ref_cnt == 0) {
+ ret = msm_mi2s_set_sclk(substream, false);
+ if (ret < 0)
+ pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n",
+ __func__, index, ret);
+ if (mi2s_intf_conf[index].msm_is_ext_mclk) {
+ mi2s_mclk[index].enable = 0;
+ pr_debug("%s: Disabling mclk, clk_freq_in_hz = %u\n",
+ __func__, mi2s_mclk[index].clk_freq_in_hz);
+ ret = afe_set_lpass_clock_v2(port_id,
+ &mi2s_mclk[index]);
+ if (ret < 0) {
+ pr_err("%s: mclk disable failed for MCLK (%d); ret=%d\n",
+ __func__, index, ret);
+ }
+ }
+ }
+ mutex_unlock(&mi2s_intf_conf[index].lock);
+}
+EXPORT_SYMBOL(msm_mi2s_snd_shutdown);
+
+/* Validate whether US EU switch is present or not */
+static int msm_prepare_us_euro(struct snd_soc_card *card)
+{
+ struct msm_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ if (pdata->us_euro_gpio >= 0) {
+ dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__,
+ pdata->us_euro_gpio);
+ ret = gpio_request(pdata->us_euro_gpio, "TASHA_CODEC_US_EURO");
+ if (ret) {
+ dev_err(card->dev,
+ "%s: Failed to request codec US/EURO gpio %d error %d\n",
+ __func__, pdata->us_euro_gpio, ret);
+ }
+ }
+
+ return ret;
+}
+
+static bool msm_swap_gnd_mic(struct snd_soc_codec *codec)
+{
+ struct snd_soc_card *card = codec->component.card;
+ struct msm_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+ int value = 0;
+
+ if (pdata->us_euro_gpio_p) {
+ value = msm_cdc_pinctrl_get_state(pdata->us_euro_gpio_p);
+ if (value)
+ msm_cdc_pinctrl_select_sleep_state(
+ pdata->us_euro_gpio_p);
+ else
+ msm_cdc_pinctrl_select_active_state(
+ pdata->us_euro_gpio_p);
+ } else if (pdata->us_euro_gpio >= 0) {
+ value = gpio_get_value_cansleep(pdata->us_euro_gpio);
+ gpio_set_value_cansleep(pdata->us_euro_gpio, !value);
+ }
+ pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value);
+ return true;
+}
+
+static int msm_populate_dai_link_component_of_node(
+ struct msm_asoc_mach_data *pdata,
+ struct snd_soc_card *card)
+{
+ int i, index, ret = 0;
+ struct device *cdev = card->dev;
+ struct snd_soc_dai_link *dai_link = card->dai_link;
+ struct device_node *phandle;
+
+ if (!cdev) {
+ pr_err("%s: Sound card device memory NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < card->num_links; i++) {
+ if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node)
+ continue;
+
+ /* populate platform_of_node for snd card dai links */
+ if (dai_link[i].platform_name &&
+ !dai_link[i].platform_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-platform-names",
+ dai_link[i].platform_name);
+ if (index < 0) {
+ pr_err("%s: No match found for platform name: %s\n",
+ __func__, dai_link[i].platform_name);
+ ret = index;
+ goto cpu_dai;
+ }
+ phandle = of_parse_phandle(cdev->of_node,
+ "asoc-platform",
+ index);
+ if (!phandle) {
+ pr_err("%s: retrieving phandle for platform %s, index %d failed\n",
+ __func__, dai_link[i].platform_name,
+ index);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].platform_of_node = phandle;
+ dai_link[i].platform_name = NULL;
+ }
+cpu_dai:
+ /* populate cpu_of_node for snd card dai links */
+ if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-cpu-names",
+ dai_link[i].cpu_dai_name);
+ if (index < 0)
+ goto codec_dai;
+ phandle = of_parse_phandle(cdev->of_node, "asoc-cpu",
+ index);
+ if (!phandle) {
+ pr_err("%s: retrieving phandle for cpu dai %s failed\n",
+ __func__, dai_link[i].cpu_dai_name);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].cpu_of_node = phandle;
+ dai_link[i].cpu_dai_name = NULL;
+ }
+codec_dai:
+ /* populate codec_of_node for snd card dai links */
+ if (dai_link[i].codec_name && !dai_link[i].codec_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-codec-names",
+ dai_link[i].codec_name);
+ if (index < 0)
+ continue;
+ phandle = of_parse_phandle(cdev->of_node, "asoc-codec",
+ index);
+ if (!phandle) {
+ pr_err("%s: retrieving phandle for codec dai %s failed\n",
+ __func__, dai_link[i].codec_name);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].codec_of_node = phandle;
+ dai_link[i].codec_name = NULL;
+ }
+ if (pdata->snd_card_val == INT_SND_CARD) {
+ if ((dai_link[i].be_id ==
+ MSM_BACKEND_DAI_INT0_MI2S_RX) ||
+ (dai_link[i].be_id ==
+ MSM_BACKEND_DAI_INT1_MI2S_RX) ||
+ (dai_link[i].be_id ==
+ MSM_BACKEND_DAI_INT2_MI2S_TX) ||
+ (dai_link[i].be_id ==
+ MSM_BACKEND_DAI_INT3_MI2S_TX)) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-codec-names",
+ MSM_INT_DIGITAL_CODEC);
+ phandle = of_parse_phandle(cdev->of_node,
+ "asoc-codec",
+ index);
+ dai_link[i].codecs[DIG_CDC].of_node = phandle;
+ index = of_property_match_string(cdev->of_node,
+ "asoc-codec-names",
+ PMIC_INT_ANALOG_CODEC);
+ phandle = of_parse_phandle(cdev->of_node,
+ "asoc-codec",
+ index);
+ dai_link[i].codecs[ANA_CDC].of_node = phandle;
+ }
+ }
+ }
+err:
+ return ret;
+}
+
+static int msm_wsa881x_init(struct snd_soc_component *component)
+{
+ u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106};
+ u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107};
+ unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200};
+ unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3};
+ struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+ struct msm_asoc_mach_data *pdata;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_codec_get_dapm(codec);
+
+ if (!codec) {
+ pr_err("%s codec is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!strcmp(component->name_prefix, "SpkrLeft")) {
+ dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n",
+ __func__, codec->component.name);
+ wsa881x_set_channel_map(codec, &spkleft_ports[0],
+ WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+ &ch_rate[0]);
+ if (dapm->component) {
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN");
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR");
+ }
+ } else if (!strcmp(component->name_prefix, "SpkrRight")) {
+ dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n",
+ __func__, codec->component.name);
+ wsa881x_set_channel_map(codec, &spkright_ports[0],
+ WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+ &ch_rate[0]);
+ if (dapm->component) {
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN");
+ snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR");
+ }
+ } else {
+ dev_err(codec->dev, "%s: wrong codec name %s\n", __func__,
+ codec->component.name);
+ return -EINVAL;
+ }
+
+
+ pdata = snd_soc_card_get_drvdata(component->card);
+ if (pdata && pdata->codec_root)
+ wsa881x_codec_info_create_codec_entry(pdata->codec_root,
+ codec);
+ return 0;
+}
+
+
+static int msm_init_wsa_dev(struct platform_device *pdev,
+ struct snd_soc_card *card)
+{
+ struct device_node *wsa_of_node;
+ u32 wsa_max_devs;
+ u32 wsa_dev_cnt;
+ char *dev_name_str = NULL;
+ struct msm_wsa881x_dev_info *wsa881x_dev_info;
+ const char *wsa_auxdev_name_prefix[1];
+ int found = 0;
+ int i;
+ int ret;
+
+ /* Get maximum WSA device count for this platform */
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,wsa-max-devs", &wsa_max_devs);
+ if (ret) {
+ dev_dbg(&pdev->dev,
+ "%s: wsa-max-devs property missing in DT %s, ret = %d\n",
+ __func__, pdev->dev.of_node->full_name, ret);
+ goto err_dt;
+ }
+ if (wsa_max_devs == 0) {
+ dev_warn(&pdev->dev,
+ "%s: Max WSA devices is 0 for this target?\n",
+ __func__);
+ goto err_dt;
+ }
+
+ /* Get count of WSA device phandles for this platform */
+ wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node,
+ "qcom,wsa-devs", NULL);
+ if (wsa_dev_cnt == -ENOENT) {
+ dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n",
+ __func__);
+ goto err_dt;
+ } else if (wsa_dev_cnt <= 0) {
+ dev_err(&pdev->dev,
+ "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n",
+ __func__, wsa_dev_cnt);
+ ret = -EINVAL;
+ goto err_dt;
+ }
+
+ /*
+ * Expect total phandles count to be NOT less than maximum possible
+ * WSA count. However, if it is less, then assign same value to
+ * max count as well.
+ */
+ if (wsa_dev_cnt < wsa_max_devs) {
+ dev_dbg(&pdev->dev,
+ "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n",
+ __func__, wsa_max_devs, wsa_dev_cnt);
+ wsa_max_devs = wsa_dev_cnt;
+ }
+
+ /* Make sure prefix string passed for each WSA device */
+ ret = of_property_count_strings(pdev->dev.of_node,
+ "qcom,wsa-aux-dev-prefix");
+ if (ret != wsa_dev_cnt) {
+ dev_err(&pdev->dev,
+ "%s: expecting %d wsa prefix. Defined only %d in DT\n",
+ __func__, wsa_dev_cnt, ret);
+ ret = -EINVAL;
+ goto err_dt;
+ }
+
+ /*
+ * Alloc mem to store phandle and index info of WSA device, if already
+ * registered with ALSA core
+ */
+ wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs,
+ sizeof(struct msm_wsa881x_dev_info),
+ GFP_KERNEL);
+ if (!wsa881x_dev_info) {
+ ret = -ENOMEM;
+ goto err_mem;
+ }
+
+ /*
+ * search and check whether all WSA devices are already
+ * registered with ALSA core or not. If found a node, store
+ * the node and the index in a local array of struct for later
+ * use.
+ */
+ for (i = 0; i < wsa_dev_cnt; i++) {
+ wsa_of_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,wsa-devs", i);
+ if (unlikely(!wsa_of_node)) {
+ /* we should not be here */
+ dev_err(&pdev->dev,
+ "%s: wsa dev node is not present\n",
+ __func__);
+ ret = -EINVAL;
+ goto err_dev_node;
+ }
+ if (soc_find_component(wsa_of_node, NULL)) {
+ /* WSA device registered with ALSA core */
+ wsa881x_dev_info[found].of_node = wsa_of_node;
+ wsa881x_dev_info[found].index = i;
+ found++;
+ if (found == wsa_max_devs)
+ break;
+ }
+ }
+
+ if (found < wsa_max_devs) {
+ dev_dbg(&pdev->dev,
+ "%s: failed to find %d components. Found only %d\n",
+ __func__, wsa_max_devs, found);
+ return -EPROBE_DEFER;
+ }
+ dev_info(&pdev->dev,
+ "%s: found %d wsa881x devices registered with ALSA core\n",
+ __func__, found);
+
+ card->num_aux_devs = wsa_max_devs;
+ card->num_configs = wsa_max_devs;
+
+ /* Alloc array of AUX devs struct */
+ msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+ sizeof(struct snd_soc_aux_dev),
+ GFP_KERNEL);
+ if (!msm_aux_dev) {
+ ret = -ENOMEM;
+ goto err_auxdev_mem;
+ }
+
+ /* Alloc array of codec conf struct */
+ msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+ sizeof(struct snd_soc_codec_conf),
+ GFP_KERNEL);
+ if (!msm_codec_conf) {
+ ret = -ENOMEM;
+ goto err_codec_conf;
+ }
+
+ for (i = 0; i < card->num_aux_devs; i++) {
+ dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN,
+ GFP_KERNEL);
+ if (!dev_name_str) {
+ ret = -ENOMEM;
+ goto err_dev_str;
+ }
+
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "qcom,wsa-aux-dev-prefix",
+ wsa881x_dev_info[i].index,
+ wsa_auxdev_name_prefix);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: failed to read wsa aux dev prefix, ret = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto err_dt_prop;
+ }
+
+ snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i);
+ msm_aux_dev[i].name = dev_name_str;
+ msm_aux_dev[i].codec_name = NULL;
+ msm_aux_dev[i].codec_of_node =
+ wsa881x_dev_info[i].of_node;
+ msm_aux_dev[i].init = msm_wsa881x_init;
+ msm_codec_conf[i].dev_name = NULL;
+ msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0];
+ msm_codec_conf[i].of_node = wsa881x_dev_info[i].of_node;
+ }
+ card->codec_conf = msm_codec_conf;
+ card->aux_dev = msm_aux_dev;
+
+ return 0;
+
+err_dt_prop:
+ devm_kfree(&pdev->dev, dev_name_str);
+err_dev_str:
+ devm_kfree(&pdev->dev, msm_codec_conf);
+err_codec_conf:
+ devm_kfree(&pdev->dev, msm_aux_dev);
+err_auxdev_mem:
+err_dev_node:
+ devm_kfree(&pdev->dev, wsa881x_dev_info);
+err_mem:
+err_dt:
+ return ret;
+}
+
+static void msm_free_auxdev_mem(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ int i;
+
+ if (card->num_aux_devs > 0) {
+ for (i = 0; i < card->num_aux_devs; i++) {
+ kfree(msm_aux_dev[i].codec_name);
+ kfree(msm_codec_conf[i].dev_name);
+ kfree(msm_codec_conf[i].name_prefix);
+ }
+ }
+}
+
+static void i2s_auxpcm_init(struct platform_device *pdev)
+{
+ int count;
+ u32 mi2s_master_slave[MI2S_MAX];
+ u32 mi2s_ext_mclk[MI2S_MAX];
+ int ret;
+
+ for (count = 0; count < MI2S_MAX; count++) {
+ mutex_init(&mi2s_intf_conf[count].lock);
+ mi2s_intf_conf[count].ref_cnt = 0;
+ }
+
+ ret = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-mi2s-master",
+ mi2s_master_slave, MI2S_MAX);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n",
+ __func__);
+ } else {
+ for (count = 0; count < MI2S_MAX; count++) {
+ mi2s_intf_conf[count].msm_is_mi2s_master =
+ mi2s_master_slave[count];
+ }
+ }
+
+ ret = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-mi2s-ext-mclk",
+ mi2s_ext_mclk, MI2S_MAX);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-ext-mclk in DT node\n",
+ __func__);
+ } else {
+ for (count = 0; count < MI2S_MAX; count++)
+ mi2s_intf_conf[count].msm_is_ext_mclk =
+ mi2s_ext_mclk[count];
+ }
+}
+
+static const struct of_device_id sdm660_asoc_machine_of_match[] = {
+ { .compatible = "qcom,sdm660-asoc-snd",
+ .data = "internal_codec"},
+ { .compatible = "qcom,sdm660-asoc-snd-tasha",
+ .data = "tasha_codec"},
+ { .compatible = "qcom,sdm660-asoc-snd-tavil",
+ .data = "tavil_codec"},
+ {},
+};
+
+static int msm_asoc_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = NULL;
+ struct msm_asoc_mach_data *pdata = NULL;
+ const char *mclk = "qcom,msm-mclk-freq";
+ int ret = -EINVAL, id;
+ const struct of_device_id *match;
+
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_asoc_mach_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ match = of_match_node(sdm660_asoc_machine_of_match,
+ pdev->dev.of_node);
+ if (!match)
+ goto err;
+
+ ret = of_property_read_u32(pdev->dev.of_node, mclk, &id);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: missing %s in dt node\n", __func__, mclk);
+ id = DEFAULT_MCLK_RATE;
+ }
+ pdata->mclk_freq = id;
+
+ if (!strcmp(match->data, "tasha_codec") ||
+ !strcmp(match->data, "tavil_codec")) {
+ if (!strcmp(match->data, "tasha_codec"))
+ pdata->snd_card_val = EXT_SND_CARD_TASHA;
+ else
+ pdata->snd_card_val = EXT_SND_CARD_TAVIL;
+ ret = msm_ext_cdc_init(pdev, pdata, &card, &mbhc_cfg);
+ if (ret)
+ goto err;
+ } else if (!strcmp(match->data, "internal_codec")) {
+ pdata->snd_card_val = INT_SND_CARD;
+ ret = msm_int_cdc_init(pdev, pdata, &card, &mbhc_cfg);
+ if (ret)
+ goto err;
+ } else {
+ dev_err(&pdev->dev,
+ "%s: Not a matching DT sound node\n", __func__);
+ goto err;
+ }
+ if (!card)
+ goto err;
+
+ if (pdata->snd_card_val == INT_SND_CARD) {
+ /*reading the gpio configurations from dtsi file*/
+ pdata->pdm_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,cdc-pdm-gpios", 0);
+ pdata->comp_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,cdc-comp-gpios", 0);
+ pdata->dmic_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,cdc-dmic-gpios", 0);
+ pdata->ext_spk_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,cdc-ext-spk-gpios", 0);
+ }
+
+ /*
+ * Parse US-Euro gpio info from DT. Report no error if us-euro
+ * entry is not found in DT file as some targets do not support
+ * US-Euro detection
+ */
+ pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,us-euro-gpios", 0);
+ if (!gpio_is_valid(pdata->us_euro_gpio))
+ pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,us-euro-gpios", 0);
+ if (!gpio_is_valid(pdata->us_euro_gpio) && (!pdata->us_euro_gpio_p)) {
+ dev_dbg(&pdev->dev, "property %s not detected in node %s",
+ "qcom,us-euro-gpios", pdev->dev.of_node->full_name);
+ } else {
+ dev_dbg(&pdev->dev, "%s detected",
+ "qcom,us-euro-gpios");
+ mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic;
+ }
+
+ ret = msm_prepare_us_euro(card);
+ if (ret)
+ dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n",
+ ret);
+
+ i2s_auxpcm_init(pdev);
+
+ ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing");
+ if (ret)
+ goto err;
+
+ ret = msm_populate_dai_link_component_of_node(pdata, card);
+ if (ret) {
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
+
+ if (!of_property_read_bool(pdev->dev.of_node, "qcom,wsa-disable")) {
+ ret = msm_init_wsa_dev(pdev, card);
+ if (ret)
+ goto err;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret == -EPROBE_DEFER) {
+ if (codec_reg_done) {
+ /*
+ * return failure as EINVAL since other codec
+ * registered sound card successfully.
+ * This avoids any further probe calls.
+ */
+ ret = -EINVAL;
+ }
+ goto err;
+ } else if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err;
+ }
+ if (pdata->snd_card_val != INT_SND_CARD)
+ msm_ext_register_audio_notifier(pdev);
+
+ return 0;
+err:
+ if (pdata->us_euro_gpio > 0) {
+ dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n",
+ __func__, pdata->us_euro_gpio);
+ pdata->us_euro_gpio = 0;
+ }
+ if (pdata->hph_en1_gpio > 0) {
+ dev_dbg(&pdev->dev, "%s free hph_en1_gpio %d\n",
+ __func__, pdata->hph_en1_gpio);
+ gpio_free(pdata->hph_en1_gpio);
+ pdata->hph_en1_gpio = 0;
+ }
+ if (pdata->hph_en0_gpio > 0) {
+ dev_dbg(&pdev->dev, "%s free hph_en0_gpio %d\n",
+ __func__, pdata->hph_en0_gpio);
+ gpio_free(pdata->hph_en0_gpio);
+ pdata->hph_en0_gpio = 0;
+ }
+ if (pdata->snd_card_val != INT_SND_CARD)
+ msm_ext_cdc_deinit(pdata);
+ devm_kfree(&pdev->dev, pdata);
+ return ret;
+}
+
+static int msm_asoc_machine_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+
+ if (pdata->snd_card_val == INT_SND_CARD)
+ mutex_destroy(&pdata->cdc_int_mclk0_mutex);
+ else
+ msm_ext_cdc_deinit(pdata);
+ msm_free_auxdev_mem(pdev);
+
+ gpio_free(pdata->us_euro_gpio);
+ gpio_free(pdata->hph_en1_gpio);
+ gpio_free(pdata->hph_en0_gpio);
+ snd_soc_unregister_card(card);
+ return 0;
+}
+
+static struct platform_driver sdm660_asoc_machine_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = sdm660_asoc_machine_of_match,
+ },
+ .probe = msm_asoc_machine_probe,
+ .remove = msm_asoc_machine_remove,
+};
+module_platform_driver(sdm660_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, sdm660_asoc_machine_of_match);
diff --git a/sound/soc/msm/sdm660-common.h b/sound/soc/msm/sdm660-common.h
new file mode 100644
index 000000000000..549c3879d1af
--- /dev/null
+++ b/sound/soc/msm/sdm660-common.h
@@ -0,0 +1,128 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_COMMON
+#define __MSM_COMMON
+
+#include <sound/soc.h>
+#include <sound/q6afe-v2.h>
+#include "../codecs/wcd-mbhc-v2.h"
+
+#define DEFAULT_MCLK_RATE 9600000
+#define NATIVE_MCLK_RATE 11289600
+
+#define SAMPLING_RATE_8KHZ 8000
+#define SAMPLING_RATE_11P025KHZ 11025
+#define SAMPLING_RATE_16KHZ 16000
+#define SAMPLING_RATE_22P05KHZ 22050
+#define SAMPLING_RATE_32KHZ 32000
+#define SAMPLING_RATE_44P1KHZ 44100
+#define SAMPLING_RATE_48KHZ 48000
+#define SAMPLING_RATE_88P2KHZ 88200
+#define SAMPLING_RATE_96KHZ 96000
+#define SAMPLING_RATE_176P4KHZ 176400
+#define SAMPLING_RATE_192KHZ 192000
+#define SAMPLING_RATE_352P8KHZ 352800
+#define SAMPLING_RATE_384KHZ 384000
+
+#define TDM_CHANNEL_MAX 8
+#define TDM_SLOT_OFFSET_MAX 8
+
+enum {
+ TDM_0 = 0,
+ TDM_1,
+ TDM_2,
+ TDM_3,
+ TDM_4,
+ TDM_5,
+ TDM_6,
+ TDM_7,
+ TDM_PORT_MAX,
+};
+
+enum {
+ TDM_PRI = 0,
+ TDM_SEC,
+ TDM_TERT,
+ TDM_QUAT,
+ TDM_INTERFACE_MAX,
+};
+
+struct tdm_port {
+ u32 mode;
+ u32 channel;
+};
+
+enum {
+ DIG_CDC,
+ ANA_CDC,
+ CODECS_MAX,
+};
+
+extern const struct snd_kcontrol_new msm_common_snd_controls[];
+extern bool codec_reg_done;
+struct sdm660_codec {
+ void* (*get_afe_config_fn)(struct snd_soc_codec *codec,
+ enum afe_config_type config_type);
+};
+
+enum {
+ INT_SND_CARD,
+ EXT_SND_CARD_TASHA,
+ EXT_SND_CARD_TAVIL,
+};
+
+struct msm_snd_interrupt {
+ void __iomem *mpm_wakeup;
+ void __iomem *intr1_cfg_apps;
+ void __iomem *lpi_gpio_intr_cfg;
+ void __iomem *lpi_gpio_cfg;
+ void __iomem *lpi_gpio_inout;
+};
+
+struct msm_asoc_mach_data {
+ int us_euro_gpio; /* used by gpio driver API */
+ int hph_en1_gpio;
+ int hph_en0_gpio;
+ struct device_node *us_euro_gpio_p; /* used by pinctrl API */
+ struct device_node *hph_en1_gpio_p; /* used by pinctrl API */
+ struct device_node *hph_en0_gpio_p; /* used by pinctrl API */
+ struct device_node *pdm_gpio_p; /* used by pinctrl API */
+ struct device_node *comp_gpio_p; /* used by pinctrl API */
+ struct device_node *dmic_gpio_p; /* used by pinctrl API */
+ struct device_node *ext_spk_gpio_p; /* used by pinctrl API */
+ struct snd_soc_codec *codec;
+ struct sdm660_codec sdm660_codec_fn;
+ struct snd_info_entry *codec_root;
+ int spk_ext_pa_gpio;
+ int mclk_freq;
+ bool native_clk_set;
+ int lb_mode;
+ int snd_card_val;
+ u8 micbias1_cap_mode;
+ u8 micbias2_cap_mode;
+ atomic_t int_mclk0_rsc_ref;
+ atomic_t int_mclk0_enabled;
+ struct mutex cdc_int_mclk0_mutex;
+ struct delayed_work disable_int_mclk0_work;
+ struct afe_clk_set digital_cdc_core_clk;
+ struct msm_snd_interrupt msm_snd_intr_lpi;
+};
+
+int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params);
+int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream);
+void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream);
+int msm_mi2s_snd_startup(struct snd_pcm_substream *substream);
+void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream);
+int msm_common_snd_controls_size(void);
+#endif
diff --git a/sound/soc/msm/sdm660-ext-dai-links.c b/sound/soc/msm/sdm660-ext-dai-links.c
new file mode 100644
index 000000000000..fb0042f48d84
--- /dev/null
+++ b/sound/soc/msm/sdm660-ext-dai-links.c
@@ -0,0 +1,2094 @@
+/* Copyright (c) 2015-2018, 2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "../codecs/wcd9335.h"
+#include "sdm660-common.h"
+#include "sdm660-external.h"
+#include <linux/pm_qos.h>
+
+#define DEV_NAME_STR_LEN 32
+#define __CHIPSET__ "SDM660 "
+#define MSM_DAILINK_NAME(name) (__CHIPSET__#name)
+
+#define WCN_CDC_SLIM_RX_CH_MAX 2
+#define WCN_CDC_SLIM_TX_CH_MAX 3
+#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */
+
+static struct snd_soc_card snd_soc_card_msm_card_tavil;
+static struct snd_soc_card snd_soc_card_msm_card_tasha;
+
+static struct snd_soc_ops msm_ext_slimbus_be_ops = {
+ .hw_params = msm_snd_hw_params,
+};
+
+static struct snd_soc_ops msm_ext_cpe_ops = {
+ .hw_params = msm_snd_cpe_hw_params,
+};
+
+static struct snd_soc_ops msm_ext_slimbus_2_be_ops = {
+ .hw_params = msm_ext_slimbus_2_hw_params,
+};
+
+static struct snd_soc_ops msm_mi2s_be_ops = {
+ .startup = msm_mi2s_snd_startup,
+ .shutdown = msm_mi2s_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_aux_pcm_be_ops = {
+ .startup = msm_aux_pcm_snd_startup,
+ .shutdown = msm_aux_pcm_snd_shutdown,
+};
+
+static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd)
+{
+ unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158};
+ unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161};
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+}
+
+static int msm_wcn_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX];
+ u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+ int ret;
+
+ dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__,
+ codec_dai->name, codec_dai->id);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret) {
+ dev_err(rtd->dev,
+ "%s: failed to get BTFM codec chan map\n, err:%d\n",
+ __func__, ret);
+ goto exit;
+ }
+
+ dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) be_id %d\n",
+ __func__, tx_ch_cnt, dai_link->be_id);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch);
+ if (ret)
+ dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+
+exit:
+ return ret;
+}
+
+static struct snd_soc_ops msm_wcn_ops = {
+ .hw_params = msm_wcn_hw_params,
+};
+
+/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */
+static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = {
+ {0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */
+};
+
+static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width,
+ int slots)
+{
+ unsigned int slot_mask = 0;
+ int i, j;
+ unsigned int *slot_offset;
+
+ for (i = TDM_0; i < TDM_PORT_MAX; i++) {
+ slot_offset = tdm_slot_offset[i];
+
+ for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) {
+ if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ slot_mask |=
+ (1 << ((slot_offset[j] * 8) / slot_width));
+ else
+ break;
+ }
+ }
+
+ return slot_mask;
+}
+
+static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ int channels, slot_width, slots;
+ unsigned int slot_mask;
+ unsigned int *slot_offset;
+ int offset_channels = 0;
+ int i;
+
+ pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+ channels = params_channels(params);
+ switch (channels) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /*
+ * up to 8 channels HW config should
+ * use 32 bit slot width for max support of
+ * stream bit width. (slot_width > bit_width)
+ */
+ slot_width = 32;
+ break;
+ default:
+ pr_err("%s: invalid param format 0x%x\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+ slots = 8;
+ slot_mask = tdm_param_set_slot_mask(cpu_dai->id,
+ slot_width,
+ slots);
+ if (!slot_mask) {
+ pr_err("%s: invalid slot_mask 0x%x\n",
+ __func__, slot_mask);
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_err("%s: invalid param channels %d\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+ /* currently only supporting TDM_RX_0 and TDM_TX_0 */
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ slot_offset = tdm_slot_offset[TDM_0];
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ offset_channels++;
+ else
+ break;
+ }
+
+ if (offset_channels == 0) {
+ pr_err("%s: slot offset not supported, offset_channels %d\n",
+ __func__, offset_channels);
+ return -EINVAL;
+ }
+
+ if (channels > offset_channels) {
+ pr_err("%s: channels %d exceed offset_channels %d\n",
+ __func__, channels, offset_channels);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ channels, slot_offset);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, channels,
+ slot_offset, 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+end:
+ return ret;
+}
+
+static struct snd_soc_ops msm_tdm_be_ops = {
+ .hw_params = msm_tdm_snd_hw_params
+};
+
+static int msm_fe_qos_prepare(struct snd_pcm_substream *substream)
+{
+ cpumask_t mask;
+
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
+
+ cpumask_clear(&mask);
+ cpumask_set_cpu(1, &mask); /* affine to core 1 */
+ cpumask_set_cpu(2, &mask); /* affine to core 2 */
+ cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask);
+
+ substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES;
+
+ pm_qos_add_request(&substream->latency_pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY,
+ MSM_LL_QOS_VALUE);
+ return 0;
+}
+
+static struct snd_soc_ops msm_fe_qos_ops = {
+ .prepare = msm_fe_qos_prepare,
+};
+
+static struct snd_soc_dai_link msm_ext_tasha_fe_dai[] = {
+ /* tasha_vifeedback for speaker protection */
+ {
+ .name = LPASS_BE_SLIMBUS_4_TX,
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_vifeedback",
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ },
+ /* Ultrasound RX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Playback",
+ .stream_name = "SLIMBUS_2 Hostless Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16388",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx2",
+ .ignore_suspend = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm_ext_slimbus_2_be_ops,
+ },
+ /* Ultrasound TX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Capture",
+ .stream_name = "SLIMBUS_2 Hostless Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16389",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx2",
+ .ignore_suspend = 1,
+ .dpcm_capture = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm_ext_slimbus_2_be_ops,
+ },
+ /* CPE LSM direct dai-link */
+ {
+ .name = "CPE Listen service",
+ .stream_name = "CPE Listen Audio Service",
+ .cpu_dai_name = "msm-dai-slim",
+ .platform_name = "msm-cpe-lsm",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .dpcm_capture = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "tasha_mad1",
+ .codec_name = "tasha_codec",
+ .ops = &msm_ext_cpe_ops,
+ },
+ {
+ .name = "SLIMBUS_6 Hostless Playback",
+ .stream_name = "SLIMBUS_6 Hostless",
+ .cpu_dai_name = "SLIMBUS6_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ /* CPE LSM EC PP direct dai-link */
+ {
+ .name = "CPE Listen service ECPP",
+ .stream_name = "CPE Listen Audio Service ECPP",
+ .cpu_dai_name = "CPE_LSM_NOHOST",
+ .platform_name = "msm-cpe-lsm.3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "tasha_cpe",
+ .codec_name = "tasha_codec",
+ },
+};
+
+static struct snd_soc_dai_link msm_ext_tavil_fe_dai[] = {
+ {
+ .name = LPASS_BE_SLIMBUS_4_TX,
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_vifeedback",
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ },
+ /* Ultrasound RX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Playback",
+ .stream_name = "SLIMBUS_2 Hostless Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16388",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm_ext_slimbus_2_be_ops,
+ },
+ /* Ultrasound TX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Capture",
+ .stream_name = "SLIMBUS_2 Hostless Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16389",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_tx2",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm_ext_slimbus_2_be_ops,
+ },
+};
+
+static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = {
+ /* Backend DAI Links */
+ {
+ .name = LPASS_BE_SLIMBUS_0_RX,
+ .stream_name = "Slimbus Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16384",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ .init = &msm_audrx_init,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_ext_slimbus_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_0_TX,
+ .stream_name = "Slimbus Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16385",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ops = &msm_ext_slimbus_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_RX,
+ .stream_name = "Slimbus1 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16386",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_TX,
+ .stream_name = "Slimbus1 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16387",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx3",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_RX,
+ .stream_name = "Slimbus3 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16390",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_TX,
+ .stream_name = "Slimbus3 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16391",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_4_RX,
+ .stream_name = "Slimbus4 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16392",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_5_RX,
+ .stream_name = "Slimbus5 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16394",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx3",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ /* MAD BE */
+ {
+ .name = LPASS_BE_SLIMBUS_5_TX,
+ .stream_name = "Slimbus5 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16395",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mad1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_6_RX,
+ .stream_name = "Slimbus6 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16396",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx4",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = {
+ {
+ .name = LPASS_BE_SLIMBUS_0_RX,
+ .stream_name = "Slimbus Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16384",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ .init = &msm_audrx_init,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_ext_slimbus_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_0_TX,
+ .stream_name = "Slimbus Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16385",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ops = &msm_ext_slimbus_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_RX,
+ .stream_name = "Slimbus1 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16386",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_TX,
+ .stream_name = "Slimbus1 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16387",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_tx3",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_2_RX,
+ .stream_name = "Slimbus2 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16388",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx2",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_2_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_RX,
+ .stream_name = "Slimbus3 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16390",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_TX,
+ .stream_name = "Slimbus3 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16391",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_4_RX,
+ .stream_name = "Slimbus4 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16392",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_5_RX,
+ .stream_name = "Slimbus5 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16394",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx3",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ /* MAD BE */
+ {
+ .name = LPASS_BE_SLIMBUS_5_TX,
+ .stream_name = "Slimbus5 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16395",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_mad1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_6_RX,
+ .stream_name = "Slimbus6 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16396",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_rx4",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_ext_slimbus_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_ext_common_fe_dai[] = {
+ /* FrontEnd DAI Links */
+ {/* hw:x,0 */
+ .name = MSM_DAILINK_NAME(Media1),
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+ {/* hw:x,1 */
+ .name = MSM_DAILINK_NAME(Media2),
+ .stream_name = "MultiMedia2",
+ .cpu_dai_name = "MultiMedia2",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+ },
+ {/* hw:x,2 */
+ .name = "VoiceMMode1",
+ .stream_name = "VoiceMMode1",
+ .cpu_dai_name = "VoiceMMode1",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE1,
+ },
+ {/* hw:x,3 */
+ .name = "MSM VoIP",
+ .stream_name = "VoIP",
+ .cpu_dai_name = "VoIP",
+ .platform_name = "msm-voip-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_VOIP,
+ },
+ {/* hw:x,4 */
+ .name = MSM_DAILINK_NAME(ULL),
+ .stream_name = "ULL",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-dsp.2",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+ /* Hostless PCM purpose */
+ {/* hw:x,5 */
+ .name = "SLIMBUS_0 Hostless",
+ .stream_name = "SLIMBUS_0 Hostless",
+ .cpu_dai_name = "SLIMBUS0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* This dai link has MI2S support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,6 */
+ .name = "MSM AFE-PCM RX",
+ .stream_name = "AFE-PROXY RX",
+ .cpu_dai_name = "msm-dai-q6-dev.241",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ },
+ {/* hw:x,7 */
+ .name = "MSM AFE-PCM TX",
+ .stream_name = "AFE-PROXY TX",
+ .cpu_dai_name = "msm-dai-q6-dev.240",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ },
+ {/* hw:x,8 */
+ .name = MSM_DAILINK_NAME(Compress1),
+ .stream_name = "Compress1",
+ .cpu_dai_name = "MultiMedia4",
+ .platform_name = "msm-compress-dsp",
+ .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS,
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+ },
+ {/* hw:x,9*/
+ .name = "AUXPCM Hostless",
+ .stream_name = "AUXPCM Hostless",
+ .cpu_dai_name = "AUXPCM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,10 */
+ .name = "SLIMBUS_1 Hostless",
+ .stream_name = "SLIMBUS_1 Hostless",
+ .cpu_dai_name = "SLIMBUS1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1, /* dai link has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,11 */
+ .name = "SLIMBUS_3 Hostless",
+ .stream_name = "SLIMBUS_3 Hostless",
+ .cpu_dai_name = "SLIMBUS3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1, /* dai link has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,12 */
+ .name = "SLIMBUS_4 Hostless",
+ .stream_name = "SLIMBUS_4 Hostless",
+ .cpu_dai_name = "SLIMBUS4_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1, /* dai link has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,13 */
+ .name = MSM_DAILINK_NAME(LowLatency),
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+ .ops = &msm_fe_qos_ops,
+ },
+ /* LSM FE */
+ {/* hw:x,14 */
+ .name = "Listen 1 Audio Service",
+ .stream_name = "Listen 1 Audio Service",
+ .cpu_dai_name = "LSM1",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM1,
+ },
+ {/* hw:x,15 */
+ .name = MSM_DAILINK_NAME(Compress2),
+ .stream_name = "Compress2",
+ .cpu_dai_name = "MultiMedia7",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+ },
+ {/* hw:x,16 */
+ .name = MSM_DAILINK_NAME(Compress3),
+ .stream_name = "Compress3",
+ .cpu_dai_name = "MultiMedia10",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10,
+ },
+ {/* hw:x,17 */
+ .name = MSM_DAILINK_NAME(ULL_NOIRQ),
+ .stream_name = "MM_NOIRQ",
+ .cpu_dai_name = "MultiMedia8",
+ .platform_name = "msm-pcm-dsp-noirq",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+ .ops = &msm_fe_qos_ops,
+ },
+ {/* hw:x,18 */
+ .name = "HDMI_RX_HOSTLESS",
+ .stream_name = "HDMI_RX_HOSTLESS",
+ .cpu_dai_name = "HDMI_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,19 */
+ .name = "VoiceMMode2",
+ .stream_name = "VoiceMMode2",
+ .cpu_dai_name = "VoiceMMode2",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE2,
+ },
+ {/* hw:x,20 */
+ .name = "Listen 2 Audio Service",
+ .stream_name = "Listen 2 Audio Service",
+ .cpu_dai_name = "LSM2",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM2,
+ },
+ {/* hw:x,21 */
+ .name = "Listen 3 Audio Service",
+ .stream_name = "Listen 3 Audio Service",
+ .cpu_dai_name = "LSM3",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM3,
+ },
+ {/* hw:x,22 */
+ .name = "Listen 4 Audio Service",
+ .stream_name = "Listen 4 Audio Service",
+ .cpu_dai_name = "LSM4",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM4,
+ },
+ {/* hw:x,23 */
+ .name = "Listen 5 Audio Service",
+ .stream_name = "Listen 5 Audio Service",
+ .cpu_dai_name = "LSM5",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM5,
+ },
+ {/* hw:x,24 */
+ .name = "Listen 6 Audio Service",
+ .stream_name = "Listen 6 Audio Service",
+ .cpu_dai_name = "LSM6",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM6
+ },
+ {/* hw:x,25 */
+ .name = "Listen 7 Audio Service",
+ .stream_name = "Listen 7 Audio Service",
+ .cpu_dai_name = "LSM7",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM7,
+ },
+ {/* hw:x,26 */
+ .name = "Listen 8 Audio Service",
+ .stream_name = "Listen 8 Audio Service",
+ .cpu_dai_name = "LSM8",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM8,
+ },
+ {/* hw:x,27 */
+ .name = MSM_DAILINK_NAME(Media9),
+ .stream_name = "MultiMedia9",
+ .cpu_dai_name = "MultiMedia9",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+ },
+ {/* hw:x,28 */
+ .name = MSM_DAILINK_NAME(Compress4),
+ .stream_name = "Compress4",
+ .cpu_dai_name = "MultiMedia11",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11,
+ },
+ {/* hw:x,29 */
+ .name = MSM_DAILINK_NAME(Compress5),
+ .stream_name = "Compress5",
+ .cpu_dai_name = "MultiMedia12",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12,
+ },
+ {/* hw:x,30 */
+ .name = MSM_DAILINK_NAME(Compress6),
+ .stream_name = "Compress6",
+ .cpu_dai_name = "MultiMedia13",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13,
+ },
+ {/* hw:x,31 */
+ .name = MSM_DAILINK_NAME(Compress7),
+ .stream_name = "Compress7",
+ .cpu_dai_name = "MultiMedia14",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+ },
+ {/* hw:x,32 */
+ .name = MSM_DAILINK_NAME(Compress8),
+ .stream_name = "Compress8",
+ .cpu_dai_name = "MultiMedia15",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
+ },
+ {/* hw:x,33 */
+ .name = MSM_DAILINK_NAME(ULL_NOIRQ_2),
+ .stream_name = "MM_NOIRQ_2",
+ .cpu_dai_name = "MultiMedia16",
+ .platform_name = "msm-pcm-dsp-noirq",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16,
+ },
+ {/* hw:x,34 */
+ .name = "SLIMBUS_8 Hostless",
+ .stream_name = "SLIMBUS8_HOSTLESS Capture",
+ .cpu_dai_name = "SLIMBUS8_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,35 */
+ .name = "SLIMBUS7 Hostless",
+ .stream_name = "SLIMBUS7 Hostless",
+ .cpu_dai_name = "SLIMBUS7_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,36 */
+ .name = "SDM660 HFP TX",
+ .stream_name = "MultiMedia6",
+ .cpu_dai_name = "MultiMedia6",
+ .platform_name = "msm-pcm-loopback",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,
+ },
+};
+
+static struct snd_soc_dai_link msm_ext_common_be_dai[] = {
+ {
+ .name = LPASS_BE_AFE_PCM_RX,
+ .stream_name = "AFE Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.224",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_AFE_PCM_TX,
+ .stream_name = "AFE Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.225",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Uplink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_TX,
+ .stream_name = "Voice Uplink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32772",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Downlink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_RX,
+ .stream_name = "Voice Downlink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32771",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE_PLAYBACK_TX,
+ .stream_name = "Voice Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32773",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music 2 BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE2_PLAYBACK_TX,
+ .stream_name = "Voice2 Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32770",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Proxy Tx BACK END DAI Link */
+ {
+ .name = LPASS_BE_PROXY_TX,
+ .stream_name = "Proxy Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.8195",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PROXY_TX,
+ .ignore_suspend = 1,
+ },
+ /* Proxy Rx BACK END DAI Link */
+ {
+ .name = LPASS_BE_PROXY_RX,
+ .stream_name = "Proxy Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.8194",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PROXY_RX,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+
+ {
+ .name = LPASS_BE_USB_AUDIO_RX,
+ .stream_name = "USB Audio Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.28672",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_USB_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_USB_AUDIO_TX,
+ .stream_name = "USB Audio Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.28673",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_USB_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_RX_0,
+ .stream_name = "Primary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36864",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_0,
+ .stream_name = "Primary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36865",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_RX_0,
+ .stream_name = "Secondary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36880",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_0,
+ .stream_name = "Secondary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36881",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_0,
+ .stream_name = "Tertiary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36896",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_0,
+ .stream_name = "Tertiary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36897",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_0,
+ .stream_name = "Quaternary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36912",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_0,
+ .stream_name = "Quaternary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36913",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = {
+ {
+ .name = LPASS_BE_PRI_MI2S_RX,
+ .stream_name = "Primary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_MI2S_TX,
+ .stream_name = "Primary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_RX,
+ .stream_name = "Secondary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_TX,
+ .stream_name = "Secondary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_RX,
+ .stream_name = "Tertiary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_TX,
+ .stream_name = "Tertiary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_RX,
+ .stream_name = "Quaternary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_TX,
+ .stream_name = "Quaternary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = {
+ /* Primary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Secondary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_SEC_AUXPCM_RX,
+ .stream_name = "Sec AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_SEC_AUXPCM_TX,
+ .stream_name = "Sec AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Tertiary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_TERT_AUXPCM_RX,
+ .stream_name = "Tert AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_TERT_AUXPCM_TX,
+ .stream_name = "Tert AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Quaternary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_QUAT_AUXPCM_RX,
+ .stream_name = "Quat AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.4",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_QUAT_AUXPCM_TX,
+ .stream_name = "Quat AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.4",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+};
+
+static struct snd_soc_dai_link msm_wcn_be_dai_links[] = {
+ {
+ .name = LPASS_BE_SLIMBUS_7_RX,
+ .stream_name = "Slimbus7 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16398",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "btfmslim_slave",
+ /* BT codec driver determines capabilities based on
+ * dai name, bt codecdai name should always contains
+ * supported usecase information
+ */
+ .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_wcn_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_7_TX,
+ .stream_name = "Slimbus7 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16399",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "btfmslim_slave",
+ .codec_dai_name = "btfm_bt_sco_slim_tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .ops = &msm_wcn_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_8_TX,
+ .stream_name = "Slimbus8 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16401",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "btfmslim_slave",
+ .codec_dai_name = "btfm_fm_slim_tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ .be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+ .init = &msm_wcn_init,
+ .ops = &msm_wcn_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link ext_disp_be_dai_link[] = {
+ /* DISP PORT BACK END DAI Link */
+ {
+ .name = LPASS_BE_DISPLAY_PORT,
+ .stream_name = "Display Port Playback",
+ .cpu_dai_name = "msm-dai-q6-dp.24608",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-ext-disp-audio-codec-rx",
+ .codec_dai_name = "msm_dp_audio_codec_rx_dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_ext_tasha_dai_links[
+ARRAY_SIZE(msm_ext_common_fe_dai) +
+ARRAY_SIZE(msm_ext_tasha_fe_dai) +
+ARRAY_SIZE(msm_ext_common_be_dai) +
+ARRAY_SIZE(msm_ext_tasha_be_dai) +
+ARRAY_SIZE(msm_mi2s_be_dai_links) +
+ARRAY_SIZE(msm_auxpcm_be_dai_links) +
+ARRAY_SIZE(msm_wcn_be_dai_links) +
+ARRAY_SIZE(ext_disp_be_dai_link)];
+
+static struct snd_soc_dai_link msm_ext_tavil_dai_links[
+ARRAY_SIZE(msm_ext_common_fe_dai) +
+ARRAY_SIZE(msm_ext_tavil_fe_dai) +
+ARRAY_SIZE(msm_ext_common_be_dai) +
+ARRAY_SIZE(msm_ext_tavil_be_dai) +
+ARRAY_SIZE(msm_mi2s_be_dai_links) +
+ARRAY_SIZE(msm_auxpcm_be_dai_links) +
+ARRAY_SIZE(msm_wcn_be_dai_links) +
+ARRAY_SIZE(ext_disp_be_dai_link)];
+
+/**
+ * populate_snd_card_dailinks - prepares dailink array and initializes card.
+ *
+ * @dev: device handle
+ *
+ * Returns card on success or NULL on failure.
+ */
+struct snd_soc_card *populate_snd_card_dailinks(struct device *dev,
+ int snd_card_val)
+{
+ struct snd_soc_card *card;
+ struct snd_soc_dai_link *msm_ext_dai_links = NULL;
+ int ret, len1, len2, len3, len4;
+ enum codec_variant codec_ver = 0;
+
+ if (snd_card_val == EXT_SND_CARD_TASHA) {
+ card = &snd_soc_card_msm_card_tasha;
+ } else if (snd_card_val == EXT_SND_CARD_TAVIL) {
+ card = &snd_soc_card_msm_card_tavil;
+ } else {
+ dev_err(dev, "%s: failing as no matching card name\n",
+ __func__);
+ return NULL;
+ }
+
+ card->dev = dev;
+ ret = snd_soc_of_parse_card_name(card, "qcom,model");
+ if (ret) {
+ dev_err(dev, "%s: parse card name failed, err:%d\n",
+ __func__, ret);
+ return NULL;
+ }
+
+ if (strnstr(card->name, "tasha", strlen(card->name))) {
+ codec_ver = tasha_codec_ver();
+ if (codec_ver == WCD9326)
+ card->name = "sdm660-tashalite-snd-card";
+
+ len1 = ARRAY_SIZE(msm_ext_common_fe_dai);
+ len2 = len1 + ARRAY_SIZE(msm_ext_tasha_fe_dai);
+ len3 = len2 + ARRAY_SIZE(msm_ext_common_be_dai);
+ memcpy(msm_ext_tasha_dai_links, msm_ext_common_fe_dai,
+ sizeof(msm_ext_common_fe_dai));
+ memcpy(msm_ext_tasha_dai_links + len1,
+ msm_ext_tasha_fe_dai, sizeof(msm_ext_tasha_fe_dai));
+ memcpy(msm_ext_tasha_dai_links + len2,
+ msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai));
+ memcpy(msm_ext_tasha_dai_links + len3,
+ msm_ext_tasha_be_dai, sizeof(msm_ext_tasha_be_dai));
+ len4 = len3 + ARRAY_SIZE(msm_ext_tasha_be_dai);
+ if (of_property_read_bool(dev->of_node,
+ "qcom,mi2s-audio-intf")) {
+ memcpy(msm_ext_tasha_dai_links + len4,
+ msm_mi2s_be_dai_links,
+ sizeof(msm_mi2s_be_dai_links));
+ len4 += ARRAY_SIZE(msm_mi2s_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,auxpcm-audio-intf")) {
+ memcpy(msm_ext_tasha_dai_links + len4,
+ msm_auxpcm_be_dai_links,
+ sizeof(msm_auxpcm_be_dai_links));
+ len4 += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) {
+ dev_dbg(dev, "%s(): WCN BTFM support present\n",
+ __func__);
+ memcpy(msm_ext_tasha_dai_links + len4,
+ msm_wcn_be_dai_links,
+ sizeof(msm_wcn_be_dai_links));
+ len4 += ARRAY_SIZE(msm_wcn_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,ext-disp-audio-rx")) {
+ dev_dbg(dev, "%s(): ext disp audio support present\n",
+ __func__);
+ memcpy(msm_ext_tasha_dai_links + len4,
+ ext_disp_be_dai_link,
+ sizeof(ext_disp_be_dai_link));
+ len4 += ARRAY_SIZE(ext_disp_be_dai_link);
+ }
+ msm_ext_dai_links = msm_ext_tasha_dai_links;
+ } else if (strnstr(card->name, "tavil", strlen(card->name))) {
+ len1 = ARRAY_SIZE(msm_ext_common_fe_dai);
+ len2 = len1 + ARRAY_SIZE(msm_ext_tavil_fe_dai);
+ len3 = len2 + ARRAY_SIZE(msm_ext_common_be_dai);
+ memcpy(msm_ext_tavil_dai_links, msm_ext_common_fe_dai,
+ sizeof(msm_ext_common_fe_dai));
+ memcpy(msm_ext_tavil_dai_links + len1,
+ msm_ext_tavil_fe_dai, sizeof(msm_ext_tavil_fe_dai));
+ memcpy(msm_ext_tavil_dai_links + len2,
+ msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai));
+ memcpy(msm_ext_tavil_dai_links + len3,
+ msm_ext_tavil_be_dai, sizeof(msm_ext_tavil_be_dai));
+ len4 = len3 + ARRAY_SIZE(msm_ext_tavil_be_dai);
+ if (of_property_read_bool(dev->of_node,
+ "qcom,mi2s-audio-intf")) {
+ memcpy(msm_ext_tavil_dai_links + len4,
+ msm_mi2s_be_dai_links,
+ sizeof(msm_mi2s_be_dai_links));
+ len4 += ARRAY_SIZE(msm_mi2s_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,auxpcm-audio-intf")) {
+ memcpy(msm_ext_tavil_dai_links + len4,
+ msm_auxpcm_be_dai_links,
+ sizeof(msm_auxpcm_be_dai_links));
+ len4 += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) {
+ dev_dbg(dev, "%s(): WCN BTFM support present\n",
+ __func__);
+ memcpy(msm_ext_tavil_dai_links + len4,
+ msm_wcn_be_dai_links,
+ sizeof(msm_wcn_be_dai_links));
+ len4 += ARRAY_SIZE(msm_wcn_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,ext-disp-audio-rx")) {
+ dev_dbg(dev, "%s(): ext disp audio support present\n",
+ __func__);
+ memcpy(msm_ext_tavil_dai_links + len4,
+ ext_disp_be_dai_link,
+ sizeof(ext_disp_be_dai_link));
+ len4 += ARRAY_SIZE(ext_disp_be_dai_link);
+ }
+ msm_ext_dai_links = msm_ext_tavil_dai_links;
+ } else {
+ dev_err(dev, "%s: failing as no matching card name\n",
+ __func__);
+ return NULL;
+ }
+ card->dai_link = msm_ext_dai_links;
+ card->num_links = len4;
+
+ return card;
+}
+EXPORT_SYMBOL(populate_snd_card_dailinks);
diff --git a/sound/soc/msm/sdm660-external.c b/sound/soc/msm/sdm660-external.c
new file mode 100644
index 000000000000..362ed2918658
--- /dev/null
+++ b/sound/soc/msm/sdm660-external.c
@@ -0,0 +1,1971 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/q6core.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "msm-audio-pinctrl.h"
+#include "sdm660-common.h"
+#include "sdm660-external.h"
+#include "../codecs/wcd9335.h"
+#include "../codecs/wcd934x/wcd934x.h"
+#include "../codecs/wcd934x/wcd934x-mbhc.h"
+
+#define SDM660_SPK_ON 1
+#define SDM660_SPK_OFF 0
+
+#define WCD9XXX_MBHC_DEF_BUTTONS 8
+#define WCD9XXX_MBHC_DEF_RLOADS 5
+#define CODEC_EXT_CLK_RATE 9600000
+#define ADSP_STATE_READY_TIMEOUT_MS 3000
+
+#define TLMM_CENTER_MPM_WAKEUP_INT_EN_0 0x03596000
+#define LPI_GPIO_22_WAKEUP_VAL 0x00000002
+
+#define TLMM_LPI_DIR_CONN_INTR1_CFG_APPS 0x0359D004
+#define LPI_GPIO_22_INTR1_CFG_VAL 0x01
+#define LPI_GPIO_22_INTR1_CFG_MASK 0x03
+
+#define TLMM_LPI_GPIO_INTR_CFG1 0x0359B004
+#define LPI_GPIO_INTR_CFG1_VAL 0x00000113
+
+#define TLMM_LPI_GPIO22_CFG 0x15078040
+#define LPI_GPIO22_CFG_VAL 0x0000009
+
+#define TLMM_LPI_GPIO22_INOUT 0x179D1318
+#define LPI_GPIO22_INOUT_VAL 0x0020000
+
+#define WSA8810_NAME_1 "wsa881x.20170211"
+#define WSA8810_NAME_2 "wsa881x.20170212"
+
+static int msm_ext_spk_control = 1;
+static struct wcd_mbhc_config *wcd_mbhc_cfg_ptr;
+bool codec_reg_done;
+
+struct msm_asoc_wcd93xx_codec {
+ void* (*get_afe_config_fn)(struct snd_soc_codec *codec,
+ enum afe_config_type config_type);
+ void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec);
+};
+
+static struct msm_asoc_wcd93xx_codec msm_codec_fn;
+static struct platform_device *spdev;
+
+static bool is_initial_boot;
+
+static void *def_ext_mbhc_cal(void);
+
+enum {
+ SLIM_RX_0 = 0,
+ SLIM_RX_1,
+ SLIM_RX_2,
+ SLIM_RX_3,
+ SLIM_RX_4,
+ SLIM_RX_5,
+ SLIM_RX_6,
+ SLIM_RX_7,
+ SLIM_RX_MAX,
+};
+
+enum {
+ SLIM_TX_0 = 0,
+ SLIM_TX_1,
+ SLIM_TX_2,
+ SLIM_TX_3,
+ SLIM_TX_4,
+ SLIM_TX_5,
+ SLIM_TX_6,
+ SLIM_TX_7,
+ SLIM_TX_8,
+ SLIM_TX_MAX,
+};
+
+struct dev_config {
+ u32 sample_rate;
+ u32 bit_format;
+ u32 channels;
+};
+
+/* Default configuration of slimbus channels */
+static struct dev_config slim_rx_cfg[] = {
+ [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config slim_tx_cfg[] = {
+ [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static int msm_vi_feed_tx_ch = 2;
+static const char *const slim_rx_ch_text[] = {"One", "Two"};
+static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven",
+ "Eight"};
+static const char *const vi_feed_ch_text[] = {"One", "Two"};
+static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE",
+ "S32_LE"};
+static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16",
+ "KHZ_32", "KHZ_44P1", "KHZ_48",
+ "KHZ_88P2", "KHZ_96", "KHZ_176P4",
+ "KHZ_192", "KHZ_352P8", "KHZ_384"};
+static const char *const spk_function_text[] = {"Off", "On"};
+static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+static char const *bt_sample_rate_rx_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+static char const *bt_sample_rate_tx_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+
+
+
+static SOC_ENUM_SINGLE_EXT_DECL(spk_func_en, spk_function_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_rx, bt_sample_rate_rx_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_tx, bt_sample_rate_tx_text);
+
+
+static int slim_get_sample_rate_val(int sample_rate)
+{
+ int sample_rate_val = 0;
+
+ switch (sample_rate) {
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 0;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_88P2KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_176P4KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 8;
+ break;
+ case SAMPLING_RATE_352P8KHZ:
+ sample_rate_val = 9;
+ break;
+ case SAMPLING_RATE_384KHZ:
+ sample_rate_val = 10;
+ break;
+ default:
+ sample_rate_val = 4;
+ break;
+ }
+ return sample_rate_val;
+}
+
+static int slim_get_sample_rate(int value)
+{
+ int sample_rate = 0;
+
+ switch (value) {
+ case 0:
+ sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ sample_rate = SAMPLING_RATE_88P2KHZ;
+ break;
+ case 6:
+ sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 7:
+ sample_rate = SAMPLING_RATE_176P4KHZ;
+ break;
+ case 8:
+ sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 9:
+ sample_rate = SAMPLING_RATE_352P8KHZ;
+ break;
+ case 10:
+ sample_rate = SAMPLING_RATE_384KHZ;
+ break;
+ default:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return sample_rate;
+}
+
+static int slim_get_bit_format_val(int bit_format)
+{
+ int val = 0;
+
+ switch (bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ val = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+static int slim_get_bit_format(int val)
+{
+ int bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+
+ switch (val) {
+ case 0:
+ bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 1:
+ bit_fmt = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 2:
+ bit_fmt = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 3:
+ bit_fmt = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ default:
+ bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return bit_fmt;
+}
+
+static int slim_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+ int port_id = 0;
+
+ if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX")))
+ port_id = SLIM_RX_0;
+ else if (strnstr(kcontrol->id.name, "SLIM_2_RX", sizeof("SLIM_2_RX")))
+ port_id = SLIM_RX_2;
+ else if (strnstr(kcontrol->id.name, "SLIM_5_RX", sizeof("SLIM_5_RX")))
+ port_id = SLIM_RX_5;
+ else if (strnstr(kcontrol->id.name, "SLIM_6_RX", sizeof("SLIM_6_RX")))
+ port_id = SLIM_RX_6;
+ else if (strnstr(kcontrol->id.name, "SLIM_0_TX", sizeof("SLIM_0_TX")))
+ port_id = SLIM_TX_0;
+ else if (strnstr(kcontrol->id.name, "SLIM_1_TX", sizeof("SLIM_1_TX")))
+ port_id = SLIM_TX_1;
+ else {
+ pr_err("%s: unsupported channel: %s",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ return port_id;
+}
+
+static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /*
+ * Slimbus_7_Rx/Tx sample rate values should always be in sync (same)
+ * when used for BT_SCO use case. Return either Rx or Tx sample rate
+ * value.
+ */
+ switch (slim_rx_cfg[SLIM_RX_7].sample_rate) {
+ case SAMPLING_RATE_48KHZ:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: sample rate = %d", __func__,
+ slim_rx_cfg[SLIM_RX_7].sample_rate);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ;
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ;
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 0:
+ default:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ;
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n",
+ __func__,
+ slim_rx_cfg[SLIM_RX_7].sample_rate,
+ slim_tx_cfg[SLIM_TX_7].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (slim_rx_cfg[SLIM_RX_7].sample_rate) {
+ case SAMPLING_RATE_48KHZ:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: sample rate = %d", __func__,
+ slim_rx_cfg[SLIM_RX_7].sample_rate);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 0:
+ default:
+ slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: sample rates: slim7_rx = %d, value = %d\n",
+ __func__,
+ slim_rx_cfg[SLIM_RX_7].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_tx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (slim_tx_cfg[SLIM_TX_7].sample_rate) {
+ case SAMPLING_RATE_48KHZ:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: sample rate = %d", __func__,
+ slim_tx_cfg[SLIM_TX_7].sample_rate);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_tx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 0:
+ default:
+ slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: sample rates: slim7_tx = %d, value = %d\n",
+ __func__,
+ slim_tx_cfg[SLIM_TX_7].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+
+static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ ucontrol->value.enumerated.item[0] =
+ slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate);
+
+ pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ ch_num, slim_rx_cfg[ch_num].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ slim_rx_cfg[ch_num].sample_rate =
+ slim_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ ch_num, slim_rx_cfg[ch_num].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ ucontrol->value.enumerated.item[0] =
+ slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate);
+
+ pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ ch_num, slim_tx_cfg[ch_num].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate = 0;
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]);
+ if (sample_rate == SAMPLING_RATE_44P1KHZ) {
+ pr_err("%s: Unsupported sample rate %d: for Tx path\n",
+ __func__, sample_rate);
+ return -EINVAL;
+ }
+ slim_tx_cfg[ch_num].sample_rate = sample_rate;
+
+ pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__,
+ ch_num, slim_tx_cfg[ch_num].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ ucontrol->value.enumerated.item[0] =
+ slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format);
+
+ pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+ __func__, ch_num, slim_rx_cfg[ch_num].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ slim_rx_cfg[ch_num].bit_format =
+ slim_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+ __func__, ch_num, slim_rx_cfg[ch_num].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ ucontrol->value.enumerated.item[0] =
+ slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format);
+
+ pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n",
+ __func__, ch_num, slim_tx_cfg[ch_num].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ slim_tx_cfg[ch_num].bit_format =
+ slim_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n",
+ __func__, ch_num, slim_tx_cfg[ch_num].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_slim_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__,
+ ch_num, slim_rx_cfg[ch_num].channels);
+ ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1;
+
+ return 0;
+}
+
+static int msm_slim_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1;
+ pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__,
+ ch_num, slim_rx_cfg[ch_num].channels);
+
+ return 1;
+}
+
+static int msm_slim_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__,
+ ch_num, slim_tx_cfg[ch_num].channels);
+ ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1;
+
+ return 0;
+}
+
+static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = slim_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1;
+ pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__,
+ ch_num, slim_tx_cfg[ch_num].channels);
+
+ return 1;
+}
+
+static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1;
+ pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch);
+ return 1;
+}
+
+static void *def_ext_mbhc_cal(void)
+{
+ void *tavil_wcd_cal;
+ struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+ u16 *btn_high;
+
+ tavil_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS,
+ WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL);
+ if (!tavil_wcd_cal)
+ return NULL;
+
+#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tavil_wcd_cal)->X) = (Y))
+ S(v_hs_max, 1600);
+#undef S
+#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal)->X) = (Y))
+ S(num_btn, WCD_MBHC_DEF_BUTTONS);
+#undef S
+
+ btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal);
+ btn_high = ((void *)&btn_cfg->_v_btn_low) +
+ (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn);
+
+ btn_high[0] = 75;
+ btn_high[1] = 150;
+ btn_high[2] = 237;
+ btn_high[3] = 500;
+ btn_high[4] = 500;
+ btn_high[5] = 500;
+ btn_high[6] = 500;
+ btn_high[7] = 500;
+
+ return tavil_wcd_cal;
+}
+
+static inline int param_is_mask(int p)
+{
+ return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
+{
+ return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+
+static void msm_ext_control(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_codec_get_dapm(codec);
+
+ pr_debug("%s: msm_ext_spk_control = %d", __func__, msm_ext_spk_control);
+ if (msm_ext_spk_control == SDM660_SPK_ON) {
+ snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_disable_pin(dapm, "Lineout_3 amp");
+ }
+ snd_soc_dapm_sync(dapm);
+}
+
+static int msm_ext_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_ext_spk_control = %d\n",
+ __func__, msm_ext_spk_control);
+ ucontrol->value.integer.value[0] = msm_ext_spk_control;
+ return 0;
+}
+
+static int msm_ext_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ pr_debug("%s()\n", __func__);
+ if (msm_ext_spk_control == ucontrol->value.integer.value[0])
+ return 0;
+
+ msm_ext_spk_control = ucontrol->value.integer.value[0];
+ msm_ext_control(codec);
+ return 1;
+}
+
+
+int msm_ext_enable_codec_mclk(struct snd_soc_codec *codec, int enable,
+ bool dapm)
+{
+ int ret;
+
+ pr_debug("%s: enable = %d\n", __func__, enable);
+
+ if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+ ret = tasha_cdc_mclk_enable(codec, enable, dapm);
+ else if (!strcmp(dev_name(codec->dev), "tavil_codec"))
+ ret = tavil_cdc_mclk_enable(codec, enable);
+ else {
+ dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+ __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+ SOC_ENUM_EXT("Speaker Function", spk_func_en, msm_ext_get_spk,
+ msm_ext_set_spk),
+ SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs,
+ msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs,
+ msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs,
+ msm_slim_tx_ch_get, msm_slim_tx_ch_put),
+ SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs,
+ msm_slim_tx_ch_get, msm_slim_tx_ch_put),
+ SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs,
+ msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs,
+ msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+ SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs,
+ msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format,
+ slim_rx_bit_format_get, slim_rx_bit_format_put),
+ SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format,
+ slim_rx_bit_format_get, slim_rx_bit_format_put),
+ SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format,
+ slim_rx_bit_format_get, slim_rx_bit_format_put),
+ SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format,
+ slim_tx_bit_format_get, slim_tx_bit_format_put),
+ SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate,
+ slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate,
+ slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate,
+ slim_tx_sample_rate_get, slim_tx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate,
+ slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+ SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate,
+ slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+ SOC_ENUM_EXT("BT SampleRate", bt_sample_rate,
+ msm_bt_sample_rate_get,
+ msm_bt_sample_rate_put),
+ SOC_ENUM_EXT("BT SampleRate RX", bt_sample_rate_rx,
+ msm_bt_sample_rate_rx_get,
+ msm_bt_sample_rate_rx_put),
+ SOC_ENUM_EXT("BT SampleRate TX", bt_sample_rate_tx,
+ msm_bt_sample_rate_tx_get,
+ msm_bt_sample_rate_tx_put),
+};
+
+static int msm_slim_get_ch_from_beid(int32_t be_id)
+{
+ int ch_id = 0;
+
+ switch (be_id) {
+ case MSM_BACKEND_DAI_SLIMBUS_0_RX:
+ ch_id = SLIM_RX_0;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_1_RX:
+ ch_id = SLIM_RX_1;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_2_RX:
+ ch_id = SLIM_RX_2;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_3_RX:
+ ch_id = SLIM_RX_3;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_4_RX:
+ ch_id = SLIM_RX_4;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+ ch_id = SLIM_RX_6;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_0_TX:
+ ch_id = SLIM_TX_0;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+ ch_id = SLIM_TX_3;
+ break;
+ default:
+ ch_id = SLIM_RX_0;
+ break;
+ }
+
+ return ch_id;
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
+{
+ if (bit >= SNDRV_MASK_MAX)
+ return;
+ if (param_is_mask(n)) {
+ struct snd_mask *m = param_to_mask(p, n);
+
+ m->bits[0] = 0;
+ m->bits[1] = 0;
+ m->bits[bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
+/**
+ * msm_ext_be_hw_params_fixup - updates settings of ALSA BE hw params.
+ *
+ * @rtd: runtime dailink instance
+ * @params: HW params of associated backend dailink.
+ *
+ * Returns 0 on success or rc on failure.
+ */
+int msm_ext_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ int rc = 0;
+ int idx;
+ void *config = NULL;
+ struct snd_soc_codec *codec = rtd->codec;
+
+ pr_debug("%s: format = %d, rate = %d\n",
+ __func__, params_format(params), params_rate(params));
+
+ switch (dai_link->be_id) {
+ case MSM_BACKEND_DAI_SLIMBUS_0_RX:
+ case MSM_BACKEND_DAI_SLIMBUS_1_RX:
+ case MSM_BACKEND_DAI_SLIMBUS_2_RX:
+ case MSM_BACKEND_DAI_SLIMBUS_3_RX:
+ case MSM_BACKEND_DAI_SLIMBUS_4_RX:
+ case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+ idx = msm_slim_get_ch_from_beid(dai_link->be_id);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim_rx_cfg[idx].bit_format);
+ rate->min = rate->max = slim_rx_cfg[idx].sample_rate;
+ channels->min = channels->max = slim_rx_cfg[idx].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_0_TX:
+ case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+ idx = msm_slim_get_ch_from_beid(dai_link->be_id);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim_tx_cfg[idx].bit_format);
+ rate->min = rate->max = slim_tx_cfg[idx].sample_rate;
+ channels->min = channels->max = slim_tx_cfg[idx].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_1_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim_tx_cfg[1].bit_format);
+ rate->min = rate->max = slim_tx_cfg[1].sample_rate;
+ channels->min = channels->max = slim_tx_cfg[1].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_4_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_FORMAT_S32_LE);
+ rate->min = rate->max = SAMPLING_RATE_8KHZ;
+ channels->min = channels->max = msm_vi_feed_tx_ch;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_5_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim_rx_cfg[5].bit_format);
+ rate->min = rate->max = slim_rx_cfg[5].sample_rate;
+ channels->min = channels->max = slim_rx_cfg[5].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_5_TX:
+ rate->min = rate->max = SAMPLING_RATE_16KHZ;
+ channels->min = channels->max = 1;
+
+ config = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_SLIMBUS_SLAVE_PORT_CONFIG);
+ if (config) {
+ rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG,
+ config, SLIMBUS_5_TX);
+ if (rc)
+ pr_err("%s: Failed to set slimbus slave port config %d\n",
+ __func__, rc);
+ }
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_7_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim_rx_cfg[SLIM_RX_7].bit_format);
+ rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate;
+ channels->min = channels->max =
+ slim_rx_cfg[SLIM_RX_7].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_7_TX:
+ rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate;
+ channels->min = channels->max =
+ slim_tx_cfg[SLIM_TX_7].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_8_TX:
+ rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate;
+ channels->min = channels->max =
+ slim_tx_cfg[SLIM_TX_8].channels;
+ break;
+
+ default:
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_ext_be_hw_params_fixup);
+
+/**
+ * msm_snd_hw_params - hw params ops of backend dailink.
+ *
+ * @substream: PCM stream of associated backend dailink.
+ * @params: HW params of associated backend dailink.
+ *
+ * Returns 0 on success or ret on failure.
+ */
+int msm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ int ret = 0;
+ u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+ u32 user_set_tx_ch = 0;
+ u32 rx_ch_count;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map, err:%d\n",
+ __func__, ret);
+ goto err_ch_map;
+ }
+ if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_5_RX) {
+ pr_debug("%s: rx_5_ch=%d\n", __func__,
+ slim_rx_cfg[5].channels);
+ rx_ch_count = slim_rx_cfg[5].channels;
+ } else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_2_RX) {
+ pr_debug("%s: rx_2_ch=%d\n", __func__,
+ slim_rx_cfg[2].channels);
+ rx_ch_count = slim_rx_cfg[2].channels;
+ } else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_6_RX) {
+ pr_debug("%s: rx_6_ch=%d\n", __func__,
+ slim_rx_cfg[6].channels);
+ rx_ch_count = slim_rx_cfg[6].channels;
+ } else {
+ pr_debug("%s: rx_0_ch=%d\n", __func__,
+ slim_rx_cfg[0].channels);
+ rx_ch_count = slim_rx_cfg[0].channels;
+ }
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+ rx_ch_count, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto err_ch_map;
+ }
+ } else {
+ pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
+ codec_dai->name, codec_dai->id, user_set_tx_ch);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map\n, err:%d\n",
+ __func__, ret);
+ goto err_ch_map;
+ }
+ /* For <codec>_tx1 case */
+ if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_0_TX)
+ user_set_tx_ch = slim_tx_cfg[0].channels;
+ /* For <codec>_tx3 case */
+ else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_1_TX)
+ user_set_tx_ch = slim_tx_cfg[1].channels;
+ else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_4_TX)
+ user_set_tx_ch = msm_vi_feed_tx_ch;
+ else
+ user_set_tx_ch = tx_ch_cnt;
+
+ pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), be_id (%d)\n",
+ __func__, slim_tx_cfg[0].channels, user_set_tx_ch,
+ tx_ch_cnt, dai_link->be_id);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ user_set_tx_ch, tx_ch, 0, 0);
+ if (ret < 0)
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ }
+
+err_ch_map:
+ return ret;
+}
+EXPORT_SYMBOL(msm_snd_hw_params);
+
+/**
+ * msm_ext_slimbus_2_hw_params - hw params ops of slimbus_2 BE.
+ *
+ * @substream: PCM stream of associated backend dailink.
+ * @params: HW params of associated backend dailink.
+ *
+ * Returns 0 on success or ret on failure.
+ */
+int msm_ext_slimbus_2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
+ unsigned int num_tx_ch = 0;
+ unsigned int num_rx_ch = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ num_rx_ch = params_channels(params);
+ pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__,
+ codec_dai->name, codec_dai->id, num_rx_ch);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+ num_rx_ch, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+ num_tx_ch = params_channels(params);
+ pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__,
+ codec_dai->name, codec_dai->id, num_tx_ch);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ num_tx_ch, tx_ch, 0, 0);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+end:
+ return ret;
+}
+EXPORT_SYMBOL(msm_ext_slimbus_2_hw_params);
+
+/**
+ * msm_snd_cpe_hw_params - hw params ops of CPE backend.
+ *
+ * @substream: PCM stream of associated backend dailink.
+ * @params: HW params of associated backend dailink.
+ *
+ * Returns 0 on success or ret on failure.
+ */
+int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ int ret = 0;
+ u32 tx_ch[SLIM_MAX_TX_PORTS];
+ u32 tx_ch_cnt = 0;
+
+ if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) {
+ pr_err("%s: Invalid stream type %d\n",
+ __func__, substream->stream);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ pr_debug("%s: %s_tx_dai_id_%d\n", __func__,
+ codec_dai->name, codec_dai->id);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, NULL, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map\n, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ pr_debug("%s: tx_ch_cnt(%d) be_id %d\n",
+ __func__, tx_ch_cnt, dai_link->be_id);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ tx_ch_cnt, tx_ch, 0, 0);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+end:
+ return ret;
+}
+EXPORT_SYMBOL(msm_snd_cpe_hw_params);
+
+static int msm_afe_set_config(struct snd_soc_codec *codec)
+{
+ int rc;
+ void *config_data;
+
+ pr_debug("%s: enter\n", __func__);
+
+ if (!msm_codec_fn.get_afe_config_fn) {
+ dev_err(codec->dev, "%s: codec get afe config not init'ed\n",
+ __func__);
+ return -EINVAL;
+ }
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_CDC_REGISTERS_CONFIG);
+ if (config_data) {
+ rc = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0);
+ if (rc) {
+ pr_err("%s: Failed to set codec registers config %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_CDC_REGISTER_PAGE_CONFIG);
+ if (config_data) {
+ rc = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data,
+ 0);
+ if (rc)
+ pr_err("%s: Failed to set cdc register page config\n",
+ __func__);
+ }
+
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_SLIMBUS_SLAVE_CONFIG);
+ if (config_data) {
+ rc = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0);
+ if (rc) {
+ pr_err("%s: Failed to set slimbus slave config %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_AANC_VERSION);
+ if (config_data) {
+ rc = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+ if (rc) {
+ pr_err("%s: Failed to set AANC version %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_CDC_CLIP_REGISTERS_CONFIG);
+ if (config_data) {
+ rc = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG,
+ config_data, 0);
+ if (rc) {
+ pr_err("%s: Failed to set clip registers %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_CLIP_BANK_SEL);
+ if (config_data) {
+ rc = afe_set_config(AFE_CLIP_BANK_SEL,
+ config_data, 0);
+ if (rc) {
+ pr_err("%s: Failed to set AFE bank selection %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_CDC_REGISTER_PAGE_CONFIG);
+ if (config_data) {
+ rc = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data,
+ 0);
+ if (rc)
+ pr_err("%s: Failed to set cdc register page config\n",
+ __func__);
+ }
+
+ return 0;
+}
+
+static void msm_afe_clear_config(void)
+{
+ afe_clear_config(AFE_CDC_REGISTERS_CONFIG);
+ afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG);
+}
+
+static void msm_snd_interrupt_config(struct msm_asoc_mach_data *pdata)
+{
+ int val;
+
+ val = ioread32(pdata->msm_snd_intr_lpi.mpm_wakeup);
+ val |= LPI_GPIO_22_WAKEUP_VAL;
+ iowrite32(val, pdata->msm_snd_intr_lpi.mpm_wakeup);
+
+ val = ioread32(pdata->msm_snd_intr_lpi.intr1_cfg_apps);
+ val &= ~(LPI_GPIO_22_INTR1_CFG_MASK);
+ val |= LPI_GPIO_22_INTR1_CFG_VAL;
+ iowrite32(val, pdata->msm_snd_intr_lpi.intr1_cfg_apps);
+
+ iowrite32(LPI_GPIO_INTR_CFG1_VAL,
+ pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg);
+ iowrite32(LPI_GPIO22_CFG_VAL,
+ pdata->msm_snd_intr_lpi.lpi_gpio_cfg);
+ val = ioread32(pdata->msm_snd_intr_lpi.lpi_gpio_inout);
+ val |= LPI_GPIO22_INOUT_VAL;
+ iowrite32(val, pdata->msm_snd_intr_lpi.lpi_gpio_inout);
+}
+
+static int msm_adsp_power_up_config(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ unsigned long timeout;
+ int adsp_ready = 0;
+ struct snd_soc_card *card = codec->component.card;
+ struct msm_asoc_mach_data *pdata;
+
+ pdata = snd_soc_card_get_drvdata(card);
+ timeout = jiffies +
+ msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+
+ do {
+ if (q6core_is_adsp_ready()) {
+ pr_debug("%s: ADSP Audio is ready\n", __func__);
+ adsp_ready = 1;
+ break;
+ }
+ /*
+ * ADSP will be coming up after subsystem restart and
+ * it might not be fully up when the control reaches
+ * here. So, wait for 50msec before checking ADSP state
+ */
+ msleep(50);
+ } while (time_after(timeout, jiffies));
+
+ if (!adsp_ready) {
+ pr_err("%s: timed out waiting for ADSP Audio\n", __func__);
+ ret = -ETIMEDOUT;
+ goto err_fail;
+ }
+ msm_snd_interrupt_config(pdata);
+
+ ret = msm_afe_set_config(codec);
+ if (ret)
+ pr_err("%s: Failed to set AFE config. err %d\n",
+ __func__, ret);
+
+ return 0;
+
+err_fail:
+ return ret;
+}
+
+static int sdm660_notifier_service_cb(struct notifier_block *this,
+ unsigned long opcode, void *ptr)
+{
+ int ret;
+ struct snd_soc_card *card = NULL;
+ const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_codec *codec;
+
+ pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode);
+
+ switch (opcode) {
+ case AUDIO_NOTIFIER_SERVICE_DOWN:
+ /*
+ * Use flag to ignore initial boot notifications
+ * On initial boot msm_adsp_power_up_config is
+ * called on init. There is no need to clear
+ * and set the config again on initial boot.
+ */
+ if (is_initial_boot)
+ break;
+ msm_afe_clear_config();
+ break;
+ case AUDIO_NOTIFIER_SERVICE_UP:
+ if (is_initial_boot) {
+ is_initial_boot = false;
+ break;
+ }
+ if (!spdev)
+ return -EINVAL;
+
+ card = platform_get_drvdata(spdev);
+ rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+ if (!rtd) {
+ dev_err(card->dev,
+ "%s: snd_soc_get_pcm_runtime for %s failed!\n",
+ __func__, be_dl_name);
+ ret = -EINVAL;
+ goto done;
+ }
+ codec = rtd->codec;
+
+ ret = msm_adsp_power_up_config(codec);
+ if (ret < 0) {
+ dev_err(card->dev,
+ "%s: msm_adsp_power_up_config failed ret = %d!\n",
+ __func__, ret);
+ goto done;
+ }
+ break;
+ default:
+ break;
+ }
+done:
+ return NOTIFY_OK;
+}
+
+static struct notifier_block service_nb = {
+ .notifier_call = sdm660_notifier_service_cb,
+ .priority = -INT_MAX,
+};
+
+static int msm_config_hph_en0_gpio(struct snd_soc_codec *codec, bool high)
+{
+ struct snd_soc_card *card = codec->component.card;
+ struct msm_asoc_mach_data *pdata;
+ int val;
+
+ if (!card)
+ return 0;
+
+ pdata = snd_soc_card_get_drvdata(card);
+ if (!pdata || !gpio_is_valid(pdata->hph_en0_gpio))
+ return 0;
+
+ val = gpio_get_value_cansleep(pdata->hph_en0_gpio);
+ if ((!!val) == high)
+ return 0;
+
+ gpio_direction_output(pdata->hph_en0_gpio, (int)high);
+
+ return 1;
+}
+
+static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec,
+ int enable, bool dapm)
+{
+ int ret = 0;
+
+ if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+ ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm);
+ else {
+ dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+ __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int msm_ext_mclk_tx_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return msm_snd_enable_codec_ext_tx_clk(codec, 1, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return msm_snd_enable_codec_ext_tx_clk(codec, 0, true);
+ }
+ return 0;
+}
+
+static int msm_ext_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return msm_ext_enable_codec_mclk(codec, 1, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return msm_ext_enable_codec_mclk(codec, 0, true);
+ }
+ return 0;
+}
+
+static int msm_ext_prepare_hifi(struct msm_asoc_mach_data *pdata)
+{
+ int ret = 0;
+
+ if (gpio_is_valid(pdata->hph_en1_gpio)) {
+ pr_debug("%s: hph_en1_gpio request %d\n", __func__,
+ pdata->hph_en1_gpio);
+ ret = gpio_request(pdata->hph_en1_gpio, "hph_en1_gpio");
+ if (ret) {
+ pr_err("%s: hph_en1_gpio request failed, ret:%d\n",
+ __func__, ret);
+ goto err;
+ }
+ }
+ if (gpio_is_valid(pdata->hph_en0_gpio)) {
+ pr_debug("%s: hph_en0_gpio request %d\n", __func__,
+ pdata->hph_en0_gpio);
+ ret = gpio_request(pdata->hph_en0_gpio, "hph_en0_gpio");
+ if (ret)
+ pr_err("%s: hph_en0_gpio request failed, ret:%d\n",
+ __func__, ret);
+ }
+
+err:
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget msm_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY_S("MCLK", -1, SND_SOC_NOPM, 0, 0,
+ msm_ext_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("MCLK TX", -1, SND_SOC_NOPM, 0, 0,
+ msm_ext_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SPK("Lineout_1 amp", NULL),
+ SND_SOC_DAPM_SPK("Lineout_3 amp", NULL),
+ SND_SOC_DAPM_SPK("Lineout_2 amp", NULL),
+ SND_SOC_DAPM_SPK("Lineout_4 amp", NULL),
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Secondary Mic", NULL),
+ SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic4", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic6", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic7", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic8", NULL),
+
+ SND_SOC_DAPM_MIC("Digital Mic0", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic6", NULL),
+};
+
+static struct snd_soc_dapm_route wcd_audio_paths_tasha[] = {
+ {"MIC BIAS1", NULL, "MCLK TX"},
+ {"MIC BIAS2", NULL, "MCLK TX"},
+ {"MIC BIAS3", NULL, "MCLK TX"},
+ {"MIC BIAS4", NULL, "MCLK TX"},
+};
+
+static struct snd_soc_dapm_route wcd_audio_paths[] = {
+ {"MIC BIAS1", NULL, "MCLK"},
+ {"MIC BIAS2", NULL, "MCLK"},
+ {"MIC BIAS3", NULL, "MCLK"},
+ {"MIC BIAS4", NULL, "MCLK"},
+};
+
+/**
+ * msm_audrx_init - Audio init function of sound card instantiate.
+ *
+ * @rtd: runtime dailink instance
+ *
+ * Returns 0 on success or ret on failure.
+ */
+int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ void *config_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_codec_get_dapm(codec);
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux;
+ struct snd_card *card;
+ struct snd_info_entry *entry;
+ struct msm_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(rtd->card);
+
+ /* Codec SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156};
+ unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143};
+
+ /* Tavil Codec SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch_tavil[WCD934X_RX_MAX] = {144, 145, 146, 147, 148,
+ 149, 150, 151};
+ unsigned int tx_ch_tavil[WCD934X_TX_MAX] = {128, 129, 130, 131, 132,
+ 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143};
+
+ pr_debug("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+ rtd->pmdown_time = 0;
+
+ ret = snd_soc_add_codec_controls(codec, msm_snd_controls,
+ ARRAY_SIZE(msm_snd_controls));
+ if (ret < 0) {
+ pr_err("%s: add_codec_controls failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_codec_controls(codec, msm_common_snd_controls,
+ msm_common_snd_controls_size());
+ if (ret < 0) {
+ pr_err("%s: add_common_snd_controls failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ snd_soc_dapm_new_controls(dapm, msm_dapm_widgets,
+ ARRAY_SIZE(msm_dapm_widgets));
+
+ if (!strcmp(dev_name(codec_dai->dev), "tasha_codec"))
+ snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tasha,
+ ARRAY_SIZE(wcd_audio_paths_tasha));
+ else
+ snd_soc_dapm_add_routes(dapm, wcd_audio_paths,
+ ARRAY_SIZE(wcd_audio_paths));
+
+ snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp");
+
+ snd_soc_dapm_ignore_suspend(dapm, "MADINPUT");
+ snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT");
+ snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_1 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_3 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_2 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_4 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic7");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic8");
+
+ snd_soc_dapm_ignore_suspend(dapm, "EAR");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC1");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC2");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC3");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC4");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC5");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC0");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC1");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC2");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC3");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC4");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC5");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC EAR");
+ snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "HPHL");
+ snd_soc_dapm_ignore_suspend(dapm, "HPHR");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI");
+ snd_soc_dapm_ignore_suspend(dapm, "VIINPUT");
+
+ if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) {
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2");
+ } else {
+ snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2");
+ }
+
+ snd_soc_dapm_sync(dapm);
+
+ if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch_tavil),
+ tx_ch_tavil, ARRAY_SIZE(rx_ch_tavil),
+ rx_ch_tavil);
+ } else {
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch),
+ rx_ch);
+ }
+
+ if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+ msm_codec_fn.get_afe_config_fn = tavil_get_afe_config;
+ } else {
+ msm_codec_fn.get_afe_config_fn = tasha_get_afe_config;
+ msm_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit;
+ }
+
+ ret = msm_adsp_power_up_config(codec);
+ if (ret) {
+ pr_err("%s: Failed to set AFE config %d\n", __func__, ret);
+ goto err_afe_cfg;
+ }
+
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_AANC_VERSION);
+ if (config_data) {
+ ret = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+ if (ret) {
+ pr_err("%s: Failed to set aanc version %d\n",
+ __func__, ret);
+ goto err_afe_cfg;
+ }
+ }
+
+ if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) {
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_CDC_CLIP_REGISTERS_CONFIG);
+ if (config_data) {
+ ret = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG,
+ config_data, 0);
+ if (ret) {
+ pr_err("%s: Failed to set clip registers %d\n",
+ __func__, ret);
+ goto err_afe_cfg;
+ }
+ }
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_CLIP_BANK_SEL);
+ if (config_data) {
+ ret = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0);
+ if (ret) {
+ pr_err("%s: Failed to set AFE bank selection %d\n",
+ __func__, ret);
+ goto err_afe_cfg;
+ }
+ }
+ }
+
+ /*
+ * Send speaker configuration only for WSA8810.
+ * Defalut configuration is for WSA8815.
+ */
+ if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+ if (rtd_aux && rtd_aux->component)
+ if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
+ !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
+ tavil_set_spkr_mode(rtd->codec, SPKR_MODE_1);
+ tavil_set_spkr_gain_offset(rtd->codec,
+ RX_GAIN_OFFSET_M1P5_DB);
+ }
+ card = rtd->card->snd_card;
+ entry = snd_register_module_info(card->module, "codecs",
+ card->proc_root);
+ if (!entry) {
+ pr_debug("%s: Cannot create codecs module entry\n",
+ __func__);
+ pdata->codec_root = NULL;
+ goto done;
+ }
+ pdata->codec_root = entry;
+ tavil_codec_info_create_codec_entry(pdata->codec_root, codec);
+ } else {
+ if (rtd_aux && rtd_aux->component)
+ if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
+ !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
+ tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1);
+ tasha_set_spkr_gain_offset(rtd->codec,
+ RX_GAIN_OFFSET_M1P5_DB);
+ }
+ card = rtd->card->snd_card;
+ entry = snd_register_module_info(card->module, "codecs",
+ card->proc_root);
+ if (!entry) {
+ pr_debug("%s: Cannot create codecs module entry\n",
+ __func__);
+ ret = 0;
+ goto err_snd_module;
+ }
+ pdata->codec_root = entry;
+ tasha_codec_info_create_codec_entry(pdata->codec_root, codec);
+ tasha_mbhc_zdet_gpio_ctrl(msm_config_hph_en0_gpio, rtd->codec);
+ }
+
+ wcd_mbhc_cfg_ptr->calibration = def_ext_mbhc_cal();
+ if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+ if (wcd_mbhc_cfg_ptr->calibration) {
+ pdata->codec = codec;
+ ret = tavil_mbhc_hs_detect(codec, wcd_mbhc_cfg_ptr);
+ if (ret < 0)
+ pr_err("%s: Failed to intialise mbhc %d\n",
+ __func__, ret);
+ } else {
+ pr_err("%s: wcd_mbhc_cfg calibration is NULL\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_mbhc_cal;
+ }
+ } else {
+ if (wcd_mbhc_cfg_ptr->calibration) {
+ pdata->codec = codec;
+ ret = tasha_mbhc_hs_detect(codec, wcd_mbhc_cfg_ptr);
+ if (ret < 0)
+ pr_err("%s: Failed to intialise mbhc %d\n",
+ __func__, ret);
+ } else {
+ pr_err("%s: wcd_mbhc_cfg calibration is NULL\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_mbhc_cal;
+ }
+
+ }
+ codec_reg_done = true;
+done:
+ return 0;
+
+err_snd_module:
+err_afe_cfg:
+err_mbhc_cal:
+ return ret;
+}
+EXPORT_SYMBOL(msm_audrx_init);
+
+/**
+ * msm_ext_register_audio_notifier - register SSR notifier.
+ */
+void msm_ext_register_audio_notifier(struct platform_device *pdev)
+{
+ int ret;
+
+ is_initial_boot = true;
+ spdev = pdev;
+ ret = audio_notifier_register("sdm660", AUDIO_NOTIFIER_ADSP_DOMAIN,
+ &service_nb);
+ if (ret < 0)
+ pr_err("%s: Audio notifier register failed ret = %d\n",
+ __func__, ret);
+}
+EXPORT_SYMBOL(msm_ext_register_audio_notifier);
+
+/**
+ * msm_ext_cdc_init - external codec machine specific init.
+ *
+ * @pdev: platform device handle
+ * @pdata: private data of machine driver
+ * @card: sound card pointer reference
+ * @mbhc_cfg: MBHC config reference
+ *
+ * Returns 0 on success or ret on failure.
+ */
+int msm_ext_cdc_init(struct platform_device *pdev,
+ struct msm_asoc_mach_data *pdata,
+ struct snd_soc_card **card,
+ struct wcd_mbhc_config *wcd_mbhc_cfg_ptr1)
+{
+ int ret = 0;
+
+ wcd_mbhc_cfg_ptr = wcd_mbhc_cfg_ptr1;
+ pdev->id = 0;
+ wcd_mbhc_cfg_ptr->moisture_en = true;
+ wcd_mbhc_cfg_ptr->mbhc_micbias = MIC_BIAS_2;
+ wcd_mbhc_cfg_ptr->anc_micbias = MIC_BIAS_2;
+ wcd_mbhc_cfg_ptr->enable_anc_mic_detect = false;
+
+ *card = populate_snd_card_dailinks(&pdev->dev, pdata->snd_card_val);
+ if (!(*card)) {
+ dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__);
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
+ platform_set_drvdata(pdev, *card);
+ snd_soc_card_set_drvdata(*card, pdata);
+ pdata->hph_en1_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,hph-en1-gpio", 0);
+ if (!gpio_is_valid(pdata->hph_en1_gpio))
+ pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,hph-en1-gpio", 0);
+ if (!gpio_is_valid(pdata->hph_en1_gpio) && (!pdata->hph_en1_gpio_p)) {
+ dev_dbg(&pdev->dev, "property %s not detected in node %s",
+ "qcom,hph-en1-gpio", pdev->dev.of_node->full_name);
+ }
+
+ pdata->hph_en0_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,hph-en0-gpio", 0);
+ if (!gpio_is_valid(pdata->hph_en0_gpio))
+ pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,hph-en0-gpio", 0);
+ if (!gpio_is_valid(pdata->hph_en0_gpio) && (!pdata->hph_en0_gpio_p)) {
+ dev_dbg(&pdev->dev, "property %s not detected in node %s",
+ "qcom,hph-en0-gpio", pdev->dev.of_node->full_name);
+ }
+
+ ret = msm_ext_prepare_hifi(pdata);
+ if (ret) {
+ dev_dbg(&pdev->dev, "msm_ext_prepare_hifi failed (%d)\n",
+ ret);
+ ret = 0;
+ }
+ pdata->msm_snd_intr_lpi.mpm_wakeup =
+ ioremap(TLMM_CENTER_MPM_WAKEUP_INT_EN_0, 4);
+ pdata->msm_snd_intr_lpi.intr1_cfg_apps =
+ ioremap(TLMM_LPI_DIR_CONN_INTR1_CFG_APPS, 4);
+ pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg =
+ ioremap(TLMM_LPI_GPIO_INTR_CFG1, 4);
+ pdata->msm_snd_intr_lpi.lpi_gpio_cfg =
+ ioremap(TLMM_LPI_GPIO22_CFG, 4);
+ pdata->msm_snd_intr_lpi.lpi_gpio_inout =
+ ioremap(TLMM_LPI_GPIO22_INOUT, 4);
+err:
+ return ret;
+}
+EXPORT_SYMBOL(msm_ext_cdc_init);
+
+/**
+ * msm_ext_cdc_deinit - external codec machine specific deinit.
+ */
+void msm_ext_cdc_deinit(struct msm_asoc_mach_data *pdata)
+{
+ if (pdata->msm_snd_intr_lpi.mpm_wakeup)
+ iounmap(pdata->msm_snd_intr_lpi.mpm_wakeup);
+ if (pdata->msm_snd_intr_lpi.intr1_cfg_apps)
+ iounmap(pdata->msm_snd_intr_lpi.intr1_cfg_apps);
+ if (pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg)
+ iounmap(pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg);
+ if (pdata->msm_snd_intr_lpi.lpi_gpio_cfg)
+ iounmap(pdata->msm_snd_intr_lpi.lpi_gpio_cfg);
+ if (pdata->msm_snd_intr_lpi.lpi_gpio_inout)
+ iounmap(pdata->msm_snd_intr_lpi.lpi_gpio_inout);
+
+}
+EXPORT_SYMBOL(msm_ext_cdc_deinit);
diff --git a/sound/soc/msm/sdm660-external.h b/sound/soc/msm/sdm660-external.h
new file mode 100644
index 000000000000..acf5735c2502
--- /dev/null
+++ b/sound/soc/msm/sdm660-external.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SDM660_EXTERNAL
+#define __SDM660_EXTERNAL
+
+int msm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
+int msm_ext_slimbus_2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
+int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params);
+int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params);
+int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params);
+int msm_audrx_init(struct snd_soc_pcm_runtime *rtd);
+int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
+struct snd_soc_card *populate_snd_card_dailinks(struct device *dev,
+ int snd_card_val);
+int msm_ext_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params);
+#ifdef CONFIG_SND_SOC_EXT_CODEC
+int msm_ext_cdc_init(struct platform_device *, struct msm_asoc_mach_data *,
+ struct snd_soc_card **, struct wcd_mbhc_config *);
+void msm_ext_register_audio_notifier(struct platform_device *pdev);
+void msm_ext_cdc_deinit(struct msm_asoc_mach_data *pdata);
+#else
+inline int msm_ext_cdc_init(struct platform_device *pdev,
+ struct msm_asoc_mach_data *pdata,
+ struct snd_soc_card **card,
+ struct wcd_mbhc_config *wcd_mbhc_cfg_ptr1)
+{
+ return 0;
+}
+
+inline void msm_ext_register_audio_notifier(struct platform_device *pdev)
+{
+}
+inline void msm_ext_cdc_deinit(void)
+{
+}
+#endif
+#endif
diff --git a/sound/soc/msm/sdm660-internal.c b/sound/soc/msm/sdm660-internal.c
new file mode 100644
index 000000000000..929020f32213
--- /dev/null
+++ b/sound/soc/msm/sdm660-internal.c
@@ -0,0 +1,3305 @@
+/* Copyright (c) 2015-2018, 2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <sound/pcm_params.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "sdm660-common.h"
+#include "../codecs/sdm660_cdc/msm-digital-cdc.h"
+#include "../codecs/sdm660_cdc/msm-analog-cdc.h"
+#include "../codecs/msm_sdw/msm_sdw.h"
+#include <linux/pm_qos.h>
+
+#define __CHIPSET__ "SDM660 "
+#define MSM_DAILINK_NAME(name) (__CHIPSET__#name)
+
+#define WCD_MBHC_DEF_RLOADS 5
+
+#define WCN_CDC_SLIM_RX_CH_MAX 2
+#define WCN_CDC_SLIM_TX_CH_MAX 3
+
+#define WSA8810_NAME_1 "wsa881x.20170211"
+#define WSA8810_NAME_2 "wsa881x.20170212"
+#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */
+
+enum {
+ INT0_MI2S = 0,
+ INT1_MI2S,
+ INT2_MI2S,
+ INT3_MI2S,
+ INT4_MI2S,
+ INT5_MI2S,
+ INT6_MI2S,
+ INT_MI2S_MAX,
+};
+
+enum {
+ BT_SLIM7_RX,
+ BT_SLIM7_TX,
+ FM_SLIM8,
+ SLIM_MAX,
+};
+
+/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */
+static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = {
+ {0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */
+};
+
+static struct afe_clk_set int_mi2s_clk[INT_MI2S_MAX] = {
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_INT1_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_INT2_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_INT4_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_INT5_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_INT6_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+};
+
+struct dev_config {
+ u32 sample_rate;
+ u32 bit_format;
+ u32 channels;
+};
+
+/* Default configuration of MI2S channels */
+static struct dev_config int_mi2s_cfg[] = {
+ [INT0_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [INT1_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [INT2_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [INT3_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [INT4_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [INT5_MI2S] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [INT6_MI2S] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static struct dev_config bt_fm_cfg[] = {
+ [BT_SLIM7_RX] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [BT_SLIM7_TX] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [FM_SLIM8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static char const *int_mi2s_rate_text[] = {"KHZ_8", "KHZ_16",
+ "KHZ_32", "KHZ_44P1", "KHZ_48",
+ "KHZ_96", "KHZ_192"};
+static const char *const int_mi2s_ch_text[] = {"One", "Two"};
+static const char *const int_mi2s_tx_ch_text[] = {"One", "Two",
+ "Three", "Four"};
+static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"};
+static const char *const loopback_mclk_text[] = {"DISABLE", "ENABLE"};
+static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+static char const *bt_sample_rate_rx_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+static char const *bt_sample_rate_tx_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_sample_rate, int_mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_chs, int_mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_sample_rate, int_mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_chs, int_mi2s_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_sample_rate, int_mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_chs, int_mi2s_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_sample_rate, int_mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_chs, int_mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int5_mi2s_tx_chs, int_mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(loopback_mclk_en, loopback_mclk_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_rx, bt_sample_rate_rx_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_tx, bt_sample_rate_tx_text);
+
+static int msm_dmic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+static int msm_int_enable_dig_cdc_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+static int msm_int_mclk0_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+static int msm_int_mi2s_snd_startup(struct snd_pcm_substream *substream);
+static void msm_int_mi2s_snd_shutdown(struct snd_pcm_substream *substream);
+
+static struct wcd_mbhc_config *mbhc_cfg_ptr;
+static struct snd_info_entry *codec_root;
+
+static int int_mi2s_get_bit_format_val(int bit_format)
+{
+ int val = 0;
+
+ switch (bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ val = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+static int int_mi2s_get_bit_format(int val)
+{
+ int bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+
+ switch (val) {
+ case 0:
+ bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 1:
+ bit_fmt = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 2:
+ bit_fmt = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ default:
+ bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return bit_fmt;
+}
+
+static int int_mi2s_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+ int port_id = 0;
+
+ if (strnstr(kcontrol->id.name, "INT0_MI2S", sizeof("INT0_MI2S")))
+ port_id = INT0_MI2S;
+ else if (strnstr(kcontrol->id.name, "INT2_MI2S", sizeof("INT2_MI2S")))
+ port_id = INT2_MI2S;
+ else if (strnstr(kcontrol->id.name, "INT3_MI2S", sizeof("INT3_MI2S")))
+ port_id = INT3_MI2S;
+ else if (strnstr(kcontrol->id.name, "INT4_MI2S", sizeof("INT4_MI2S")))
+ port_id = INT4_MI2S;
+ else {
+ pr_err("%s: unsupported channel: %s",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ return port_id;
+}
+
+static int int_mi2s_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = int_mi2s_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ ucontrol->value.enumerated.item[0] =
+ int_mi2s_get_bit_format_val(int_mi2s_cfg[ch_num].bit_format);
+
+ pr_debug("%s: int_mi2s[%d]_bit_format = %d, ucontrol value = %d\n",
+ __func__, ch_num, int_mi2s_cfg[ch_num].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int int_mi2s_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ch_num = int_mi2s_get_port_idx(kcontrol);
+
+ if (ch_num < 0)
+ return ch_num;
+
+ int_mi2s_cfg[ch_num].bit_format =
+ int_mi2s_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: int_mi2s[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+ __func__, ch_num, int_mi2s_cfg[ch_num].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static inline int param_is_mask(int p)
+{
+ return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p,
+ int n)
+{
+ return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
+{
+ if (bit >= SNDRV_MASK_MAX)
+ return;
+ if (param_is_mask(n)) {
+ struct snd_mask *m = param_to_mask(p, n);
+
+ m->bits[0] = 0;
+ m->bits[1] = 0;
+ m->bits[bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
+static int int_mi2s_get_sample_rate_val(int sample_rate)
+{
+ int sample_rate_val;
+
+ switch (sample_rate) {
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 0;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 6;
+ break;
+ default:
+ sample_rate_val = 4;
+ break;
+ }
+ return sample_rate_val;
+}
+
+static int int_mi2s_get_sample_rate(int value)
+{
+ int sample_rate;
+
+ switch (value) {
+ case 0:
+ sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 6:
+ sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ default:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return sample_rate;
+}
+
+static int int_mi2s_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = int_mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ int_mi2s_cfg[idx].sample_rate =
+ int_mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_sample_rate = %d, item = %d\n", __func__,
+ idx, int_mi2s_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int int_mi2s_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = int_mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ int_mi2s_get_sample_rate_val(int_mi2s_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_sample_rate = %d, item = %d\n", __func__,
+ idx, int_mi2s_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int int_mi2s_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = int_mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ pr_debug("%s: int_mi2s_[%d]_rx_ch = %d\n", __func__,
+ idx, int_mi2s_cfg[idx].channels);
+ ucontrol->value.enumerated.item[0] = int_mi2s_cfg[idx].channels - 1;
+
+ return 0;
+}
+
+static int int_mi2s_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = int_mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ int_mi2s_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+ pr_debug("%s: int_mi2s_[%d]_ch = %d\n", __func__,
+ idx, int_mi2s_cfg[idx].channels);
+
+ return 1;
+}
+
+static const struct snd_soc_dapm_widget msm_int_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY_S("INT_MCLK0", -1, SND_SOC_NOPM, 0, 0,
+ msm_int_mclk0_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Secondary Mic", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic1", msm_dmic_event),
+ SND_SOC_DAPM_MIC("Digital Mic2", msm_dmic_event),
+ SND_SOC_DAPM_MIC("Digital Mic3", msm_dmic_event),
+ SND_SOC_DAPM_MIC("Digital Mic4", msm_dmic_event),
+};
+
+static int msm_config_hph_compander_gpio(bool enable,
+ struct snd_soc_codec *codec)
+{
+ struct snd_soc_card *card = codec->component.card;
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ pr_debug("%s: %s HPH Compander\n", __func__,
+ enable ? "Enable" : "Disable");
+
+ if (enable) {
+ ret = msm_cdc_pinctrl_select_active_state(pdata->comp_gpio_p);
+ if (ret) {
+ pr_err("%s: gpio set cannot be activated %s\n",
+ __func__, "comp_gpio");
+ goto done;
+ }
+ } else {
+ ret = msm_cdc_pinctrl_select_sleep_state(pdata->comp_gpio_p);
+ if (ret) {
+ pr_err("%s: gpio set cannot be de-activated %s\n",
+ __func__, "comp_gpio");
+ goto done;
+ }
+ }
+
+done:
+ return ret;
+}
+
+static int is_ext_spk_gpio_support(struct platform_device *pdev,
+ struct msm_asoc_mach_data *pdata)
+{
+ const char *spk_ext_pa = "qcom,msm-spk-ext-pa";
+
+ pr_debug("%s:Enter\n", __func__);
+
+ pdata->spk_ext_pa_gpio = of_get_named_gpio(pdev->dev.of_node,
+ spk_ext_pa, 0);
+
+ if (pdata->spk_ext_pa_gpio < 0) {
+ dev_dbg(&pdev->dev,
+ "%s: missing %s in dt node\n", __func__, spk_ext_pa);
+ } else {
+ if (!gpio_is_valid(pdata->spk_ext_pa_gpio)) {
+ pr_err("%s: Invalid external speaker gpio: %d",
+ __func__, pdata->spk_ext_pa_gpio);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int enable_spk_ext_pa(struct snd_soc_codec *codec, int enable)
+{
+ struct snd_soc_card *card = codec->component.card;
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ if (!gpio_is_valid(pdata->spk_ext_pa_gpio)) {
+ pr_err("%s: Invalid gpio: %d\n", __func__,
+ pdata->spk_ext_pa_gpio);
+ return false;
+ }
+
+ pr_debug("%s: %s external speaker PA\n", __func__,
+ enable ? "Enable" : "Disable");
+
+ if (enable) {
+ ret = msm_cdc_pinctrl_select_active_state(
+ pdata->ext_spk_gpio_p);
+ if (ret) {
+ pr_err("%s: gpio set cannot be de-activated %s\n",
+ __func__, "ext_spk_gpio");
+ return ret;
+ }
+ gpio_set_value_cansleep(pdata->spk_ext_pa_gpio, enable);
+ } else {
+ gpio_set_value_cansleep(pdata->spk_ext_pa_gpio, enable);
+ ret = msm_cdc_pinctrl_select_sleep_state(
+ pdata->ext_spk_gpio_p);
+ if (ret) {
+ pr_err("%s: gpio set cannot be de-activated %s\n",
+ __func__, "ext_spk_gpio");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int int_mi2s_get_idx_from_beid(int32_t be_id)
+{
+ int idx = 0;
+
+ switch (be_id) {
+ case MSM_BACKEND_DAI_INT0_MI2S_RX:
+ idx = INT0_MI2S;
+ break;
+ case MSM_BACKEND_DAI_INT2_MI2S_TX:
+ idx = INT2_MI2S;
+ break;
+ case MSM_BACKEND_DAI_INT3_MI2S_TX:
+ idx = INT3_MI2S;
+ break;
+ case MSM_BACKEND_DAI_INT4_MI2S_RX:
+ idx = INT4_MI2S;
+ break;
+ case MSM_BACKEND_DAI_INT5_MI2S_TX:
+ idx = INT5_MI2S;
+ break;
+ default:
+ idx = INT0_MI2S;
+ break;
+ }
+
+ return idx;
+}
+
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ return 0;
+}
+
+static int int_mi2s_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ int idx;
+
+ pr_debug("%s: format = %d, rate = %d\n",
+ __func__, params_format(params), params_rate(params));
+
+ switch (dai_link->be_id) {
+ case MSM_BACKEND_DAI_INT0_MI2S_RX:
+ case MSM_BACKEND_DAI_INT2_MI2S_TX:
+ case MSM_BACKEND_DAI_INT3_MI2S_TX:
+ case MSM_BACKEND_DAI_INT4_MI2S_RX:
+ case MSM_BACKEND_DAI_INT5_MI2S_TX:
+ idx = int_mi2s_get_idx_from_beid(dai_link->be_id);
+ rate->min = rate->max = int_mi2s_cfg[idx].sample_rate;
+ channels->min = channels->max =
+ int_mi2s_cfg[idx].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ int_mi2s_cfg[idx].bit_format);
+ break;
+ default:
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return 0;
+}
+
+static int msm_btfm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ switch (dai_link->be_id) {
+ case MSM_BACKEND_DAI_SLIMBUS_7_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ bt_fm_cfg[BT_SLIM7_RX].bit_format);
+ rate->min = rate->max = bt_fm_cfg[BT_SLIM7_RX].sample_rate;
+ channels->min = channels->max =
+ bt_fm_cfg[BT_SLIM7_RX].channels;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_7_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ bt_fm_cfg[BT_SLIM7_TX].bit_format);
+ rate->min = rate->max = bt_fm_cfg[BT_SLIM7_TX].sample_rate;
+ channels->min = channels->max =
+ bt_fm_cfg[BT_SLIM7_TX].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SLIMBUS_8_TX:
+ rate->min = rate->max = bt_fm_cfg[FM_SLIM8].sample_rate;
+ channels->min = channels->max =
+ bt_fm_cfg[FM_SLIM8].channels;
+ break;
+
+ default:
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return 0;
+}
+
+static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ (int_mi2s_cfg[INT5_MI2S].channels/2 - 1);
+ pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int_mi2s_cfg[INT5_MI2S].channels =
+ roundup_pow_of_two(ucontrol->value.integer.value[0] + 2);
+
+ pr_debug("%s: msm_vi_feed_tx_ch = %d\n",
+ __func__, int_mi2s_cfg[INT5_MI2S].channels);
+ return 1;
+}
+
+static int msm_int_enable_dig_cdc_clk(struct snd_soc_codec *codec,
+ int enable, bool dapm)
+{
+ int ret = 0;
+ struct msm_asoc_mach_data *pdata = NULL;
+ int clk_freq_in_hz;
+ bool int_mclk0_freq_chg = false;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+ pr_debug("%s: enable %d mclk ref counter %d\n",
+ __func__, enable,
+ atomic_read(&pdata->int_mclk0_rsc_ref));
+ if (enable) {
+ if (int_mi2s_cfg[INT0_MI2S].sample_rate ==
+ SAMPLING_RATE_44P1KHZ) {
+ clk_freq_in_hz = NATIVE_MCLK_RATE;
+ pdata->native_clk_set = true;
+ } else {
+ clk_freq_in_hz = pdata->mclk_freq;
+ pdata->native_clk_set = false;
+ }
+
+ if (pdata->digital_cdc_core_clk.clk_freq_in_hz
+ != clk_freq_in_hz)
+ int_mclk0_freq_chg = true;
+ if (!atomic_read(&pdata->int_mclk0_rsc_ref) ||
+ int_mclk0_freq_chg) {
+ cancel_delayed_work_sync(
+ &pdata->disable_int_mclk0_work);
+ mutex_lock(&pdata->cdc_int_mclk0_mutex);
+ if (atomic_read(&pdata->int_mclk0_enabled) == false ||
+ int_mclk0_freq_chg) {
+ if (atomic_read(&pdata->int_mclk0_enabled)) {
+ pdata->digital_cdc_core_clk.enable = 0;
+ afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT0_MI2S_RX,
+ &pdata->digital_cdc_core_clk);
+ }
+ pdata->digital_cdc_core_clk.clk_freq_in_hz =
+ clk_freq_in_hz;
+ pdata->digital_cdc_core_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT0_MI2S_RX,
+ &pdata->digital_cdc_core_clk);
+ if (ret < 0) {
+ pr_err("%s: failed to enable CCLK\n",
+ __func__);
+ mutex_unlock(
+ &pdata->cdc_int_mclk0_mutex);
+ return ret;
+ }
+ pr_debug("enabled digital codec core clk\n");
+ atomic_set(&pdata->int_mclk0_enabled, true);
+ }
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+ }
+ atomic_inc(&pdata->int_mclk0_rsc_ref);
+ } else {
+ cancel_delayed_work_sync(&pdata->disable_int_mclk0_work);
+ mutex_lock(&pdata->cdc_int_mclk0_mutex);
+ if (atomic_read(&pdata->int_mclk0_enabled) == true) {
+ pdata->digital_cdc_core_clk.clk_freq_in_hz =
+ DEFAULT_MCLK_RATE;
+ pdata->digital_cdc_core_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT0_MI2S_RX,
+ &pdata->digital_cdc_core_clk);
+ if (ret < 0)
+ pr_err("%s: failed to disable CCLK\n",
+ __func__);
+ atomic_set(&pdata->int_mclk0_enabled, false);
+ atomic_set(&pdata->int_mclk0_rsc_ref, 0);
+ }
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+ }
+ return ret;
+}
+
+static int loopback_mclk_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int loopback_mclk_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = -EINVAL;
+ struct msm_asoc_mach_data *pdata = NULL;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+ pr_debug("%s: mclk_rsc_ref %d enable %ld\n",
+ __func__, atomic_read(&pdata->int_mclk0_rsc_ref),
+ ucontrol->value.integer.value[0]);
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ ret = msm_cdc_pinctrl_select_active_state(pdata->pdm_gpio_p);
+ if (ret) {
+ pr_err("%s: failed to enable the pri gpios: %d\n",
+ __func__, ret);
+ break;
+ }
+ mutex_lock(&pdata->cdc_int_mclk0_mutex);
+ if ((!atomic_read(&pdata->int_mclk0_rsc_ref)) &&
+ (!atomic_read(&pdata->int_mclk0_enabled))) {
+ pdata->digital_cdc_core_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT0_MI2S_RX,
+ &pdata->digital_cdc_core_clk);
+ if (ret < 0) {
+ pr_err("%s: failed to enable the MCLK: %d\n",
+ __func__, ret);
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+ ret = msm_cdc_pinctrl_select_sleep_state(
+ pdata->pdm_gpio_p);
+ if (ret)
+ pr_err("%s: failed to disable the pri gpios: %d\n",
+ __func__, ret);
+ break;
+ }
+ atomic_set(&pdata->int_mclk0_enabled, true);
+ }
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+ atomic_inc(&pdata->int_mclk0_rsc_ref);
+ msm_anlg_cdc_mclk_enable(codec, 1, true);
+ break;
+ case 0:
+ if (atomic_read(&pdata->int_mclk0_rsc_ref) <= 0)
+ break;
+ msm_anlg_cdc_mclk_enable(codec, 0, true);
+ mutex_lock(&pdata->cdc_int_mclk0_mutex);
+ if ((!atomic_dec_return(&pdata->int_mclk0_rsc_ref)) &&
+ (atomic_read(&pdata->int_mclk0_enabled))) {
+ pdata->digital_cdc_core_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT0_MI2S_RX,
+ &pdata->digital_cdc_core_clk);
+ if (ret < 0) {
+ pr_err("%s: failed to disable the CCLK: %d\n",
+ __func__, ret);
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+ break;
+ }
+ atomic_set(&pdata->int_mclk0_enabled, false);
+ }
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+ ret = msm_cdc_pinctrl_select_sleep_state(pdata->pdm_gpio_p);
+ if (ret)
+ pr_err("%s: failed to disable the pri gpios: %d\n",
+ __func__, ret);
+ break;
+ default:
+ pr_err("%s: Unexpected input value\n", __func__);
+ break;
+ }
+ return ret;
+}
+
+static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /*
+ * Slimbus_7_Rx/Tx sample rate values should always be in sync (same)
+ * when used for BT_SCO use case. Return either Rx or Tx sample rate
+ * value.
+ */
+ switch (bt_fm_cfg[BT_SLIM7_RX].sample_rate) {
+ case SAMPLING_RATE_48KHZ:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: sample rate = %d", __func__,
+ bt_fm_cfg[BT_SLIM7_RX].sample_rate);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ bt_fm_cfg[BT_SLIM7_RX].sample_rate = SAMPLING_RATE_16KHZ;
+ bt_fm_cfg[BT_SLIM7_TX].sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ bt_fm_cfg[BT_SLIM7_RX].sample_rate = SAMPLING_RATE_48KHZ;
+ bt_fm_cfg[BT_SLIM7_TX].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 0:
+ default:
+ bt_fm_cfg[BT_SLIM7_RX].sample_rate = SAMPLING_RATE_8KHZ;
+ bt_fm_cfg[BT_SLIM7_TX].sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: sample rates: slim7_rx = %d, value = %d\n",
+ __func__,
+ bt_fm_cfg[BT_SLIM7_RX].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (bt_fm_cfg[BT_SLIM7_RX].sample_rate) {
+ case SAMPLING_RATE_48KHZ:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: sample rate = %d", __func__,
+ bt_fm_cfg[BT_SLIM7_RX].sample_rate);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ bt_fm_cfg[BT_SLIM7_RX].sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ bt_fm_cfg[BT_SLIM7_RX].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 0:
+ default:
+ bt_fm_cfg[BT_SLIM7_RX].sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: sample rates: slim7_rx = %d, value = %d\n",
+ __func__,
+ bt_fm_cfg[BT_SLIM7_RX].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_tx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (bt_fm_cfg[BT_SLIM7_TX].sample_rate) {
+ case SAMPLING_RATE_48KHZ:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: sample rate = %d", __func__,
+ bt_fm_cfg[BT_SLIM7_TX].sample_rate);
+
+ return 0;
+}
+
+static int msm_bt_sample_rate_tx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ bt_fm_cfg[BT_SLIM7_TX].sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ bt_fm_cfg[BT_SLIM7_TX].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 0:
+ default:
+ bt_fm_cfg[BT_SLIM7_TX].sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: sample rates: slim7_tx = %d, value = %d\n",
+ __func__,
+ bt_fm_cfg[BT_SLIM7_TX].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+ SOC_ENUM_EXT("INT0_MI2S_RX Format", int0_mi2s_rx_format,
+ int_mi2s_bit_format_get, int_mi2s_bit_format_put),
+ SOC_ENUM_EXT("INT2_MI2S_TX Format", int2_mi2s_tx_format,
+ int_mi2s_bit_format_get, int_mi2s_bit_format_put),
+ SOC_ENUM_EXT("INT3_MI2S_TX Format", int3_mi2s_tx_format,
+ int_mi2s_bit_format_get, int_mi2s_bit_format_put),
+ SOC_ENUM_EXT("INT0_MI2S_RX SampleRate", int0_mi2s_rx_sample_rate,
+ int_mi2s_sample_rate_get,
+ int_mi2s_sample_rate_put),
+ SOC_ENUM_EXT("INT2_MI2S_TX SampleRate", int2_mi2s_tx_sample_rate,
+ int_mi2s_sample_rate_get,
+ int_mi2s_sample_rate_put),
+ SOC_ENUM_EXT("INT3_MI2S_TX SampleRate", int3_mi2s_tx_sample_rate,
+ int_mi2s_sample_rate_get,
+ int_mi2s_sample_rate_put),
+ SOC_ENUM_EXT("INT0_MI2S_RX Channels", int0_mi2s_rx_chs,
+ int_mi2s_ch_get, int_mi2s_ch_put),
+ SOC_ENUM_EXT("INT2_MI2S_TX Channels", int2_mi2s_tx_chs,
+ int_mi2s_ch_get, int_mi2s_ch_put),
+ SOC_ENUM_EXT("INT3_MI2S_TX Channels", int3_mi2s_tx_chs,
+ int_mi2s_ch_get, int_mi2s_ch_put),
+ SOC_ENUM_EXT("Loopback MCLK", loopback_mclk_en,
+ loopback_mclk_get, loopback_mclk_put),
+ SOC_ENUM_EXT("BT SampleRate", bt_sample_rate,
+ msm_bt_sample_rate_get,
+ msm_bt_sample_rate_put),
+ SOC_ENUM_EXT("BT SampleRate RX", bt_sample_rate_rx,
+ msm_bt_sample_rate_rx_get,
+ msm_bt_sample_rate_rx_put),
+ SOC_ENUM_EXT("BT SampleRate TX", bt_sample_rate_tx,
+ msm_bt_sample_rate_tx_get,
+ msm_bt_sample_rate_tx_put),
+};
+
+static const struct snd_kcontrol_new msm_sdw_controls[] = {
+ SOC_ENUM_EXT("INT4_MI2S_RX Format", int4_mi2s_rx_format,
+ int_mi2s_bit_format_get, int_mi2s_bit_format_put),
+ SOC_ENUM_EXT("INT4_MI2S_RX SampleRate", int4_mi2s_rx_sample_rate,
+ int_mi2s_sample_rate_get,
+ int_mi2s_sample_rate_put),
+ SOC_ENUM_EXT("INT4_MI2S_RX Channels", int4_mi2s_rx_chs,
+ int_mi2s_ch_get, int_mi2s_ch_put),
+ SOC_ENUM_EXT("VI_FEED_TX Channels", int5_mi2s_tx_chs,
+ msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put),
+};
+
+static int msm_dmic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct msm_asoc_mach_data *pdata = NULL;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int ret = 0;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+ pr_debug("%s: event = %d\n", __func__, event);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = msm_cdc_pinctrl_select_active_state(pdata->dmic_gpio_p);
+ if (ret < 0) {
+ pr_err("%s: gpio set cannot be activated %sd",
+ __func__, "dmic_gpio");
+ return ret;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = msm_cdc_pinctrl_select_sleep_state(pdata->dmic_gpio_p);
+ if (ret < 0) {
+ pr_err("%s: gpio set cannot be de-activated %sd",
+ __func__, "dmic_gpio");
+ return ret;
+ }
+ break;
+ default:
+ pr_err("%s: invalid DAPM event %d\n", __func__, event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int msm_int_mclk0_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct msm_asoc_mach_data *pdata = NULL;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ int ret = 0;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+ pr_debug("%s: event = %d\n", __func__, event);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = msm_cdc_pinctrl_select_active_state(pdata->pdm_gpio_p);
+ if (ret < 0) {
+ pr_err("%s: gpio set cannot be activated %s\n",
+ __func__, "int_pdm");
+ return ret;
+ }
+ msm_int_enable_dig_cdc_clk(codec, 1, true);
+ msm_anlg_cdc_mclk_enable(codec, 1, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ pr_debug("%s: mclk_res_ref = %d\n",
+ __func__, atomic_read(&pdata->int_mclk0_rsc_ref));
+ ret = msm_cdc_pinctrl_select_sleep_state(pdata->pdm_gpio_p);
+ if (ret < 0) {
+ pr_err("%s: gpio set cannot be de-activated %sd",
+ __func__, "int_pdm");
+ return ret;
+ }
+ pr_debug("%s: disabling MCLK\n", __func__);
+ /* disable the codec mclk config*/
+ msm_anlg_cdc_mclk_enable(codec, 0, true);
+ msm_int_enable_dig_cdc_clk(codec, 0, true);
+ break;
+ default:
+ pr_err("%s: invalid DAPM event %d\n", __func__, event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int int_mi2s_get_port_id(int be_id)
+{
+ int afe_port_id;
+
+ switch (be_id) {
+ case MSM_BACKEND_DAI_INT0_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_INT0_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_INT2_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_INT2_MI2S_TX;
+ break;
+ case MSM_BACKEND_DAI_INT3_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_INT3_MI2S_TX;
+ break;
+ case MSM_BACKEND_DAI_INT4_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_INT4_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_INT5_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_INT5_MI2S_TX;
+ break;
+ default:
+ pr_err("%s: Invalid be_id: %d\n", __func__, be_id);
+ afe_port_id = -EINVAL;
+ }
+
+ return afe_port_id;
+}
+
+static int int_mi2s_get_index(int port_id)
+{
+ int index;
+
+ switch (port_id) {
+ case AFE_PORT_ID_INT0_MI2S_RX:
+ index = INT0_MI2S;
+ break;
+ case AFE_PORT_ID_INT2_MI2S_TX:
+ index = INT2_MI2S;
+ break;
+ case AFE_PORT_ID_INT3_MI2S_TX:
+ index = INT3_MI2S;
+ break;
+ case AFE_PORT_ID_INT4_MI2S_RX:
+ index = INT4_MI2S;
+ break;
+ case AFE_PORT_ID_INT5_MI2S_TX:
+ index = INT5_MI2S;
+ break;
+ default:
+ pr_err("%s: Invalid port_id: %d\n", __func__, port_id);
+ index = -EINVAL;
+ }
+
+ return index;
+}
+
+static u32 get_int_mi2s_bits_per_sample(u32 bit_format)
+{
+ u32 bit_per_sample;
+
+ switch (bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bit_per_sample = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bit_per_sample = 16;
+ break;
+ }
+
+ return bit_per_sample;
+}
+
+static void update_int_mi2s_clk_val(int idx, int stream)
+{
+ u32 bit_per_sample;
+
+ bit_per_sample =
+ get_int_mi2s_bits_per_sample(int_mi2s_cfg[idx].bit_format);
+ int_mi2s_clk[idx].clk_freq_in_hz =
+ (int_mi2s_cfg[idx].sample_rate * 2 * bit_per_sample);
+}
+
+static int int_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int port_id = 0;
+ int index;
+
+ port_id = int_mi2s_get_port_id(rtd->dai_link->be_id);
+ if (IS_ERR_VALUE(port_id)) {
+ dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__);
+ ret = port_id;
+ goto done;
+ }
+ index = int_mi2s_get_index(port_id);
+ if (index < 0) {
+ dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__);
+ ret = port_id;
+ goto done;
+ }
+ if (enable) {
+ update_int_mi2s_clk_val(index, substream->stream);
+ dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__,
+ int_mi2s_clk[index].clk_freq_in_hz);
+ }
+
+ int_mi2s_clk[index].enable = enable;
+ ret = afe_set_lpass_clock_v2(port_id,
+ &int_mi2s_clk[index]);
+ if (ret < 0) {
+ dev_err(rtd->card->dev,
+ "%s: afe lpass clock failed for port 0x%x , err:%d\n",
+ __func__, port_id, ret);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int msm_sdw_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ ret = int_mi2s_set_sclk(substream, true);
+ if (ret < 0) {
+ pr_err("%s: failed to enable sclk %d\n",
+ __func__, ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ pr_err("%s: set fmt cpu dai failed; ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static void msm_sdw_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ int ret;
+
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ ret = int_mi2s_set_sclk(substream, false);
+ if (ret < 0)
+ pr_err("%s:clock disable failed; ret=%d\n", __func__,
+ ret);
+}
+
+static int msm_int_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_codec *codec = rtd->codec_dais[ANA_CDC]->codec;
+ int ret = 0;
+ struct msm_asoc_mach_data *pdata = NULL;
+
+ pdata = snd_soc_card_get_drvdata(codec->component.card);
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ ret = int_mi2s_set_sclk(substream, true);
+ if (ret < 0) {
+ pr_err("%s: failed to enable sclk %d\n",
+ __func__, ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ pr_err("%s: set fmt cpu dai failed; ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static void msm_int_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ int ret;
+
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ ret = int_mi2s_set_sclk(substream, false);
+ if (ret < 0)
+ pr_err("%s:clock disable failed; ret=%d\n", __func__,
+ ret);
+}
+
+static void *def_msm_int_wcd_mbhc_cal(void)
+{
+ void *msm_int_wcd_cal;
+ struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+ u16 *btn_low, *btn_high;
+
+ msm_int_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS,
+ WCD_MBHC_DEF_RLOADS), GFP_KERNEL);
+ if (!msm_int_wcd_cal)
+ return NULL;
+
+#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(msm_int_wcd_cal)->X) = (Y))
+ S(v_hs_max, 1500);
+#undef S
+#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(msm_int_wcd_cal)->X) = (Y))
+ S(num_btn, WCD_MBHC_DEF_BUTTONS);
+#undef S
+
+
+ btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(msm_int_wcd_cal);
+ btn_low = btn_cfg->_v_btn_low;
+ btn_high = ((void *)&btn_cfg->_v_btn_low) +
+ (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn);
+
+ /*
+ * In SW we are maintaining two sets of threshold register
+ * one for current source and another for Micbias.
+ * all btn_low corresponds to threshold for current source
+ * all bt_high corresponds to threshold for Micbias
+ * Below thresholds are based on following resistances
+ * 0-70 == Button 0
+ * 110-180 == Button 1
+ * 210-290 == Button 2
+ * 360-680 == Button 3
+ */
+ btn_low[0] = 75;
+ btn_high[0] = 75;
+ btn_low[1] = 150;
+ btn_high[1] = 150;
+ btn_low[2] = 225;
+ btn_high[2] = 225;
+ btn_low[3] = 450;
+ btn_high[3] = 450;
+ btn_low[4] = 500;
+ btn_high[4] = 500;
+
+ return msm_int_wcd_cal;
+}
+
+static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *dig_cdc = rtd->codec_dais[DIG_CDC]->codec;
+ struct snd_soc_codec *ana_cdc = rtd->codec_dais[ANA_CDC]->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(ana_cdc);
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_card *card;
+ int ret = -ENOMEM;
+
+ pr_debug("%s(),dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+ ret = snd_soc_add_codec_controls(ana_cdc, msm_snd_controls,
+ ARRAY_SIZE(msm_snd_controls));
+ if (ret < 0) {
+ pr_err("%s: add_codec_controls failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ ret = snd_soc_add_codec_controls(ana_cdc, msm_common_snd_controls,
+ msm_common_snd_controls_size());
+ if (ret < 0) {
+ pr_err("%s: add common snd controls failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ snd_soc_dapm_new_controls(dapm, msm_int_dapm_widgets,
+ ARRAY_SIZE(msm_int_dapm_widgets));
+
+ snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4");
+
+ snd_soc_dapm_ignore_suspend(dapm, "EAR");
+ snd_soc_dapm_ignore_suspend(dapm, "HEADPHONE");
+ snd_soc_dapm_ignore_suspend(dapm, "SPK_OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC1");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC2");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC3");
+ snd_soc_dapm_sync(dapm);
+
+ dapm = snd_soc_codec_get_dapm(dig_cdc);
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC1");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC2");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC3");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC4");
+
+ snd_soc_dapm_sync(dapm);
+
+ msm_anlg_cdc_spk_ext_pa_cb(enable_spk_ext_pa, ana_cdc);
+ msm_dig_cdc_hph_comp_cb(msm_config_hph_compander_gpio, dig_cdc);
+
+ card = rtd->card->snd_card;
+ if (!codec_root)
+ codec_root = snd_register_module_info(card->module, "codecs",
+ card->proc_root);
+ if (!codec_root) {
+ pr_debug("%s: Cannot create codecs module entry\n",
+ __func__);
+ goto done;
+ }
+ pdata->codec_root = codec_root;
+ msm_dig_codec_info_create_codec_entry(codec_root, dig_cdc);
+ msm_anlg_codec_info_create_codec_entry(codec_root, ana_cdc);
+done:
+ return 0;
+}
+
+static int msm_sdw_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_codec_get_dapm(codec);
+ struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux;
+ struct snd_card *card;
+
+ snd_soc_add_codec_controls(codec, msm_sdw_controls,
+ ARRAY_SIZE(msm_sdw_controls));
+
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1_SDW Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "VIfeed_SDW");
+ snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1_SDW VI");
+ snd_soc_dapm_ignore_suspend(dapm, "VIINPUT_SDW");
+
+ snd_soc_dapm_sync(dapm);
+
+ /*
+ * Send speaker configuration only for WSA8810.
+ * Default configuration is for WSA8815.
+ */
+ if (rtd_aux && rtd_aux->component)
+ if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
+ !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
+ msm_sdw_set_spkr_mode(rtd->codec, SPKR_MODE_1);
+ msm_sdw_set_spkr_gain_offset(rtd->codec,
+ RX_GAIN_OFFSET_M1P5_DB);
+ }
+ card = rtd->card->snd_card;
+ if (!codec_root)
+ codec_root = snd_register_module_info(card->module, "codecs",
+ card->proc_root);
+ if (!codec_root) {
+ pr_debug("%s: Cannot create codecs module entry\n",
+ __func__);
+ goto done;
+ }
+ pdata->codec_root = codec_root;
+ msm_sdw_codec_info_create_codec_entry(codec_root, codec);
+done:
+ return 0;
+}
+
+static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd)
+{
+ unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158};
+ unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161};
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+}
+
+static int msm_wcn_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX];
+ u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+ int ret;
+
+ dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__,
+ codec_dai->name, codec_dai->id);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+ if (ret) {
+ dev_err(rtd->dev,
+ "%s: failed to get BTFM codec chan map\n, err:%d\n",
+ __func__, ret);
+ goto exit;
+ }
+
+ dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) be_id %d\n",
+ __func__, tx_ch_cnt, dai_link->be_id);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch);
+ if (ret)
+ dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+
+exit:
+ return ret;
+}
+
+static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width,
+ int slots)
+{
+ unsigned int slot_mask = 0;
+ int i, j;
+ unsigned int *slot_offset;
+
+ for (i = TDM_0; i < TDM_PORT_MAX; i++) {
+ slot_offset = tdm_slot_offset[i];
+
+ for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) {
+ if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ slot_mask |=
+ (1 << ((slot_offset[j] * 8) / slot_width));
+ else
+ break;
+ }
+ }
+
+ return slot_mask;
+}
+
+static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ int channels, slot_width, slots;
+ unsigned int slot_mask;
+ unsigned int *slot_offset;
+ int offset_channels = 0;
+ int i;
+
+ pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+ channels = params_channels(params);
+ switch (channels) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /*
+ * up to 8 channels HW config should
+ * use 32 bit slot width for max support of
+ * stream bit width. (slot_width > bit_width)
+ */
+ slot_width = 32;
+ break;
+ default:
+ pr_err("%s: invalid param format 0x%x\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+ slots = 8;
+ slot_mask = tdm_param_set_slot_mask(cpu_dai->id,
+ slot_width,
+ slots);
+ if (!slot_mask) {
+ pr_err("%s: invalid slot_mask 0x%x\n",
+ __func__, slot_mask);
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_err("%s: invalid param channels %d\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+ /* currently only supporting TDM_RX_0 and TDM_TX_0 */
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ slot_offset = tdm_slot_offset[TDM_0];
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ offset_channels++;
+ else
+ break;
+ }
+
+ if (offset_channels == 0) {
+ pr_err("%s: slot offset not supported, offset_channels %d\n",
+ __func__, offset_channels);
+ return -EINVAL;
+ }
+
+ if (channels > offset_channels) {
+ pr_err("%s: channels %d exceed offset_channels %d\n",
+ __func__, channels, offset_channels);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ channels, slot_offset);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, channels,
+ slot_offset, 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+end:
+ return ret;
+}
+
+static int msm_snd_card_late_probe(struct snd_soc_card *card)
+{
+ const char *be_dl_name = LPASS_BE_INT0_MI2S_RX;
+ struct snd_soc_codec *ana_cdc;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret = 0;
+
+ rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+ if (!rtd) {
+ dev_err(card->dev,
+ "%s: snd_soc_get_pcm_runtime for %s failed!\n",
+ __func__, be_dl_name);
+ return -EINVAL;
+ }
+
+ ana_cdc = rtd->codec_dais[ANA_CDC]->codec;
+ mbhc_cfg_ptr->calibration = def_msm_int_wcd_mbhc_cal();
+ if (!mbhc_cfg_ptr->calibration)
+ return -ENOMEM;
+
+ ret = msm_anlg_cdc_hs_detect(ana_cdc, mbhc_cfg_ptr);
+ if (ret) {
+ dev_err(card->dev,
+ "%s: msm_anlg_cdc_hs_detect failed\n", __func__);
+ kfree(mbhc_cfg_ptr->calibration);
+ }
+
+ return ret;
+}
+
+static struct snd_soc_ops msm_tdm_be_ops = {
+ .hw_params = msm_tdm_snd_hw_params
+};
+
+static struct snd_soc_ops msm_wcn_ops = {
+ .hw_params = msm_wcn_hw_params,
+};
+
+static struct snd_soc_ops msm_mi2s_be_ops = {
+ .startup = msm_mi2s_snd_startup,
+ .shutdown = msm_mi2s_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_aux_pcm_be_ops = {
+ .startup = msm_aux_pcm_snd_startup,
+ .shutdown = msm_aux_pcm_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_int_mi2s_be_ops = {
+ .startup = msm_int_mi2s_snd_startup,
+ .shutdown = msm_int_mi2s_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_sdw_mi2s_be_ops = {
+ .startup = msm_sdw_mi2s_snd_startup,
+ .shutdown = msm_sdw_mi2s_snd_shutdown,
+};
+
+static int msm_fe_qos_prepare(struct snd_pcm_substream *substream)
+{
+ cpumask_t mask;
+
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
+
+ cpumask_clear(&mask);
+ cpumask_set_cpu(1, &mask); /* affine to core 1 */
+ cpumask_set_cpu(2, &mask); /* affine to core 2 */
+ cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask);
+ substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES;
+
+ pm_qos_add_request(&substream->latency_pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY,
+ MSM_LL_QOS_VALUE);
+ return 0;
+}
+
+static struct snd_soc_ops msm_fe_qos_ops = {
+ .prepare = msm_fe_qos_prepare,
+};
+
+struct snd_soc_dai_link_component dlc_rx1[] = {
+ {
+ .of_node = NULL,
+ .dai_name = "msm_dig_cdc_dai_rx1",
+ },
+ {
+ .of_node = NULL,
+ .dai_name = "msm_anlg_cdc_i2s_rx1",
+ },
+};
+
+struct snd_soc_dai_link_component dlc_tx1[] = {
+ {
+ .of_node = NULL,
+ .dai_name = "msm_dig_cdc_dai_tx1",
+ },
+ {
+ .of_node = NULL,
+ .dai_name = "msm_anlg_cdc_i2s_tx1",
+ },
+};
+
+struct snd_soc_dai_link_component dlc_tx2[] = {
+ {
+ .of_node = NULL,
+ .dai_name = "msm_dig_cdc_dai_tx2",
+ },
+ {
+ .of_node = NULL,
+ .dai_name = "msm_anlg_cdc_i2s_tx2",
+ },
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm_int_dai[] = {
+ /* FrontEnd DAI Links */
+ {/* hw:x,0 */
+ .name = MSM_DAILINK_NAME(Media1),
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+ {/* hw:x,1 */
+ .name = MSM_DAILINK_NAME(Media2),
+ .stream_name = "MultiMedia2",
+ .cpu_dai_name = "MultiMedia2",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+ },
+ {/* hw:x,2 */
+ .name = "VoiceMMode1",
+ .stream_name = "VoiceMMode1",
+ .cpu_dai_name = "VoiceMMode1",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE1,
+ },
+ {/* hw:x,3 */
+ .name = "MSM VoIP",
+ .stream_name = "VoIP",
+ .cpu_dai_name = "VoIP",
+ .platform_name = "msm-voip-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_VOIP,
+ },
+ {/* hw:x,4 */
+ .name = MSM_DAILINK_NAME(ULL),
+ .stream_name = "ULL",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-dsp.2",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+ /* Hostless PCM purpose */
+ {/* hw:x,5 */
+ .name = "INT4 MI2S_RX Hostless",
+ .stream_name = "INT4 MI2S_RX Hostless",
+ .cpu_dai_name = "INT4_MI2S_RX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ /* This dainlink has MI2S support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,6 */
+ .name = "MSM AFE-PCM RX",
+ .stream_name = "AFE-PROXY RX",
+ .cpu_dai_name = "msm-dai-q6-dev.241",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ },
+ {/* hw:x,7 */
+ .name = "MSM AFE-PCM TX",
+ .stream_name = "AFE-PROXY TX",
+ .cpu_dai_name = "msm-dai-q6-dev.240",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ },
+ {/* hw:x,8 */
+ .name = MSM_DAILINK_NAME(Compress1),
+ .stream_name = "Compress1",
+ .cpu_dai_name = "MultiMedia4",
+ .platform_name = "msm-compress-dsp",
+ .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS,
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+ },
+ {/* hw:x,9*/
+ .name = "AUXPCM Hostless",
+ .stream_name = "AUXPCM Hostless",
+ .cpu_dai_name = "AUXPCM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,10 */
+ .name = "SLIMBUS_1 Hostless",
+ .stream_name = "SLIMBUS_1 Hostless",
+ .cpu_dai_name = "SLIMBUS1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1, /* dai link has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,11 */
+ .name = "INT3 MI2S_TX Hostless",
+ .stream_name = "INT3 MI2S_TX Hostless",
+ .cpu_dai_name = "INT3_MI2S_TX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,12 */
+ .name = "SLIMBUS_7 Hostless",
+ .stream_name = "SLIMBUS_7 Hostless",
+ .cpu_dai_name = "SLIMBUS7_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1, /* dai link has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,13 */
+ .name = MSM_DAILINK_NAME(LowLatency),
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+ .ops = &msm_fe_qos_ops,
+ },
+ /* LSM FE */
+ {/* hw:x,14 */
+ .name = "Listen 1 Audio Service",
+ .stream_name = "Listen 1 Audio Service",
+ .cpu_dai_name = "LSM1",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM1,
+ },
+ {/* hw:x,15 */
+ .name = MSM_DAILINK_NAME(Compress2),
+ .stream_name = "Compress2",
+ .cpu_dai_name = "MultiMedia7",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+ },
+ {/* hw:x,16 */
+ .name = MSM_DAILINK_NAME(Compress3),
+ .stream_name = "Compress3",
+ .cpu_dai_name = "MultiMedia10",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10,
+ },
+ {/* hw:x,17 */
+ .name = MSM_DAILINK_NAME(ULL_NOIRQ),
+ .stream_name = "MM_NOIRQ",
+ .cpu_dai_name = "MultiMedia8",
+ .platform_name = "msm-pcm-dsp-noirq",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+ .ops = &msm_fe_qos_ops,
+ },
+ {/* hw:x,18 */
+ .name = "HDMI_RX_HOSTLESS",
+ .stream_name = "HDMI_RX_HOSTLESS",
+ .cpu_dai_name = "HDMI_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,19 */
+ .name = "VoiceMMode2",
+ .stream_name = "VoiceMMode2",
+ .cpu_dai_name = "VoiceMMode2",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE2,
+ },
+ {/* hw:x,20 */
+ .name = "Listen 2 Audio Service",
+ .stream_name = "Listen 2 Audio Service",
+ .cpu_dai_name = "LSM2",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM2,
+ },
+ {/* hw:x,21 */
+ .name = "Listen 3 Audio Service",
+ .stream_name = "Listen 3 Audio Service",
+ .cpu_dai_name = "LSM3",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM3,
+ },
+ {/* hw:x,22 */
+ .name = "Listen 4 Audio Service",
+ .stream_name = "Listen 4 Audio Service",
+ .cpu_dai_name = "LSM4",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM4,
+ },
+ {/* hw:x,23 */
+ .name = "Listen 5 Audio Service",
+ .stream_name = "Listen 5 Audio Service",
+ .cpu_dai_name = "LSM5",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM5,
+ },
+ {/* hw:x,24 */
+ .name = "Listen 6 Audio Service",
+ .stream_name = "Listen 6 Audio Service",
+ .cpu_dai_name = "LSM6",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM6
+ },
+ {/* hw:x,25 */
+ .name = "Listen 7 Audio Service",
+ .stream_name = "Listen 7 Audio Service",
+ .cpu_dai_name = "LSM7",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM7,
+ },
+ {/* hw:x,26 */
+ .name = "Listen 8 Audio Service",
+ .stream_name = "Listen 8 Audio Service",
+ .cpu_dai_name = "LSM8",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM8,
+ },
+ {/* hw:x,27 */
+ .name = MSM_DAILINK_NAME(Media9),
+ .stream_name = "MultiMedia9",
+ .cpu_dai_name = "MultiMedia9",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+ },
+ {/* hw:x,28 */
+ .name = MSM_DAILINK_NAME(Compress4),
+ .stream_name = "Compress4",
+ .cpu_dai_name = "MultiMedia11",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11,
+ },
+ {/* hw:x,29 */
+ .name = MSM_DAILINK_NAME(Compress5),
+ .stream_name = "Compress5",
+ .cpu_dai_name = "MultiMedia12",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12,
+ },
+ {/* hw:x,30 */
+ .name = MSM_DAILINK_NAME(Compress6),
+ .stream_name = "Compress6",
+ .cpu_dai_name = "MultiMedia13",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13,
+ },
+ {/* hw:x,31 */
+ .name = MSM_DAILINK_NAME(Compress7),
+ .stream_name = "Compress7",
+ .cpu_dai_name = "MultiMedia14",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+ },
+ {/* hw:x,32 */
+ .name = MSM_DAILINK_NAME(Compress8),
+ .stream_name = "Compress8",
+ .cpu_dai_name = "MultiMedia15",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
+ },
+ {/* hw:x,33 */
+ .name = MSM_DAILINK_NAME(ULL_NOIRQ_2),
+ .stream_name = "MM_NOIRQ_2",
+ .cpu_dai_name = "MultiMedia16",
+ .platform_name = "msm-pcm-dsp-noirq",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dai link has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16,
+ },
+ {/* hw:x,34 */
+ .name = "SLIMBUS_8 Hostless",
+ .stream_name = "SLIMBUS8_HOSTLESS Capture",
+ .cpu_dai_name = "SLIMBUS8_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,35 */
+ .name = "Primary MI2S_RX Hostless",
+ .stream_name = "Primary MI2S_RX Hostless",
+ .cpu_dai_name = "PRI_MI2S_RX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ /* This dainlink has MI2S support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,36 */
+ .name = "Secondary MI2S_RX Hostless",
+ .stream_name = "Secondary MI2S_RX Hostless",
+ .cpu_dai_name = "SEC_MI2S_RX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ /* This dainlink has MI2S support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,37 */
+ .name = "Tertiary MI2S_RX Hostless",
+ .stream_name = "Tertiary MI2S_RX Hostless",
+ .cpu_dai_name = "TERT_MI2S_RX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ /* This dainlink has MI2S support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,38 */
+ .name = "INT0 MI2S_RX Hostless",
+ .stream_name = "INT0 MI2S_RX Hostless",
+ .cpu_dai_name = "INT0_MI2S_RX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ /* This dainlink has MI2S support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {/* hw:x,39 */
+ .name = "SDM660 HFP TX",
+ .stream_name = "MultiMedia6",
+ .cpu_dai_name = "MultiMedia6",
+ .platform_name = "msm-pcm-loopback",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,
+ },
+};
+
+
+static struct snd_soc_dai_link msm_int_wsa_dai[] = {
+ {/* hw:x,40 */
+ .name = LPASS_BE_INT5_MI2S_TX,
+ .stream_name = "INT5_mi2s Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.12",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "msm_sdw_codec",
+ .codec_dai_name = "msm_sdw_vifeedback",
+ .be_id = MSM_BACKEND_DAI_INT5_MI2S_TX,
+ .be_hw_params_fixup = int_mi2s_be_hw_params_fixup,
+ .ops = &msm_sdw_mi2s_be_ops,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .dpcm_capture = 1,
+ .ignore_pmdown_time = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_int_be_dai[] = {
+ /* Backend I2S DAI Links */
+ {
+ .name = LPASS_BE_INT0_MI2S_RX,
+ .stream_name = "INT0 MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.7",
+ .platform_name = "msm-pcm-routing",
+ .codecs = dlc_rx1,
+ .num_codecs = CODECS_MAX,
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE |
+ ASYNC_DPCM_SND_SOC_HW_PARAMS,
+ .be_id = MSM_BACKEND_DAI_INT0_MI2S_RX,
+ .init = &msm_audrx_init,
+ .be_hw_params_fixup = int_mi2s_be_hw_params_fixup,
+ .ops = &msm_int_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_INT3_MI2S_TX,
+ .stream_name = "INT3 MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.10",
+ .platform_name = "msm-pcm-routing",
+ .codecs = dlc_tx1,
+ .num_codecs = CODECS_MAX,
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE |
+ ASYNC_DPCM_SND_SOC_HW_PARAMS,
+ .be_id = MSM_BACKEND_DAI_INT3_MI2S_TX,
+ .be_hw_params_fixup = int_mi2s_be_hw_params_fixup,
+ .ops = &msm_int_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_INT2_MI2S_TX,
+ .stream_name = "INT2 MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.9",
+ .platform_name = "msm-pcm-routing",
+ .codecs = dlc_tx2,
+ .num_codecs = CODECS_MAX,
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE |
+ ASYNC_DPCM_SND_SOC_HW_PARAMS,
+ .be_id = MSM_BACKEND_DAI_INT2_MI2S_TX,
+ .be_hw_params_fixup = int_mi2s_be_hw_params_fixup,
+ .ops = &msm_int_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_AFE_PCM_RX,
+ .stream_name = "AFE Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.224",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_AFE_PCM_TX,
+ .stream_name = "AFE Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.225",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Uplink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_TX,
+ .stream_name = "Voice Uplink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32772",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Downlink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_RX,
+ .stream_name = "Voice Downlink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32771",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE_PLAYBACK_TX,
+ .stream_name = "Voice Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32773",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music 2 BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE2_PLAYBACK_TX,
+ .stream_name = "Voice2 Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32770",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Proxy Tx BACK END DAI Link */
+ {
+ .name = LPASS_BE_PROXY_TX,
+ .stream_name = "Proxy Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.8195",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PROXY_TX,
+ .ignore_suspend = 1,
+ },
+ /* Proxy Rx BACK END DAI Link */
+ {
+ .name = LPASS_BE_PROXY_RX,
+ .stream_name = "Proxy Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.8194",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PROXY_RX,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_USB_AUDIO_RX,
+ .stream_name = "USB Audio Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.28672",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_USB_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_USB_AUDIO_TX,
+ .stream_name = "USB Audio Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.28673",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_USB_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_RX_0,
+ .stream_name = "Primary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36864",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_TDM_TX_0,
+ .stream_name = "Primary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36865",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_RX_0,
+ .stream_name = "Secondary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36880",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_0,
+ .stream_name = "Secondary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36881",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_0,
+ .stream_name = "Tertiary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36896",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_0,
+ .stream_name = "Tertiary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36897",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_0,
+ .stream_name = "Quaternary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36912",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_0,
+ .stream_name = "Quaternary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36913",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = {
+ {
+ .name = LPASS_BE_PRI_MI2S_RX,
+ .stream_name = "Primary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_MI2S_TX,
+ .stream_name = "Primary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_RX,
+ .stream_name = "Secondary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_TX,
+ .stream_name = "Secondary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_RX,
+ .stream_name = "Tertiary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_TX,
+ .stream_name = "Tertiary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_RX,
+ .stream_name = "Quaternary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_TX,
+ .stream_name = "Quaternary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = {
+ /* Primary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Secondary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_SEC_AUXPCM_RX,
+ .stream_name = "Sec AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_SEC_AUXPCM_TX,
+ .stream_name = "Sec AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Tertiary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_TERT_AUXPCM_RX,
+ .stream_name = "Tert AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_TERT_AUXPCM_TX,
+ .stream_name = "Tert AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Quaternary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_QUAT_AUXPCM_RX,
+ .stream_name = "Quat AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.4",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_QUAT_AUXPCM_TX,
+ .stream_name = "Quat AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.4",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+};
+
+
+static struct snd_soc_dai_link msm_wcn_be_dai_links[] = {
+ {
+ .name = LPASS_BE_SLIMBUS_7_RX,
+ .stream_name = "Slimbus7 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16398",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "btfmslim_slave",
+ /* BT codec driver determines capabilities based on
+ * dai name, bt codecdai name should always contains
+ * supported usecase information
+ */
+ .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_7_RX,
+ .be_hw_params_fixup = msm_btfm_be_hw_params_fixup,
+ .ops = &msm_wcn_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_7_TX,
+ .stream_name = "Slimbus7 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16399",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "btfmslim_slave",
+ .codec_dai_name = "btfm_bt_sco_slim_tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_7_TX,
+ .be_hw_params_fixup = msm_btfm_be_hw_params_fixup,
+ .ops = &msm_wcn_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_8_TX,
+ .stream_name = "Slimbus8 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16401",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "btfmslim_slave",
+ .codec_dai_name = "btfm_fm_slim_tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_8_TX,
+ .be_hw_params_fixup = msm_btfm_be_hw_params_fixup,
+ .init = &msm_wcn_init,
+ .ops = &msm_wcn_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_wsa_be_dai_links[] = {
+ {
+ .name = LPASS_BE_INT4_MI2S_RX,
+ .stream_name = "INT4 MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.11",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm_sdw_codec",
+ .codec_dai_name = "msm_sdw_i2s_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_INT4_MI2S_RX,
+ .init = &msm_sdw_audrx_init,
+ .be_hw_params_fixup = int_mi2s_be_hw_params_fixup,
+ .ops = &msm_sdw_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link ext_disp_be_dai_link[] = {
+ /* DISP PORT BACK END DAI Link */
+ {
+ .name = LPASS_BE_DISPLAY_PORT,
+ .stream_name = "Display Port Playback",
+ .cpu_dai_name = "msm-dai-q6-dp.24608",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-ext-disp-audio-codec-rx",
+ .codec_dai_name = "msm_dp_audio_codec_rx_dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ .be_hw_params_fixup = msm_common_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_int_dai_links[
+ARRAY_SIZE(msm_int_dai) +
+ARRAY_SIZE(msm_int_wsa_dai) +
+ARRAY_SIZE(msm_int_be_dai) +
+ARRAY_SIZE(msm_mi2s_be_dai_links) +
+ARRAY_SIZE(msm_auxpcm_be_dai_links)+
+ARRAY_SIZE(msm_wcn_be_dai_links) +
+ARRAY_SIZE(msm_wsa_be_dai_links) +
+ARRAY_SIZE(ext_disp_be_dai_link)];
+
+static struct snd_soc_card sdm660_card = {
+ /* snd_soc_card_sdm660 */
+ .name = "sdm660-snd-card",
+ .dai_link = msm_int_dai,
+ .num_links = ARRAY_SIZE(msm_int_dai),
+ .late_probe = msm_snd_card_late_probe,
+};
+
+static void msm_disable_int_mclk0(struct work_struct *work)
+{
+ struct msm_asoc_mach_data *pdata = NULL;
+ struct delayed_work *dwork;
+ int ret = 0;
+
+ dwork = to_delayed_work(work);
+ pdata = container_of(dwork, struct msm_asoc_mach_data,
+ disable_int_mclk0_work);
+ mutex_lock(&pdata->cdc_int_mclk0_mutex);
+ pr_debug("%s: mclk_enabled %d mclk_rsc_ref %d\n", __func__,
+ atomic_read(&pdata->int_mclk0_enabled),
+ atomic_read(&pdata->int_mclk0_rsc_ref));
+
+ if (atomic_read(&pdata->int_mclk0_enabled) == true
+ && atomic_read(&pdata->int_mclk0_rsc_ref) == 0) {
+ pr_debug("Disable the mclk\n");
+ pdata->digital_cdc_core_clk.enable = 0;
+ pdata->digital_cdc_core_clk.clk_freq_in_hz =
+ DEFAULT_MCLK_RATE;
+ ret = afe_set_lpass_clock_v2(
+ AFE_PORT_ID_INT0_MI2S_RX,
+ &pdata->digital_cdc_core_clk);
+ if (ret < 0)
+ pr_err("%s failed to disable the CCLK\n", __func__);
+ atomic_set(&pdata->int_mclk0_enabled, false);
+ }
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+}
+
+static void msm_int_dt_parse_cap_info(struct platform_device *pdev,
+ struct msm_asoc_mach_data *pdata)
+{
+ const char *ext1_cap = "qcom,msm-micbias1-ext-cap";
+ const char *ext2_cap = "qcom,msm-micbias2-ext-cap";
+
+ pdata->micbias1_cap_mode =
+ (of_property_read_bool(pdev->dev.of_node, ext1_cap) ?
+ MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+
+ pdata->micbias2_cap_mode =
+ (of_property_read_bool(pdev->dev.of_node, ext2_cap) ?
+ MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+}
+
+static struct snd_soc_card *msm_int_populate_sndcard_dailinks(
+ struct device *dev)
+{
+ struct snd_soc_card *card = &sdm660_card;
+ struct snd_soc_dai_link *dailink;
+ int len1;
+
+ card->name = dev_name(dev);
+ len1 = ARRAY_SIZE(msm_int_dai);
+ memcpy(msm_int_dai_links, msm_int_dai, sizeof(msm_int_dai));
+ dailink = msm_int_dai_links;
+ if (!of_property_read_bool(dev->of_node,
+ "qcom,wsa-disable")) {
+ memcpy(dailink + len1,
+ msm_int_wsa_dai,
+ sizeof(msm_int_wsa_dai));
+ len1 += ARRAY_SIZE(msm_int_wsa_dai);
+ }
+ memcpy(dailink + len1, msm_int_be_dai, sizeof(msm_int_be_dai));
+ len1 += ARRAY_SIZE(msm_int_be_dai);
+
+ if (of_property_read_bool(dev->of_node,
+ "qcom,mi2s-audio-intf")) {
+ memcpy(dailink + len1,
+ msm_mi2s_be_dai_links,
+ sizeof(msm_mi2s_be_dai_links));
+ len1 += ARRAY_SIZE(msm_mi2s_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,auxpcm-audio-intf")) {
+ memcpy(dailink + len1,
+ msm_auxpcm_be_dai_links,
+ sizeof(msm_auxpcm_be_dai_links));
+ len1 += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) {
+ dev_dbg(dev, "%s(): WCN BTFM support present\n",
+ __func__);
+ memcpy(dailink + len1,
+ msm_wcn_be_dai_links,
+ sizeof(msm_wcn_be_dai_links));
+ len1 += ARRAY_SIZE(msm_wcn_be_dai_links);
+ }
+ if (!of_property_read_bool(dev->of_node, "qcom,wsa-disable")) {
+ memcpy(dailink + len1,
+ msm_wsa_be_dai_links,
+ sizeof(msm_wsa_be_dai_links));
+ len1 += ARRAY_SIZE(msm_wsa_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node, "qcom,ext-disp-audio-rx")) {
+ dev_dbg(dev, "%s(): ext disp audio support present\n",
+ __func__);
+ memcpy(dailink + len1,
+ ext_disp_be_dai_link,
+ sizeof(ext_disp_be_dai_link));
+ len1 += ARRAY_SIZE(ext_disp_be_dai_link);
+ }
+ card->dai_link = dailink;
+ card->num_links = len1;
+ return card;
+}
+
+static int msm_internal_init(struct platform_device *pdev,
+ struct msm_asoc_mach_data *pdata,
+ struct snd_soc_card *card)
+{
+ const char *type = NULL;
+ const char *hs_micbias_type = "qcom,msm-hs-micbias-type";
+ int ret;
+
+ ret = is_ext_spk_gpio_support(pdev, pdata);
+ if (ret < 0)
+ dev_dbg(&pdev->dev,
+ "%s: doesn't support external speaker pa\n",
+ __func__);
+
+ ret = of_property_read_string(pdev->dev.of_node,
+ hs_micbias_type, &type);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: missing %s in dt node\n",
+ __func__, hs_micbias_type);
+ goto err;
+ }
+ if (!strcmp(type, "external")) {
+ dev_dbg(&pdev->dev, "Headset is using external micbias\n");
+ mbhc_cfg_ptr->hs_ext_micbias = true;
+ } else {
+ dev_dbg(&pdev->dev, "Headset is using internal micbias\n");
+ mbhc_cfg_ptr->hs_ext_micbias = false;
+ }
+
+ /* initialize the int_mclk0 */
+ pdata->digital_cdc_core_clk.clk_set_minor_version =
+ AFE_API_VERSION_I2S_CONFIG;
+ pdata->digital_cdc_core_clk.clk_id =
+ Q6AFE_LPASS_CLK_ID_INT_MCLK_0;
+ pdata->digital_cdc_core_clk.clk_freq_in_hz = pdata->mclk_freq;
+ pdata->digital_cdc_core_clk.clk_attri =
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
+ pdata->digital_cdc_core_clk.clk_root =
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT;
+ pdata->digital_cdc_core_clk.enable = 1;
+
+ /* Initialize loopback mode to false */
+ pdata->lb_mode = false;
+
+ msm_int_dt_parse_cap_info(pdev, pdata);
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, pdata);
+ ret = snd_soc_of_parse_card_name(card, "qcom,model");
+ if (ret)
+ goto err;
+ /* initialize timer */
+ INIT_DELAYED_WORK(&pdata->disable_int_mclk0_work,
+ msm_disable_int_mclk0);
+ mutex_init(&pdata->cdc_int_mclk0_mutex);
+ atomic_set(&pdata->int_mclk0_rsc_ref, 0);
+ atomic_set(&pdata->int_mclk0_enabled, false);
+
+ dev_info(&pdev->dev, "%s: default codec configured\n", __func__);
+
+ return 0;
+err:
+ return ret;
+}
+
+/**
+ * msm_int_cdc_init - internal codec machine specific init.
+ *
+ * @pdev: platform device handle
+ * @pdata: private data of machine driver
+ * @card: sound card pointer reference
+ * @mbhc_cfg: MBHC config reference
+ *
+ * Returns 0.
+ */
+int msm_int_cdc_init(struct platform_device *pdev,
+ struct msm_asoc_mach_data *pdata,
+ struct snd_soc_card **card,
+ struct wcd_mbhc_config *mbhc_cfg)
+{
+ mbhc_cfg_ptr = mbhc_cfg;
+
+ *card = msm_int_populate_sndcard_dailinks(&pdev->dev);
+ msm_internal_init(pdev, pdata, *card);
+ return 0;
+}
+EXPORT_SYMBOL(msm_int_cdc_init);
diff --git a/sound/soc/msm/sdm660-internal.h b/sound/soc/msm/sdm660-internal.h
new file mode 100644
index 000000000000..ccc62b8f33dc
--- /dev/null
+++ b/sound/soc/msm/sdm660-internal.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SDM660_INTERNAL
+#define __SDM660_INTERNAL
+
+#include <sound/soc.h>
+
+#ifdef CONFIG_SND_SOC_INT_CODEC
+int msm_int_cdc_init(struct platform_device *pdev,
+ struct msm_asoc_mach_data *pdata,
+ struct snd_soc_card **card,
+ struct wcd_mbhc_config *mbhc_cfg);
+#else
+int msm_int_cdc_init(struct platform_device *pdev,
+ struct msm_asoc_mach_data *pdata,
+ struct snd_soc_card **card,
+ struct wcd_mbhc_config *mbhc_cfg)
+{
+ return 0;
+}
+#endif
+#endif
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index c1addf49c4f2..736b9c45e59a 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -409,6 +409,37 @@ err:
return ret;
}
+static void dpcm_be_hw_params_prepare(void *data)
+{
+ struct snd_compr_stream *cstream = data;
+ struct snd_soc_pcm_runtime *fe = cstream->private_data;
+ struct snd_soc_pcm_runtime *be = cstream->be;
+ int stream, ret;
+
+ if (cstream->direction == SND_COMPRESS_PLAYBACK)
+ stream = SNDRV_PCM_STREAM_PLAYBACK;
+ else
+ stream = SNDRV_PCM_STREAM_CAPTURE;
+
+ ret = dpcm_fe_dai_hw_params_be(fe, be,
+ &fe->dpcm[stream].hw_params, stream);
+ if (ret < 0) {
+ fe->err_ops = ret;
+ return;
+ }
+
+ ret = dpcm_fe_dai_prepare_be(fe, be, stream);
+ if (ret < 0) {
+ fe->err_ops = ret;
+ return;
+ }
+}
+
+static void dpcm_be_hw_params_prepare_async(void *data, async_cookie_t cookie)
+{
+ dpcm_be_hw_params_prepare(data);
+}
+
static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
struct snd_compr_params *params)
{
@@ -416,7 +447,11 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
struct snd_pcm_substream *fe_substream =
fe->pcm->streams[cstream->direction].substream;
struct snd_soc_platform *platform = fe->platform;
- int ret = 0, stream;
+ struct snd_soc_pcm_runtime *be_list[DPCM_MAX_BE_USERS];
+ struct snd_soc_dpcm *dpcm;
+ int ret = 0, stream, i, j = 0;
+
+ ASYNC_DOMAIN_EXCLUSIVE(async_domain);
if (cstream->direction == SND_COMPRESS_PLAYBACK)
stream = SNDRV_PCM_STREAM_PLAYBACK;
@@ -425,36 +460,112 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) {
- ret = platform->driver->compr_ops->set_params(cstream, params);
+ if (!(fe->dai_link->async_ops & ASYNC_DPCM_SND_SOC_HW_PARAMS)) {
+ /* first we call set_params for the platform driver
+ * this should configure the soc side
+ * if the machine has compressed ops then we call that as well
+ * expectation is that platform and machine will configure
+ * everything for this compress path, like configuring pcm
+ * port for codec
+ */
+ if (platform->driver->compr_ops &&
+ platform->driver->compr_ops->set_params) {
+ ret = platform->driver->compr_ops->set_params(cstream,
+ params);
+ if (ret < 0)
+ goto out;
+ }
+
+ if (fe->dai_link->compr_ops &&
+ fe->dai_link->compr_ops->set_params) {
+ ret = fe->dai_link->compr_ops->set_params(cstream);
+ if (ret < 0)
+ goto out;
+ }
+
+ /*
+ * Create an empty hw_params for the BE as the machine
+ * driver must fix this up to match DSP decoder and
+ * ASRC configuration.
+ * I.e. machine driver fixup for compressed BE is
+ * mandatory.
+ */
+ memset(&fe->dpcm[fe_substream->stream].hw_params, 0,
+ sizeof(struct snd_pcm_hw_params));
+
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+ ret = dpcm_be_dai_hw_params(fe, stream);
if (ret < 0)
goto out;
- }
- if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) {
- ret = fe->dai_link->compr_ops->set_params(cstream);
+ ret = dpcm_be_dai_prepare(fe, stream);
if (ret < 0)
goto out;
- }
-
- /*
- * Create an empty hw_params for the BE as the machine driver must
- * fix this up to match DSP decoder and ASRC configuration.
- * I.e. machine driver fixup for compressed BE is mandatory.
- */
- memset(&fe->dpcm[fe_substream->stream].hw_params, 0,
- sizeof(struct snd_pcm_hw_params));
-
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
-
- ret = dpcm_be_dai_hw_params(fe, stream);
- if (ret < 0)
- goto out;
+ } else {
+ /*
+ * Create an empty hw_params for the BE as the machine
+ * driver must fix this up to match DSP decoder and
+ * ASRC configuration.
+ * I.e. machine driver fixup for compressed BE is
+ * mandatory.
+ */
+ memset(&fe->dpcm[fe_substream->stream].hw_params, 0,
+ sizeof(struct snd_pcm_hw_params));
+
+ fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+ list_for_each_entry(dpcm,
+ &fe->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+
+ if (be->dai_link->async_ops &
+ ASYNC_DPCM_SND_SOC_HW_PARAMS) {
+ cstream->be = be;
+ async_schedule_domain(
+ dpcm_be_hw_params_prepare_async,
+ cstream, &async_domain);
+ } else {
+ be_list[j++] = be;
+ if (j == DPCM_MAX_BE_USERS) {
+ dev_dbg(fe->dev,
+ "ASoC: MAX backend users!\n");
+ break;
+ }
+ }
+ }
+ for (i = 0; i < j; i++) {
+ cstream->be = be_list[i];
+ dpcm_be_hw_params_prepare(cstream);
+ }
+ /* first we call set_params for the platform driver
+ * this should configure the soc side
+ * if the machine has compressed ops then we call that as well
+ * expectation is that platform and machine will configure
+ * everything this compress path, like configuring pcm port
+ * for codec
+ */
+ if (platform->driver->compr_ops &&
+ platform->driver->compr_ops->set_params) {
+ ret = platform->driver->compr_ops->set_params(cstream,
+ params);
+ if (ret < 0)
+ goto exit;
+ }
- ret = dpcm_be_dai_prepare(fe, stream);
- if (ret < 0)
- goto out;
+ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
+ if (fe->dai_link->compr_ops &&
+ fe->dai_link->compr_ops->set_params) {
+ ret = fe->dai_link->compr_ops->set_params(cstream);
+ if (ret < 0)
+ goto exit;
+ }
+exit:
+ async_synchronize_full_domain(&async_domain);
+ if (fe->err_ops < 0 || ret < 0)
+ goto out;
+ }
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
@@ -532,14 +643,15 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream,
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_soc_platform *platform = rtd->platform;
+ int ret = 0;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
if (platform->driver->compr_ops && platform->driver->compr_ops->pointer)
- platform->driver->compr_ops->pointer(cstream, tstamp);
+ ret = platform->driver->compr_ops->pointer(cstream, tstamp);
mutex_unlock(&rtd->pcm_mutex);
- return 0;
+ return ret;
}
static int soc_compr_copy(struct snd_compr_stream *cstream,
@@ -558,6 +670,22 @@ static int soc_compr_copy(struct snd_compr_stream *cstream,
return ret;
}
+static int sst_compr_set_next_track_param(struct snd_compr_stream *cstream,
+ union snd_codec_options *codec_options)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ int ret = 0;
+
+ if (platform->driver->compr_ops &&
+ platform->driver->compr_ops->set_next_track_param)
+ ret = platform->driver->compr_ops->set_next_track_param(cstream,
+ codec_options);
+
+ return ret;
+}
+
+
static int soc_compr_set_metadata(struct snd_compr_stream *cstream,
struct snd_compr_metadata *metadata)
{
@@ -590,6 +718,7 @@ static struct snd_compr_ops soc_compr_ops = {
.free = soc_compr_free,
.set_params = soc_compr_set_params,
.set_metadata = soc_compr_set_metadata,
+ .set_next_track_param = sst_compr_set_next_track_param,
.get_metadata = soc_compr_get_metadata,
.get_params = soc_compr_get_params,
.trigger = soc_compr_trigger,
@@ -606,6 +735,7 @@ static struct snd_compr_ops soc_compr_dyn_ops = {
.set_params = soc_compr_set_params_fe,
.get_params = soc_compr_get_params,
.set_metadata = soc_compr_set_metadata,
+ .set_next_track_param = sst_compr_set_next_track_param,
.get_metadata = soc_compr_get_metadata,
.trigger = soc_compr_trigger_fe,
.pointer = soc_compr_pointer,
@@ -721,8 +851,16 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
rtd->compr = compr;
compr->private_data = rtd;
- printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name,
- cpu_dai->name);
+ if (platform->driver->pcm_new) {
+ ret = platform->driver->pcm_new(rtd);
+ if (ret < 0) {
+ pr_err("asoc: compress pcm constructor failed\n");
+ goto compr_err;
+ }
+ }
+
+ dev_dbg(rtd->card->dev, "compress asoc: %s <-> %s mapping ok\n",
+ codec_dai->name, cpu_dai->name);
return ret;
compr_err:
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e69a7f8b6163..388a1edeec4c 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -303,7 +303,7 @@ static void soc_init_component_debugfs(struct snd_soc_component *component)
}
if (!component->debugfs_root) {
- dev_warn(component->dev,
+ dev_dbg(component->dev,
"ASoC: Failed to create component debugfs directory\n");
return;
}
@@ -328,7 +328,7 @@ static void soc_init_codec_debugfs(struct snd_soc_component *component)
codec->component.debugfs_root,
codec, &codec_reg_fops);
if (!codec->debugfs_reg)
- dev_warn(codec->dev,
+ dev_dbg(codec->dev,
"ASoC: Failed to create codec register debugfs file\n");
}
@@ -861,11 +861,27 @@ EXPORT_SYMBOL_GPL(snd_soc_resume);
static const struct snd_soc_dai_ops null_dai_ops = {
};
-static struct snd_soc_component *soc_find_component(
+/**
+ * soc_find_component: find a component from component_list in ASoC core
+ *
+ * @of_node: of_node of the component to query.
+ * @name: name of the component to query.
+ *
+ * function to find out if a component is already registered with ASoC core.
+ *
+ * Returns component handle for success, else NULL error.
+ */
+struct snd_soc_component *soc_find_component(
const struct device_node *of_node, const char *name)
{
struct snd_soc_component *component;
+ if (!of_node && !name) {
+ pr_err("%s: Either of_node or name must be valid\n",
+ __func__);
+ return NULL;
+ }
+
lockdep_assert_held(&client_mutex);
list_for_each_entry(component, &component_list, list) {
@@ -879,6 +895,7 @@ static struct snd_soc_component *soc_find_component(
return NULL;
}
+EXPORT_SYMBOL(soc_find_component);
static struct snd_soc_dai *snd_soc_find_dai(
const struct snd_soc_dai_link_component *dlc)
@@ -2295,8 +2312,7 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
if (dai->driver->ops->mute_stream)
return dai->driver->ops->mute_stream(dai, mute, direction);
- else if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
- dai->driver->ops->digital_mute)
+ else if (dai->driver->ops->digital_mute)
return dai->driver->ops->digital_mute(dai, mute);
else
return -ENOTSUPP;
@@ -2438,6 +2454,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
card->instantiated = 0;
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
+ mutex_init(&card->dapm_power_mutex);
ret = snd_soc_instantiate_card(card);
if (ret != 0)
@@ -3041,6 +3058,18 @@ static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm,
}
/**
+ * snd_soc_card_change_online_state - Mark if soc card is online/offline
+ *
+ * @soc_card : soc_card to mark
+ */
+void snd_soc_card_change_online_state(struct snd_soc_card *soc_card, int online)
+{
+ if (soc_card && soc_card->snd_card)
+ snd_card_change_online_state(soc_card->snd_card, online);
+}
+EXPORT_SYMBOL(snd_soc_card_change_online_state);
+
+/**
* snd_soc_register_codec - Register a codec with the ASoC core
*
* @dev: The parent device for this codec
@@ -3589,6 +3618,63 @@ static int snd_soc_get_dai_name(struct of_phandle_args *args,
return ret;
}
+/**
+ * snd_soc_info_multi_ext - external single mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single external mixer control.
+ * that accepts multiple input.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_multi_ext(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_multi_mixer_control *mc =
+ (struct soc_multi_mixer_control *)kcontrol->private_value;
+ int platform_max;
+
+ if (!mc->platform_max)
+ mc->platform_max = mc->max;
+ platform_max = mc->platform_max;
+
+ if (platform_max == 1 && !strnstr(kcontrol->id.name, " Volume", 30))
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+ uinfo->count = mc->count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = platform_max;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_multi_ext);
+
+/**
+ * snd_soc_dai_get_channel_map - configure DAI audio channel map
+ * @dai: DAI
+ * @tx_num: how many TX channels
+ * @tx_slot: pointer to an array which imply the TX slot number channel
+ * 0~num-1 uses
+ * @rx_num: how many RX channels
+ * @rx_slot: pointer to an array which imply the RX slot number channel
+ * 0~num-1 uses
+ *
+ * configure the relationship between channel number and TDM slot number.
+ */
+int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ if (dai->driver && dai->driver->ops->get_channel_map)
+ return dai->driver->ops->get_channel_map(dai, tx_num, tx_slot,
+ rx_num, rx_slot);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map);
+
int snd_soc_of_get_dai_name(struct device_node *of_node,
const char **dai_name)
{
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 7466e8c6815d..c47962c12c16 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -78,8 +78,7 @@ static int dapm_up_seq[] = {
[snd_soc_dapm_dai_link] = 2,
[snd_soc_dapm_dai_in] = 4,
[snd_soc_dapm_dai_out] = 4,
- [snd_soc_dapm_aif_in] = 4,
- [snd_soc_dapm_aif_out] = 4,
+ [snd_soc_dapm_adc] = 4,
[snd_soc_dapm_mic] = 5,
[snd_soc_dapm_mux] = 6,
[snd_soc_dapm_demux] = 6,
@@ -88,7 +87,8 @@ static int dapm_up_seq[] = {
[snd_soc_dapm_mixer] = 8,
[snd_soc_dapm_mixer_named_ctl] = 8,
[snd_soc_dapm_pga] = 9,
- [snd_soc_dapm_adc] = 10,
+ [snd_soc_dapm_aif_in] = 9,
+ [snd_soc_dapm_aif_out] = 9,
[snd_soc_dapm_out_drv] = 11,
[snd_soc_dapm_hp] = 11,
[snd_soc_dapm_spk] = 11,
@@ -100,7 +100,9 @@ static int dapm_up_seq[] = {
static int dapm_down_seq[] = {
[snd_soc_dapm_pre] = 0,
[snd_soc_dapm_kcontrol] = 1,
- [snd_soc_dapm_adc] = 2,
+ [snd_soc_dapm_aif_in] = 2,
+ [snd_soc_dapm_aif_out] = 2,
+ [snd_soc_dapm_adc] = 5,
[snd_soc_dapm_hp] = 3,
[snd_soc_dapm_spk] = 3,
[snd_soc_dapm_line] = 3,
@@ -114,8 +116,6 @@ static int dapm_down_seq[] = {
[snd_soc_dapm_micbias] = 8,
[snd_soc_dapm_mux] = 9,
[snd_soc_dapm_demux] = 9,
- [snd_soc_dapm_aif_in] = 10,
- [snd_soc_dapm_aif_out] = 10,
[snd_soc_dapm_dai_in] = 10,
[snd_soc_dapm_dai_out] = 10,
[snd_soc_dapm_dai_link] = 11,
@@ -282,6 +282,8 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
mutex_lock(&card->dapm_mutex);
list_for_each_entry(w, &card->widgets, list) {
+ if (w->ignore_suspend)
+ continue;
if (w->is_ep) {
dapm_mark_dirty(w, "Rechecking endpoints");
if (w->is_ep & SND_SOC_DAPM_EP_SINK)
@@ -431,13 +433,14 @@ static void dapm_kcontrol_free(struct snd_kcontrol *kctl)
kfree(data);
}
-static struct snd_soc_dapm_widget_list *dapm_kcontrol_get_wlist(
+struct snd_soc_dapm_widget_list *dapm_kcontrol_get_wlist(
const struct snd_kcontrol *kcontrol)
{
struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
return data->wlist;
}
+EXPORT_SYMBOL(dapm_kcontrol_get_wlist);
static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_widget *widget)
@@ -756,7 +759,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
unsigned int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
- unsigned int val;
+ unsigned int val = 0;
if (reg != SND_SOC_NOPM) {
soc_dapm_read(p->sink->dapm, reg, &val);
@@ -1512,7 +1515,7 @@ static void dapm_seq_run(struct snd_soc_card *card,
/* Do we need to apply any queued changes? */
if (sort[w->id] != cur_sort || w->reg != cur_reg ||
w->dapm != cur_dapm || w->subseq != cur_subseq) {
- if (!list_empty(&pending))
+ if (cur_dapm && !list_empty(&pending))
dapm_seq_run_coalesced(card, &pending);
if (cur_dapm && cur_dapm->seq_notifier) {
@@ -1570,12 +1573,17 @@ static void dapm_seq_run(struct snd_soc_card *card,
break;
}
+ /* Add this debug log to keep track of widgets being
+ * powered-up and powered-down */
+ dev_dbg(w->dapm->dev, "dapm: powering %s widget %s\n",
+ power_up ? "up" : "down", w->name);
+
if (ret < 0)
dev_err(w->dapm->dev,
"ASoC: Failed to apply widget power: %d\n", ret);
}
- if (!list_empty(&pending))
+ if (cur_dapm && !list_empty(&pending))
dapm_seq_run_coalesced(card, &pending);
if (cur_dapm && cur_dapm->seq_notifier) {
@@ -1807,10 +1815,13 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
LIST_HEAD(down_list);
ASYNC_DOMAIN_EXCLUSIVE(async_domain);
enum snd_soc_bias_level bias;
+ struct snd_soc_platform *p;
+ struct snd_soc_codec *c;
lockdep_assert_held(&card->dapm_mutex);
trace_snd_soc_dapm_start(card);
+ mutex_lock(&card->dapm_power_mutex);
list_for_each_entry(d, &card->dapm_list, list) {
if (dapm_idle_bias_off(d))
@@ -1888,7 +1899,9 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
dapm_pre_sequence_async(&card->dapm, 0);
/* Run other bias changes in parallel */
list_for_each_entry(d, &card->dapm_list, list) {
- if (d != &card->dapm)
+ p = snd_soc_dapm_to_platform(d);
+ c = snd_soc_dapm_to_codec(d);
+ if ((d != &card->dapm) && (c || p))
async_schedule_domain(dapm_pre_sequence_async, d,
&async_domain);
}
@@ -1912,7 +1925,9 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
/* Run all the bias changes in parallel */
list_for_each_entry(d, &card->dapm_list, list) {
- if (d != &card->dapm)
+ p = snd_soc_dapm_to_platform(d);
+ c = snd_soc_dapm_to_codec(d);
+ if ((d != &card->dapm) && (c || p))
async_schedule_domain(dapm_post_sequence_async, d,
&async_domain);
}
@@ -1929,6 +1944,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
pop_dbg(card->dev, card->pop_time,
"DAPM sequencing finished, waiting %dms\n", card->pop_time);
pop_wait(card->pop_time);
+ mutex_unlock(&card->dapm_power_mutex);
trace_snd_soc_dapm_done(card);
@@ -2639,8 +2655,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
dapm_mark_dirty(widgets[dir], "Route added");
}
- if (dapm->card->instantiated && path->connect)
- dapm_path_invalidate(path);
+ dapm_path_invalidate(path);
return 0;
err:
@@ -3935,6 +3950,9 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
for (i = 0; i < rtd->num_codecs; i++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
+ if (!cpu_dai->component->codec)
+ continue;
+
/* connect BE DAI playback if widgets are valid */
if (codec_dai->playback_widget && cpu_dai->playback_widget) {
source = cpu_dai->playback_widget;
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index acacbce2a821..793c4fd2c825 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -201,7 +201,10 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = platform_max - mc->min;
+ if (uinfo->type == SNDRV_CTL_ELEM_TYPE_INTEGER)
+ uinfo->value.integer.max = platform_max - mc->min;
+ else
+ uinfo->value.integer.max = platform_max;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
@@ -221,14 +224,12 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
int snd_soc_info_volsw_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
-
snd_soc_info_volsw(kcontrol, uinfo);
/* Max represents the number of levels in an SX control not the
- * maximum value, so add the minimum value back on
+ * maximum value.
+ * uinfo->value.integer.max is set to number of levels
+ * in snd_soc_info_volsw_sx. No further adjustment is necessary.
*/
- uinfo->value.integer.max += mc->min;
return 0;
}
@@ -378,7 +379,7 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
- int mask = (1 << (fls(min + max) - 1)) - 1;
+ unsigned int mask = (1U << (fls(min + max) - 1)) - 1;
unsigned int val;
int ret;
@@ -423,7 +424,7 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
- int mask = (1 << (fls(min + max) - 1)) - 1;
+ unsigned int mask = (1U << (fls(min + max) - 1)) - 1;
int err = 0;
unsigned int val, val_mask, val2 = 0;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index b773d61cd5bc..5845a3d53010 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -25,6 +25,7 @@
#include <linux/workqueue.h>
#include <linux/export.h>
#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -52,6 +53,26 @@ static bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream)
return codec_stream->channels_min;
}
+static const struct snd_pcm_hardware no_host_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = PAGE_SIZE >> 2,
+ .period_bytes_max = PAGE_SIZE >> 1,
+ .periods_min = 2,
+ .periods_max = 4,
+ /*
+ * Increase the max buffer bytes as PAGE_SIZE bytes is
+ * not enough to encompass all the scenarios sent by
+ * userspapce.
+ */
+ .buffer_bytes_max = PAGE_SIZE * 4,
+};
+
/**
* snd_soc_runtime_activate() - Increment active count for PCM runtime components
* @rtd: ASoC PCM runtime that is activated
@@ -156,6 +177,8 @@ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
const struct snd_pcm_hardware *hw)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ if (!runtime)
+ return 0;
runtime->hw.info = hw->info;
runtime->hw.formats = hw->formats;
runtime->hw.period_bytes_min = hw->period_bytes_min;
@@ -182,8 +205,10 @@ int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
be->dai_link->name, event, dir);
if ((event == SND_SOC_DAPM_STREAM_STOP) &&
- (be->dpcm[dir].users >= 1))
+ (be->dpcm[dir].users >= 1)) {
+ pr_debug("%s Don't close BE \n", __func__);
continue;
+ }
snd_soc_dapm_stream_event(be, dir, event);
}
@@ -468,6 +493,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
pm_runtime_get_sync(platform->dev);
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+ if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST)
+ snd_soc_set_runtime_hwparams(substream, &no_host_hardware);
/* startup the audio subsystem */
if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) {
@@ -598,7 +625,7 @@ codec_dai_err:
platform->driver->ops->close(substream);
platform_err:
- if (cpu_dai->driver->ops->shutdown)
+ if (cpu_dai->driver->ops && cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
out:
mutex_unlock(&rtd->pcm_mutex);
@@ -674,6 +701,20 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
+ /* powered down playback stream now */
+ snd_soc_dapm_stream_event(rtd,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ SND_SOC_DAPM_STREAM_STOP);
+ } else {
+ /* start delayed pop wq here for playback streams */
+ rtd->pop_wait = 1;
+ queue_delayed_work(system_power_efficient_wq,
+ &rtd->delayed_work,
+ msecs_to_jiffies(rtd->pmdown_time));
+ }
+ }
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
@@ -689,20 +730,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
- /* powered down playback stream now */
- snd_soc_dapm_stream_event(rtd,
- SNDRV_PCM_STREAM_PLAYBACK,
- SND_SOC_DAPM_STREAM_STOP);
- } else {
- /* start delayed pop wq here for playback streams */
- rtd->pop_wait = 1;
- queue_delayed_work(system_power_efficient_wq,
- &rtd->delayed_work,
- msecs_to_jiffies(rtd->pmdown_time));
- }
- } else {
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
/* capture streams can be powered down now */
snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
SND_SOC_DAPM_STREAM_STOP);
@@ -739,6 +767,11 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_stream_event(rtd,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ SND_SOC_DAPM_STREAM_START);
+
if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
ret = rtd->dai_link->ops->prepare(substream);
if (ret < 0) {
@@ -787,8 +820,15 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
cancel_delayed_work(&rtd->delayed_work);
}
- snd_soc_dapm_stream_event(rtd, substream->stream,
- SND_SOC_DAPM_STREAM_START);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->capture_active == 1)
+ snd_soc_dapm_stream_event(rtd,
+ SNDRV_PCM_STREAM_CAPTURE,
+ SND_SOC_DAPM_STREAM_START);
+ }
+ }
for (i = 0; i < rtd->num_codecs; i++)
snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
@@ -796,6 +836,13 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
out:
+ if (ret < 0 && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ pr_err("%s: Issue stop stream for codec_dai due to op failure %d = ret\n",
+ __func__, ret);
+ snd_soc_dapm_stream_event(rtd,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ SND_SOC_DAPM_STREAM_STOP);
+ }
mutex_unlock(&rtd->pcm_mutex);
return ret;
}
@@ -844,10 +891,31 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+ /* perform any hw_params fixups */
+ if ((rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) &&
+ rtd->dai_link->be_hw_params_fixup) {
+ ret = rtd->dai_link->be_hw_params_fixup(rtd,
+ params);
+ if (ret < 0)
+ dev_err(rtd->card->dev, "ASoC: fixup failed for %s\n",
+ rtd->dai_link->name);
+ }
+
ret = soc_pcm_params_symmetry(substream, params);
if (ret)
goto out;
+ /* perform any hw_params fixups */
+ if ((rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) &&
+ rtd->dai_link->be_hw_params_fixup) {
+ ret = rtd->dai_link->be_hw_params_fixup(rtd,
+ params);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "ASoC: fixup failed for %s\n",
+ rtd->dai_link->name);
+ }
+ }
+
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
ret = rtd->dai_link->ops->hw_params(substream, params);
if (ret < 0) {
@@ -920,6 +988,22 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
cpu_dai->channels = params_channels(params);
cpu_dai->sample_bits =
snd_pcm_format_physical_width(params_format(params));
+ /* malloc a page for hostless IO.
+ * FIXME: rework with alsa-lib changes so that this malloc is not required.
+ */
+ if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) {
+ substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV;
+ substream->dma_buffer.dev.dev = rtd->dev;
+ substream->dma_buffer.dev.dev->coherent_dma_mask =
+ DMA_BIT_MASK(sizeof(dma_addr_t) * 8);
+ substream->dma_buffer.private_data = NULL;
+
+ arch_setup_dma_ops(substream->dma_buffer.dev.dev,
+ 0, 0, NULL, 0);
+ ret = snd_pcm_lib_malloc_pages(substream, PAGE_SIZE);
+ if (ret < 0)
+ goto platform_err;
+ }
out:
mutex_unlock(&rtd->pcm_mutex);
@@ -1003,6 +1087,9 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free)
cpu_dai->driver->ops->hw_free(substream, cpu_dai);
+ if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST)
+ snd_pcm_lib_free_pages(substream);
+
mutex_unlock(&rtd->pcm_mutex);
return 0;
}
@@ -1099,6 +1186,9 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
if (platform->driver->ops && platform->driver->ops->pointer)
offset = platform->driver->ops->pointer(substream);
+ if (platform->driver->delay_blk)
+ return offset;
+
if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay)
delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
@@ -1123,6 +1213,22 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
return offset;
}
+static int soc_pcm_delay_blk(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_sframes_t delay = 0;
+
+ if (platform->driver->delay_blk)
+ delay = platform->driver->delay_blk(substream,
+ rtd->codec_dais[0]);
+
+ runtime->delay = delay;
+
+ return 0;
+}
+
/* connect a FE and BE */
static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
struct snd_soc_pcm_runtime *be, int stream)
@@ -1229,7 +1335,11 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
if (!be->dai_link->no_pcm)
continue;
- if (be->cpu_dai->playback_widget == widget)
+ if ((be->cpu_dai->playback_widget == widget &&
+ (be->dai_link->stream_name &&
+ !strcmp(be->dai_link->stream_name,
+ be->cpu_dai->playback_widget->sname))) ||
+ be->codec_dai->playback_widget == widget)
return be;
for (j = 0; j < be->num_codecs; j++) {
@@ -1246,7 +1356,11 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
if (!be->dai_link->no_pcm)
continue;
- if (be->cpu_dai->capture_widget == widget)
+ if ((be->cpu_dai->capture_widget == widget &&
+ (be->dai_link->stream_name &&
+ !strcmp(be->dai_link->stream_name,
+ be->cpu_dai->capture_widget->sname))) ||
+ be->codec_dai->capture_widget == widget)
return be;
for (j = 0; j < be->num_codecs; j++) {
@@ -1716,14 +1830,14 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
- /* shutdown the BEs */
- dpcm_be_dai_shutdown(fe, substream->stream);
-
dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name);
/* now shutdown the frontend */
soc_pcm_close(substream);
+ /* shutdown the BEs */
+ dpcm_be_dai_shutdown(fe, substream->stream);
+
/* run the stream event for each BE */
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
@@ -1802,6 +1916,81 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
return 0;
}
+int dpcm_fe_dai_hw_params_be(struct snd_soc_pcm_runtime *fe,
+ struct snd_soc_pcm_runtime *be,
+ struct snd_pcm_hw_params *params, int stream)
+{
+ int ret;
+ struct snd_soc_dpcm *dpcm;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+
+ /* is this op for this BE ? */
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ return 0;
+
+ /* only allow hw_params() if no connected FEs are running */
+ if (!snd_soc_dpcm_can_be_params(fe, be, stream))
+ return 0;
+
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
+ (be->dpcm[stream].state !=
+ SND_SOC_DPCM_STATE_HW_PARAMS) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
+ return 0;
+
+ dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
+ fe->dai_link->name);
+
+ /* perform any hw_params fixups */
+ if (be->dai_link->be_hw_params_fixup) {
+ ret = be->dai_link->be_hw_params_fixup(be,
+ params);
+ if (ret < 0) {
+ dev_err(be->dev,
+ "ASoC: hw_params BE fixup failed %d\n",
+ ret);
+ goto unwind;
+ }
+ }
+
+ ret = soc_pcm_hw_params(be_substream, params);
+ if (ret < 0) {
+ dev_err(be->dev, "ASoC: hw_params BE failed %d\n", ret);
+ goto unwind;
+ }
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
+ return 0;
+
+unwind:
+ /* disable any enabled and non active backends */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ continue;
+
+ /* only allow hw_free() if no connected FEs are running */
+ if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+ continue;
+
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
+ (be->dpcm[stream].state
+ != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+ (be->dpcm[stream].state
+ != SND_SOC_DPCM_STATE_HW_FREE) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ continue;
+
+ soc_pcm_hw_free(be_substream);
+ }
+
+ return ret;
+}
+
int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm;
@@ -2147,6 +2336,35 @@ out:
return ret;
}
+int dpcm_fe_dai_prepare_be(struct snd_soc_pcm_runtime *fe,
+ struct snd_soc_pcm_runtime *be, int stream)
+{
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+ int ret = 0;
+
+ /* is this op for this BE ? */
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ return 0;
+
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ return 0;
+
+ dev_dbg(be->dev, "ASoC: prepare BE %s\n",
+ fe->dai_link->name);
+
+ ret = soc_pcm_prepare(be_substream);
+ if (ret < 0) {
+ dev_err(be->dev, "ASoC: backend prepare failed %d\n",
+ ret);
+ return ret;
+ }
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
+ return ret;
+}
+
static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *fe = substream->private_data;
@@ -2198,13 +2416,94 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
return ret;
}
+static void dpcm_be_async_prepare(void *data, async_cookie_t cookie)
+{
+ struct snd_soc_dpcm *dpcm = data;
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ int stream = dpcm->stream;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+ int ret;
+
+ dev_dbg(be->dev, "%s ASoC: prepare BE %s\n", __func__,
+ dpcm->fe->dai_link->name);
+ ret = soc_pcm_prepare(be_substream);
+ if (ret < 0) {
+ be->err_ops = ret;
+ dev_err(be->dev, "ASoC: backend prepare failed %d\n",
+ ret);
+ return;
+ }
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
+}
+
+void dpcm_be_dai_prepare_async(struct snd_soc_pcm_runtime *fe, int stream,
+ struct async_domain *domain)
+{
+ struct snd_soc_dpcm *dpcm;
+ struct snd_soc_dpcm *dpcm_async[DPCM_MAX_BE_USERS];
+ int i = 0, j;
+
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+
+ be->err_ops = 0;
+ /* is this op for this BE ? */
+ if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+ continue;
+
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ continue;
+
+ /* does this BE support async op ?*/
+ if ((fe->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE) &&
+ (be->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE)) {
+ dpcm->stream = stream;
+ async_schedule_domain(dpcm_be_async_prepare,
+ dpcm, domain);
+ } else {
+ dpcm_async[i++] = dpcm;
+ if (i == DPCM_MAX_BE_USERS) {
+ dev_dbg(fe->dev, "ASoC: MAX backend users!\n");
+ break;
+ }
+ }
+ }
+
+ for (j = 0; j < i; j++) {
+ struct snd_soc_dpcm *dpcm = dpcm_async[j];
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+ int ret;
+
+ dev_dbg(be->dev, "ASoC: prepare BE %s\n",
+ dpcm->fe->dai_link->name);
+
+ ret = soc_pcm_prepare(be_substream);
+ if (ret < 0) {
+ dev_err(be->dev, "ASoC: backend prepare failed %d\n",
+ ret);
+ be->err_ops = ret;
+ return;
+ }
+
+ be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
+ }
+}
+
static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *fe = substream->private_data;
+ struct snd_soc_dpcm *dpcm;
int stream = substream->stream, ret = 0;
+ ASYNC_DOMAIN_EXCLUSIVE(async_domain);
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+ fe->err_ops = 0;
+
dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
@@ -2217,16 +2516,47 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
goto out;
}
- ret = dpcm_be_dai_prepare(fe, substream->stream);
- if (ret < 0)
- goto out;
+ if (!(fe->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE)) {
+ ret = dpcm_be_dai_prepare(fe, substream->stream);
+ if (ret < 0)
+ goto out;
+ /* call prepare on the frontend */
+ ret = soc_pcm_prepare(substream);
+ if (ret < 0) {
+ dev_err(fe->dev, "ASoC: prepare FE %s failed\n",
+ fe->dai_link->name);
+ goto out;
+ }
+ } else {
+ dpcm_be_dai_prepare_async(fe, substream->stream,
+ &async_domain);
- /* call prepare on the frontend */
- ret = soc_pcm_prepare(substream);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: prepare FE %s failed\n",
- fe->dai_link->name);
- goto out;
+ /* call prepare on the frontend */
+ ret = soc_pcm_prepare(substream);
+ if (ret < 0) {
+ fe->err_ops = ret;
+ dev_err(fe->dev, "ASoC: prepare FE %s failed\n",
+ fe->dai_link->name);
+ }
+
+ async_synchronize_full_domain(&async_domain);
+
+ /* check if any BE failed */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients,
+ list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+
+ if (be->err_ops < 0) {
+ ret = be->err_ops;
+ goto out;
+ }
+ }
+
+ /* check if FE failed */
+ if (fe->err_ops < 0) {
+ ret = fe->err_ops;
+ goto out;
+ }
}
/* run the stream event for each BE */
@@ -2240,6 +2570,18 @@ out:
return ret;
}
+static int soc_pcm_compat_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+
+ if (platform->driver->ops->compat_ioctl)
+ return platform->driver->ops->compat_ioctl(substream,
+ cmd, arg);
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
static int soc_pcm_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
@@ -2668,9 +3010,27 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
if (capture)
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
+ if (platform->driver->pcm_new)
+ rtd->platform->driver->pcm_new(rtd);
goto out;
}
+ /* setup any hostless PCMs - i.e. no host IO is performed */
+ if (rtd->dai_link->no_host_mode) {
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->hw_no_buffer = 1;
+ snd_soc_set_runtime_hwparams(
+ pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+ &no_host_hardware);
+ }
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->hw_no_buffer = 1;
+ snd_soc_set_runtime_hwparams(
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+ &no_host_hardware);
+ }
+ }
+
/* ASoC PCM operations */
if (rtd->dai_link->dynamic) {
rtd->ops.open = dpcm_fe_dai_open;
@@ -2680,7 +3040,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
rtd->ops.hw_free = dpcm_fe_dai_hw_free;
rtd->ops.close = dpcm_fe_dai_close;
rtd->ops.pointer = soc_pcm_pointer;
+ rtd->ops.delay_blk = soc_pcm_delay_blk;
rtd->ops.ioctl = soc_pcm_ioctl;
+ rtd->ops.compat_ioctl = soc_pcm_compat_ioctl;
} else {
rtd->ops.open = soc_pcm_open;
rtd->ops.hw_params = soc_pcm_hw_params;
@@ -2689,7 +3051,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
rtd->ops.hw_free = soc_pcm_hw_free;
rtd->ops.close = soc_pcm_close;
rtd->ops.pointer = soc_pcm_pointer;
+ rtd->ops.delay_blk = soc_pcm_delay_blk;
rtd->ops.ioctl = soc_pcm_ioctl;
+ rtd->ops.compat_ioctl = soc_pcm_compat_ioctl;
}
if (platform->driver->ops) {
@@ -2718,7 +3082,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
pcm->private_free = platform->driver->pcm_free;
out:
- dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",
+ dev_dbg(rtd->card->dev, "%s <-> %s mapping ok\n",
(rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,
cpu_dai->name);
return ret;
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 53dd085d3ee2..a6884fd1e4b3 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -139,6 +139,9 @@ static int snd_soc_dummy_probe(struct platform_device *pdev)
{
int ret;
+ memset(&dummy_codec, 0,
+ sizeof(struct snd_soc_codec_driver));
+
ret = snd_soc_register_codec(&pdev->dev, &dummy_codec, &dummy_dai, 1);
if (ret < 0)
return ret;
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index a452ad7cec40..f32cfa40c192 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -162,5 +162,13 @@ config SND_BCD2000
source "sound/usb/line6/Kconfig"
+config SND_USB_AUDIO_QMI
+ tristate "USB Audio QMI Service driver"
+ depends on MSM_QMI_INTERFACE
+ help
+ Starts USB Audio QMI server to communicate with remote entity
+ to perform operations like enable or disable particular audio
+ stream on a connected USB device.
+
endif # SND_USB
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 2d2d122b069f..083887ba0346 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -13,7 +13,8 @@ snd-usb-audio-objs := card.o \
pcm.o \
proc.o \
quirks.o \
- stream.o
+ stream.o \
+ badd.o
snd-usbmidi-lib-objs := midi.o
@@ -26,3 +27,4 @@ obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o
obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/
obj-$(CONFIG_SND_USB_LINE6) += line6/
+obj-$(CONFIG_SND_USB_AUDIO_QMI) += usb_audio_qmi_v01.o usb_audio_qmi_svc.o
diff --git a/sound/usb/badd.c b/sound/usb/badd.c
new file mode 100644
index 000000000000..cc6c26cbb624
--- /dev/null
+++ b/sound/usb/badd.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
+
+struct uac3_input_terminal_descriptor badd_baif_in_term_desc = {
+ .bLength = UAC3_DT_INPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+ .bTerminalID = BADD_IN_TERM_ID_BAIF,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .wExTerminalDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+struct uac3_input_terminal_descriptor badd_baof_in_term_desc = {
+ .bLength = UAC3_DT_INPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+ .bTerminalID = BADD_IN_TERM_ID_BAOF,
+ .wTerminalType = UAC_TERMINAL_STREAMING,
+ .bAssocTerminal = 0x00,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .bmControls = 0x00000000,
+ .wExTerminalDescrID = 0x0000,
+ .wConnectorsDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+struct uac3_output_terminal_descriptor badd_baif_out_term_desc = {
+ .bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+ .bTerminalID = BADD_OUT_TERM_ID_BAIF,
+ .wTerminalType = UAC_TERMINAL_STREAMING,
+ .bAssocTerminal = 0x00, /* No associated terminal */
+ .bSourceID = BADD_FU_ID_BAIF,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .bmControls = 0x00000000, /* No controls */
+ .wExTerminalDescrID = 0x0000,
+ .wConnectorsDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+struct uac3_output_terminal_descriptor badd_baof_out_term_desc = {
+ .bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+ .bTerminalID = BADD_OUT_TERM_ID_BAOF,
+ .bSourceID = BADD_FU_ID_BAOF,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .wExTerminalDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+__u8 monoControls[] = {
+ 0x03, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00};
+
+__u8 stereoControls[] = {
+ 0x03, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00
+};
+
+__u8 badd_mu_src_ids[] = {BADD_IN_TERM_ID_BAOF, BADD_FU_ID_BAIOF};
+
+struct uac3_mixer_unit_descriptor badd_baiof_mu_desc = {
+ .bLength = UAC3_DT_MIXER_UNIT_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_MIXER_UNIT_V3,
+ .bUnitID = BADD_MU_ID_BAIOF,
+ .bNrInPins = 0x02,
+ .baSourceID = badd_mu_src_ids,
+ .bmMixerControls = 0x00,
+ .bmControls = 0x00000000,
+ .wMixerDescrStr = 0x0000
+};
+
+struct uac3_feature_unit_descriptor badd_baif_fu_desc = {
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
+ .bUnitID = BADD_FU_ID_BAIF,
+ .bSourceID = BADD_IN_TERM_ID_BAIF,
+ .wFeatureDescrStr = 0x0000
+};
+
+struct uac3_feature_unit_descriptor badd_baof_fu_desc = {
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
+ .bUnitID = BADD_FU_ID_BAOF,
+ .wFeatureDescrStr = 0x0000
+};
+
+struct uac3_feature_unit_descriptor badd_baiof_fu_desc = {
+ .bLength = 0x0f,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
+ .bUnitID = BADD_FU_ID_BAIOF,
+ .bSourceID = BADD_IN_TERM_ID_BAIF,
+ .bmaControls = monoControls,
+ .wFeatureDescrStr = 0x0000
+};
+
+struct uac3_clock_source_descriptor badd_clock_desc = {
+ .bLength = UAC3_DT_CLOCK_SRC_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_CLOCK_SOURCE,
+ .bClockID = BADD_CLOCK_SOURCE,
+ .bmControls = 0x00000001,
+ .bReferenceTerminal = 0x00,
+ .wClockSourceStr = 0x0000
+};
+
+void *badd_desc_list[] = {
+ &badd_baif_in_term_desc,
+ &badd_baof_in_term_desc,
+ &badd_baiof_mu_desc,
+ &badd_baif_fu_desc,
+ &badd_baof_fu_desc,
+ &badd_baiof_fu_desc,
+ &badd_clock_desc
+};
+
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 3ded5fe94cea..1ec81225e534 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -45,6 +45,7 @@
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <linux/module.h>
+#include <linux/usb/audio-v3.h>
#include <sound/control.h>
#include <sound/core.h>
@@ -110,6 +111,71 @@ static DEFINE_MUTEX(register_mutex);
static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
static struct usb_driver usb_audio_driver;
+struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num,
+ unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio
+ **uchip, void (*disconnect_cb)(struct snd_usb_audio *chip))
+{
+ int idx;
+ struct snd_usb_stream *as;
+ struct snd_usb_substream *subs = NULL;
+ struct snd_usb_audio *chip = NULL;
+
+ mutex_lock(&register_mutex);
+ /*
+ * legacy audio snd card number assignment is dynamic. Hence
+ * search using chip->card->number
+ */
+ for (idx = 0; idx < SNDRV_CARDS; idx++) {
+ if (!usb_chip[idx])
+ continue;
+ if (usb_chip[idx]->card->number == card_num) {
+ chip = usb_chip[idx];
+ break;
+ }
+ }
+
+ if (!chip || atomic_read(&chip->shutdown)) {
+ pr_debug("%s: instance of usb crad # %d does not exist\n",
+ __func__, card_num);
+ goto err;
+ }
+
+ if (pcm_idx >= chip->pcm_devs) {
+ pr_err("%s: invalid pcm dev number %u > %d\n", __func__,
+ pcm_idx, chip->pcm_devs);
+ goto err;
+ }
+
+ if (direction > SNDRV_PCM_STREAM_CAPTURE) {
+ pr_err("%s: invalid direction %u\n", __func__, direction);
+ goto err;
+ }
+
+ list_for_each_entry(as, &chip->pcm_list, list) {
+ if (as->pcm_index == pcm_idx) {
+ subs = &as->substream[direction];
+ if (subs->interface < 0 && !subs->data_endpoint &&
+ !subs->sync_endpoint) {
+ pr_debug("%s: stream disconnected, bail out\n",
+ __func__);
+ subs = NULL;
+ goto err;
+ }
+ goto done;
+ }
+ }
+
+done:
+ chip->card_num = card_num;
+ chip->disconnect_cb = disconnect_cb;
+err:
+ *uchip = chip;
+ if (!subs)
+ pr_debug("%s: substream instance not found\n", __func__);
+ mutex_unlock(&register_mutex);
+ return subs;
+}
+
/*
* disconnect streams
* called from usb_audio_disconnect()
@@ -215,32 +281,31 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
struct usb_device *dev = chip->dev;
struct usb_host_interface *host_iface;
struct usb_interface_descriptor *altsd;
- void *control_header;
+ struct usb_interface *usb_iface;
int i, protocol;
- int rest_bytes;
- /* find audiocontrol interface */
- host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
- control_header = snd_usb_find_csint_desc(host_iface->extra,
- host_iface->extralen,
- NULL, UAC_HEADER);
- altsd = get_iface_desc(host_iface);
- protocol = altsd->bInterfaceProtocol;
-
- if (!control_header) {
- dev_err(&dev->dev, "cannot find UAC_HEADER\n");
+ usb_iface = usb_ifnum_to_if(dev, ctrlif);
+ if (!usb_iface) {
+ snd_printk(KERN_ERR "%d:%u : does not exist\n",
+ dev->devnum, ctrlif);
return -EINVAL;
}
- rest_bytes = (void *)(host_iface->extra + host_iface->extralen) -
- control_header;
-
- /* just to be sure -- this shouldn't hit at all */
- if (rest_bytes <= 0) {
- dev_err(&dev->dev, "invalid control header\n");
+ /* find audiocontrol interface */
+ host_iface = &usb_iface->altsetting[0];
+ if (!host_iface) {
+ snd_printk(KERN_ERR "Audio Control interface is not available.");
return -EINVAL;
}
+ altsd = get_iface_desc(host_iface);
+ protocol = altsd->bInterfaceProtocol;
+
+ /*
+ * UAC 1.0 devices use AC HEADER Desc for linking AS interfaces;
+ * UAC 2.0 and 3.0 devices use IAD for linking AS interfaces
+ */
+
switch (protocol) {
default:
dev_warn(&dev->dev,
@@ -249,7 +314,27 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
/* fall through */
case UAC_VERSION_1: {
- struct uac1_ac_header_descriptor *h1 = control_header;
+ void *control_header;
+ struct uac1_ac_header_descriptor *h1;
+ int rest_bytes;
+
+ control_header = snd_usb_find_csint_desc(host_iface->extra,
+ host_iface->extralen, NULL, UAC_HEADER);
+ if (!control_header) {
+ dev_err(&dev->dev, "cannot find UAC_HEADER\n");
+ return -EINVAL;
+ }
+
+ rest_bytes = (void *)(host_iface->extra + host_iface->extralen) -
+ control_header;
+
+ /* just to be sure -- this shouldn't hit at all */
+ if (rest_bytes <= 0) {
+ dev_err(&dev->dev, "invalid control header\n");
+ return -EINVAL;
+ }
+
+ h1 = control_header;
if (rest_bytes < sizeof(*h1)) {
dev_err(&dev->dev, "too short v1 buffer descriptor\n");
@@ -277,10 +362,10 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
break;
}
- case UAC_VERSION_2: {
+ case UAC_VERSION_2:
+ case UAC_VERSION_3: {
struct usb_interface_assoc_descriptor *assoc =
- usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
-
+ usb_iface->intf_assoc;
if (!assoc) {
/*
* Firmware writers cannot count to three. So to find
@@ -297,7 +382,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
}
if (!assoc) {
- dev_err(&dev->dev, "Audio class v2 interfaces need an interface association\n");
+ dev_err(&dev->dev, "Audio class V%d interfaces need an interface association\n",
+ protocol);
return -EINVAL;
}
@@ -329,6 +415,7 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip)
list_for_each_entry_safe(ep, n, &chip->ep_list, list)
snd_usb_endpoint_free(ep);
+ mutex_destroy(&chip->dev_lock);
mutex_destroy(&chip->mutex);
kfree(chip);
return 0;
@@ -384,6 +471,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
}
mutex_init(&chip->mutex);
+ mutex_init(&chip->dev_lock);
init_waitqueue_head(&chip->shutdown_wait);
chip->index = idx;
chip->dev = dev;
@@ -495,6 +583,15 @@ static int usb_audio_probe(struct usb_interface *intf,
struct usb_host_interface *alts;
int ifnum;
u32 id;
+ struct usb_interface_assoc_descriptor *assoc;
+
+ assoc = intf->intf_assoc;
+ if (assoc && assoc->bFunctionClass == USB_CLASS_AUDIO &&
+ assoc->bFunctionProtocol == UAC_VERSION_3 &&
+ assoc->bFunctionSubClass == FULL_ADC_PROFILE) {
+ dev_info(&dev->dev, "No support for full-fledged ADC 3.0 yet!!\n");
+ return -EINVAL;
+ }
alts = &intf->altsetting[0];
ifnum = get_iface_desc(alts)->bInterfaceNumber;
@@ -583,6 +680,8 @@ static int usb_audio_probe(struct usb_interface *intf,
usb_chip[chip->index] = chip;
chip->num_interfaces++;
usb_set_intfdata(intf, chip);
+ intf->needs_remote_wakeup = 1;
+ usb_enable_autosuspend(chip->dev);
atomic_dec(&chip->active);
mutex_unlock(&register_mutex);
return 0;
@@ -615,6 +714,9 @@ static void usb_audio_disconnect(struct usb_interface *intf)
card = chip->card;
+ if (chip->disconnect_cb)
+ chip->disconnect_cb(chip);
+
mutex_lock(&register_mutex);
if (atomic_inc_return(&chip->shutdown) == 1) {
struct snd_usb_stream *as;
diff --git a/sound/usb/card.h b/sound/usb/card.h
index b24f2efea1cb..a4dbbeb309bc 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -168,4 +168,8 @@ struct snd_usb_stream {
struct list_head list;
};
+struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num,
+ unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio
+ **uchip, void (*disconnect_cb)(struct snd_usb_audio *chip));
+
#endif /* __USBAUDIO_CARD_H */
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 66294eb64501..2899797610e8 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -427,6 +427,10 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
case UAC_VERSION_2:
return set_sample_rate_v2(chip, iface, alts, fmt, rate);
+
+ /* Clock rate is fixed at 48 kHz for BADD devices */
+ case UAC_VERSION_3:
+ return 0;
}
}
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 66648b4bdd28..1f18719e09aa 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -357,7 +357,7 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
if (err < 0)
usb_audio_err(ep->chip,
- "Unable to submit urb #%d: %d (urb %p)\n",
+ "Unable to submit urb #%d: %d (urb %pK)\n",
ctx->index, err, ctx->urb);
else
set_bit(ctx->index, &ep->active_mask);
@@ -465,7 +465,7 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
ep->iface == alts->desc.bInterfaceNumber &&
ep->altsetting == alts->desc.bAlternateSetting) {
usb_audio_dbg(ep->chip,
- "Re-using EP %x in iface %d,%d @%p\n",
+ "Re-using EP %x in iface %d,%d @%pK\n",
ep_num, ep->iface, ep->altsetting, ep);
goto __exit_unlock;
}
diff --git a/sound/usb/format.c b/sound/usb/format.c
index fc64232d181b..6e5d33dec07d 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -20,6 +20,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -71,6 +72,35 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
format <<= 1;
break;
}
+
+ case UAC_VERSION_3: {
+ switch (fp->maxpacksize) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16: {
+ sample_width = BIT_RES_16_BIT;
+ sample_bytes = SUBSLOTSIZE_16_BIT;
+ break;
+ }
+
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24: {
+ sample_width = BIT_RES_24_BIT;
+ sample_bytes = SUBSLOTSIZE_24_BIT;
+ break;
+ }
+
+ default:
+ usb_audio_err(chip, "%u:%d : Invalid wMaxPacketSize\n",
+ fp->iface, fp->altsetting);
+ return pcm_formats;
+ }
+ format = 1 << format;
+ break;
+ }
}
if ((pcm_formats == 0) &&
@@ -422,6 +452,22 @@ err:
return ret;
}
+static int badd_set_audio_rate_v3(struct snd_usb_audio *chip,
+ struct audioformat *fp)
+{
+ unsigned int rate;
+
+ fp->rate_table = kmalloc(sizeof(int), GFP_KERNEL);
+ if (fp->rate_table == NULL)
+ return -ENOMEM;
+
+ fp->nr_rates = 1;
+ rate = BADD_SAMPLING_RATE;
+ fp->rate_min = fp->rate_max = fp->rate_table[0] = rate;
+ fp->rates |= snd_pcm_rate_to_rate_bit(rate);
+ return 0;
+}
+
/*
* parse the format type I and III descriptors
*/
@@ -471,6 +517,9 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
/* fp->channels is already set in this case */
ret = parse_audio_format_rates_v2(chip, fp);
break;
+ case UAC_VERSION_3:
+ ret = badd_set_audio_rate_v3(chip, fp);
+ break;
}
if (fp->channels < 1) {
@@ -558,7 +607,10 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
fmt->bFormatType);
return -ENOTSUPP;
}
- fp->fmt_type = fmt->bFormatType;
+ if (fp->protocol == UAC_VERSION_3)
+ fp->fmt_type = UAC_FORMAT_TYPE_I;
+ else
+ fp->fmt_type = fmt->bFormatType;
if (err < 0)
return err;
#if 1
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 9b9d653d5e90..0e32b987e4e8 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -50,6 +50,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -185,6 +186,17 @@ static void *find_audio_control_unit(struct mixer_build *state,
/* we just parse the header */
struct uac_feature_unit_descriptor *hdr = NULL;
+ if (state->mixer->protocol == UAC_VERSION_3) {
+ int i;
+
+ for (i = 0; i < NUM_BADD_DESCS; i++) {
+ hdr = (void *)badd_desc_list[i];
+ if (hdr->bUnitID == unit)
+ return hdr;
+ }
+
+ return NULL;
+ }
while ((hdr = snd_usb_find_desc(state->buffer, state->buflen, hdr,
USB_DT_CS_INTERFACE)) != NULL) {
if (hdr->bLength >= 4 &&
@@ -739,7 +751,7 @@ static int __check_input_term(struct mixer_build *state, int id,
term->channels = d->bNrChannels;
term->chconfig = le16_to_cpu(d->wChannelConfig);
term->name = d->iTerminal;
- } else { /* UAC_VERSION_2 */
+ } else if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_input_terminal_descriptor *d = p1;
/* call recursively to verify that the
@@ -756,6 +768,24 @@ static int __check_input_term(struct mixer_build *state, int id,
term->channels = d->bNrChannels;
term->chconfig = le32_to_cpu(d->bmChannelConfig);
term->name = d->iTerminal;
+ } else { /* UAC_VERSION_3 */
+ struct uac3_input_terminal_descriptor *d = p1;
+
+ err = __check_input_term(state,
+ d->bCSourceID, term);
+ if (err < 0)
+ return err;
+
+ term->id = id;
+ term->type = d->wTerminalType;
+ if (d->wClusterDescrID == CLUSTER_ID_MONO) {
+ term->channels = NUM_CHANNELS_MONO;
+ term->chconfig = BADD_CH_CONFIG_MONO;
+ } else {
+ term->channels = NUM_CHANNELS_STEREO;
+ term->chconfig = BADD_CH_CONFIG_STEREO;
+ }
+ term->name = d->wTerminalDescrStr;
}
return 0;
case UAC_FEATURE_UNIT: {
@@ -773,41 +803,81 @@ static int __check_input_term(struct mixer_build *state, int id,
return 0;
}
case UAC_SELECTOR_UNIT:
- case UAC2_CLOCK_SELECTOR: {
- struct uac_selector_unit_descriptor *d = p1;
- /* call recursively to retrieve the channel info */
- err = __check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->id = id;
- term->name = uac_selector_unit_iSelector(d);
+ /* UAC3_MIXER_UNIT_V3 */
+ case UAC2_CLOCK_SELECTOR:
+ /* UAC3_CLOCK_SOURCE */ {
+ if (state->mixer->protocol == UAC_VERSION_3
+ && hdr[2] == UAC3_CLOCK_SOURCE) {
+ struct uac3_clock_source_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16;
+ term->id = id;
+ term->name = d->wClockSourceStr;
+ } else if (state->mixer->protocol == UAC_VERSION_3
+ && hdr[2] == UAC3_MIXER_UNIT_V3) {
+ struct uac3_mixer_unit_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16;
+ if (d->wClusterDescrID == CLUSTER_ID_MONO) {
+ term->channels = NUM_CHANNELS_MONO;
+ term->chconfig = BADD_CH_CONFIG_MONO;
+ } else {
+ term->channels = NUM_CHANNELS_STEREO;
+ term->chconfig = BADD_CH_CONFIG_STEREO;
+ }
+ term->name = d->wMixerDescrStr;
+ } else {
+ struct uac_selector_unit_descriptor *d = p1;
+ /* call recursively to retrieve channel info */
+ err = __check_input_term(state,
+ d->baSourceID[0], term);
+ if (err < 0)
+ return err;
+ /* virtual type */
+ term->type = d->bDescriptorSubtype << 16;
+ term->id = id;
+ term->name = uac_selector_unit_iSelector(d);
+ }
return 0;
}
case UAC1_PROCESSING_UNIT:
case UAC1_EXTENSION_UNIT:
/* UAC2_PROCESSING_UNIT_V2 */
/* UAC2_EFFECT_UNIT */
+ /* UAC3_FEATURE_UNIT_V3 */
case UAC2_EXTENSION_UNIT_V2: {
- struct uac_processing_unit_descriptor *d = p1;
+ if (state->mixer->protocol == UAC_VERSION_3) {
+ struct uac_feature_unit_descriptor *d = p1;
+
+ id = d->bSourceID;
+ } else {
+ struct uac_processing_unit_descriptor *d = p1;
+
+ if (state->mixer->protocol == UAC_VERSION_2 &&
+ hdr[2] == UAC2_EFFECT_UNIT) {
+ /* UAC2/UAC1 unit IDs overlap here in an
+ * uncompatible way. Ignore this unit
+ * for now.
+ */
+ return 0;
+ }
- if (state->mixer->protocol == UAC_VERSION_2 &&
- hdr[2] == UAC2_EFFECT_UNIT) {
- /* UAC2/UAC1 unit IDs overlap here in an
- * uncompatible way. Ignore this unit for now.
- */
+ if (d->bNrInPins) {
+ id = d->baSourceID[0];
+ break; /* continue to parse */
+ }
+ /* virtual type */
+ term->type = d->bDescriptorSubtype << 16;
+ term->channels =
+ uac_processing_unit_bNrChannels(d);
+ term->chconfig =
+ uac_processing_unit_wChannelConfig(
+ d, state->mixer->protocol);
+ term->name = uac_processing_unit_iProcessing(
+ d, state->mixer->protocol);
return 0;
}
-
- if (d->bNrInPins) {
- id = d->baSourceID[0];
- break; /* continue to parse */
- }
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->channels = uac_processing_unit_bNrChannels(d);
- term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol);
- term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
- return 0;
+ break;
}
case UAC2_CLOCK_SOURCE: {
struct uac_clock_source_descriptor *d = p1;
@@ -989,6 +1059,15 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
cval->res = 1;
}
break;
+ case USB_ID(0x1130, 0x1620): /* Logitech Speakers S150 */
+ /* This audio device has 2 channels and it explicitly requires the
+ * host to send SET_CUR command on the volume control of both the
+ * channels. 7936 = 0x1F00 is the default value.
+ */
+ if (cval->channels == 2)
+ snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
+ (cval->control << 8) | 2, 7936);
+ break;
}
}
@@ -1086,8 +1165,10 @@ no_res_check:
/* USB descriptions contain the dB scale in 1/256 dB unit
* while ALSA TLV contains in 1/100 dB unit
*/
- cval->dBmin = (convert_signed_value(cval, cval->min) * 100) / 256;
- cval->dBmax = (convert_signed_value(cval, cval->max) * 100) / 256;
+ cval->dBmin =
+ (convert_signed_value(cval, cval->min) * 100) / (cval->res);
+ cval->dBmax =
+ (convert_signed_value(cval, cval->max) * 100) / (cval->res);
if (cval->dBmin > cval->dBmax) {
/* something is wrong; assume it's either from/to 0dB */
if (cval->dBmin < 0)
@@ -1279,12 +1360,18 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
struct usb_feature_control_info *ctl_info;
unsigned int len = 0;
int mapped_name = 0;
- int nameid = uac_feature_unit_iFeature(desc);
+ int nameid;
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *cval;
const struct usbmix_name_map *map;
unsigned int range;
+ if (state->mixer->protocol == UAC_VERSION_3)
+ nameid = ((struct uac3_feature_unit_descriptor *)
+ raw_desc)->wFeatureDescrStr;
+ else
+ nameid = uac_feature_unit_iFeature(desc);
+
control++; /* change from zero-based to 1-based value */
if (control == UAC_FU_GRAPHIC_EQUALIZER) {
@@ -1305,7 +1392,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
ctl_info = &audio_feature_info[control-1];
if (state->mixer->protocol == UAC_VERSION_1)
cval->val_type = ctl_info->type;
- else /* UAC_VERSION_2 */
+ else /* UAC_VERSION_2 or UAC_VERSION_3*/
cval->val_type = ctl_info->type_uac2 >= 0 ?
ctl_info->type_uac2 : ctl_info->type;
@@ -1428,6 +1515,62 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
snd_usb_mixer_add_control(&cval->head, kctl);
}
+static int find_num_channels(struct mixer_build *state, int dir)
+{
+ int num_ch = -EINVAL, num, i, j, wMaxPacketSize;
+ int ctrlif = get_iface_desc(state->mixer->hostif)->bInterfaceNumber;
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(state->mixer->chip->dev, ctrlif);
+ struct usb_interface_assoc_descriptor *assoc = usb_iface->intf_assoc;
+ struct usb_host_interface *alts;
+
+ for (i = 0; i < assoc->bInterfaceCount; i++) {
+ int intf = assoc->bFirstInterface + i;
+
+ if (intf != ctrlif) {
+ struct usb_interface *iface =
+ usb_ifnum_to_if(state->mixer->chip->dev, intf);
+
+ alts = &iface->altsetting[1];
+ if (dir == USB_DIR_OUT &&
+ get_endpoint(alts, 0)->bEndpointAddress &
+ USB_DIR_IN)
+ continue;
+ if (dir == USB_DIR_IN &&
+ !(get_endpoint(alts, 0)->bEndpointAddress &
+ USB_DIR_IN))
+ continue;
+ num = iface->num_altsetting;
+ for (j = 1; j < num; j++) {
+ num_ch = NUM_CHANNELS_MONO;
+ alts = &iface->altsetting[j];
+ wMaxPacketSize = le16_to_cpu(
+ get_endpoint(alts, 0)->
+ wMaxPacketSize);
+ switch (wMaxPacketSize) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_24:
+ break;
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24:
+ num_ch = NUM_CHANNELS_STEREO;
+ break;
+ }
+ if (num_ch == NUM_CHANNELS_MONO)
+ continue;
+ else
+ break;
+ }
+ }
+ }
+
+ return num_ch;
+}
+
/*
* parse a feature unit
*
@@ -1465,7 +1608,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
unitid);
return -EINVAL;
}
- } else {
+ } else if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_feature_unit_descriptor *ftr = _ftr;
if (hdr->bLength < 6) {
usb_audio_err(state->chip,
@@ -1482,11 +1625,118 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
unitid);
return -EINVAL;
}
+ } else {
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(state->mixer->chip->dev,
+ get_iface_desc(state->mixer->hostif)->bInterfaceNumber);
+ struct usb_interface_assoc_descriptor *assoc =
+ usb_iface->intf_assoc;
+
+ csize = 4;
+ switch (unitid) {
+ case BADD_FU_ID_BAIOF:
+ channels = NUM_CHANNELS_MONO;
+ bmaControls = monoControls;
+ badd_baif_in_term_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+
+ case BADD_FU_ID_BAOF:
+ switch (assoc->bFunctionSubClass) {
+ case PROF_HEADPHONE:
+ case PROF_HEADSET_ADAPTER:
+ channels = NUM_CHANNELS_STEREO;
+ bmaControls = stereoControls;
+ badd_baiof_mu_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+ case PROF_SPEAKERPHONE:
+ channels = NUM_CHANNELS_MONO;
+ bmaControls = monoControls;
+ badd_baof_in_term_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+ default:
+ channels = find_num_channels(state,
+ USB_DIR_OUT);
+ if (channels < 0) {
+ usb_audio_err(state->chip,
+ "unit %u: Cant find num of channels\n",
+ unitid);
+ return channels;
+ }
+
+ bmaControls = (channels == NUM_CHANNELS_MONO) ?
+ monoControls : stereoControls;
+ badd_baof_in_term_desc.wClusterDescrID =
+ (channels == NUM_CHANNELS_MONO) ?
+ CLUSTER_ID_MONO : CLUSTER_ID_STEREO;
+ break;
+ }
+ break;
+
+ case BADD_FU_ID_BAIF:
+ switch (assoc->bFunctionSubClass) {
+ case PROF_HEADSET:
+ case PROF_HEADSET_ADAPTER:
+ case PROF_SPEAKERPHONE:
+ channels = NUM_CHANNELS_MONO;
+ bmaControls = monoControls;
+ badd_baif_in_term_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+ default:
+ channels = find_num_channels(state, USB_DIR_IN);
+ if (channels < 0) {
+ usb_audio_err(state->chip,
+ "unit %u: Cant find num of channels\n",
+ unitid);
+ return channels;
+ }
+
+ bmaControls = (channels == NUM_CHANNELS_MONO) ?
+ monoControls : stereoControls;
+ badd_baif_in_term_desc.wClusterDescrID =
+ (channels == NUM_CHANNELS_MONO) ?
+ CLUSTER_ID_MONO : CLUSTER_ID_STEREO;
+ break;
+ }
+ break;
+
+ default:
+ usb_audio_err(state->chip, "Invalid unit %u\n", unitid);
+ return -EINVAL;
+ }
}
/* parse the source unit */
- if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0)
- return err;
+ if (state->mixer->protocol != UAC_VERSION_3) {
+ err = parse_audio_unit(state, hdr->bSourceID);
+ if (err < 0)
+ return err;
+ } else {
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(state->mixer->chip->dev,
+ get_iface_desc(state->mixer->hostif)->bInterfaceNumber);
+ struct usb_interface_assoc_descriptor *assoc =
+ usb_iface->intf_assoc;
+
+ switch (unitid) {
+ case BADD_FU_ID_BAOF:
+ switch (assoc->bFunctionSubClass) {
+ case PROF_HEADSET:
+ case PROF_HEADSET_ADAPTER:
+ hdr->bSourceID = BADD_MU_ID_BAIOF;
+ break;
+ default:
+ hdr->bSourceID = BADD_IN_TERM_ID_BAOF;
+ break;
+ }
+ }
+ err = parse_audio_unit(state, hdr->bSourceID);
+ if (err < 0)
+ return err;
+ }
/* determine the input source type and name */
err = check_input_term(state, hdr->bSourceID, &iterm);
@@ -1540,7 +1790,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
build_feature_ctl(state, _ftr, 0, i, &iterm,
unitid, 0);
}
- } else { /* UAC_VERSION_2 */
+ } else { /* UAC_VERSION_2 or UAC_VERSION_3*/
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
unsigned int ch_bits = 0;
unsigned int ch_read_only = 0;
@@ -1658,13 +1908,20 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
int input_pins, num_ins, num_outs;
int pin, ich, err;
- if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
- desc->bLength < sizeof(*desc) + desc->bNrInPins ||
- !(num_outs = uac_mixer_unit_bNrChannels(desc))) {
- usb_audio_err(state->chip,
- "invalid MIXER UNIT descriptor %d\n",
- unitid);
- return -EINVAL;
+ if (state->mixer->protocol == UAC_VERSION_3) {
+ input_pins = badd_baiof_mu_desc.bNrInPins;
+ num_outs =
+ (badd_baiof_mu_desc.wClusterDescrID == CLUSTER_ID_MONO) ?
+ NUM_CHANNELS_MONO : NUM_CHANNELS_STEREO;
+ } else {
+ if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
+ desc->bLength < sizeof(*desc) + desc->bNrInPins ||
+ !(num_outs = uac_mixer_unit_bNrChannels(desc))) {
+ usb_audio_err(state->chip,
+ "invalid MIXER UNIT descriptor %d\n",
+ unitid);
+ return -EINVAL;
+ }
}
num_ins = 0;
@@ -1684,9 +1941,14 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
int och, ich_has_controls = 0;
for (och = 0; och < num_outs; och++) {
- __u8 *c = uac_mixer_unit_bmControls(desc,
- state->mixer->protocol);
+ __u8 *c = NULL;
+ if (state->mixer->protocol == UAC_VERSION_3)
+ c =
+ &(badd_baiof_mu_desc.bmMixerControls);
+ else
+ c = uac_mixer_unit_bmControls(desc,
+ state->mixer->protocol);
if (check_matrix_bitmap(c, ich, och, num_outs)) {
ich_has_controls = 1;
break;
@@ -2209,16 +2471,28 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
case UAC_MIXER_UNIT:
return parse_audio_mixer_unit(state, unitid, p1);
case UAC_SELECTOR_UNIT:
+ /* UAC3_MIXER_UNIT_V3 has the same value */
case UAC2_CLOCK_SELECTOR:
- return parse_audio_selector_unit(state, unitid, p1);
+ /* UAC3_CLOCK_SOURCE has the same value */
+ if (state->mixer->protocol == UAC_VERSION_3 &&
+ p1[2] == UAC3_CLOCK_SOURCE)
+ return 0; /* NOP */
+ else if (state->mixer->protocol == UAC_VERSION_3
+ && p1[2] == UAC3_MIXER_UNIT_V3)
+ return parse_audio_mixer_unit(state, unitid, p1);
+ else
+ return parse_audio_selector_unit(state, unitid, p1);
case UAC_FEATURE_UNIT:
return parse_audio_feature_unit(state, unitid, p1);
case UAC1_PROCESSING_UNIT:
/* UAC2_EFFECT_UNIT has the same value */
+ /* UAC3_FEATURE_UNIT_V3 has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
return parse_audio_processing_unit(state, unitid, p1);
- else
+ else if (state->mixer->protocol == UAC_VERSION_2)
return 0; /* FIXME - effect units not implemented yet */
+ else
+ return parse_audio_feature_unit(state, unitid, p1);
case UAC1_EXTENSION_UNIT:
/* UAC2_PROCESSING_UNIT_V2 has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
@@ -2256,6 +2530,23 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
return 0;
}
+static int make_out_term(struct mixer_build state, int wTerminalType)
+{
+ struct uac3_output_terminal_descriptor *desc = NULL;
+
+ if (wTerminalType == UAC_TERMINAL_STREAMING)
+ desc = &badd_baif_out_term_desc;
+ else {
+ desc = &badd_baof_out_term_desc;
+ desc->wTerminalType = wTerminalType;
+ }
+ set_bit(desc->bTerminalID, state.unitbitmap);
+ state.oterm.id = desc->bTerminalID;
+ state.oterm.type = desc->wTerminalType;
+ state.oterm.name = desc->wTerminalDescrStr;
+ return parse_audio_unit(&state, desc->bSourceID);
+}
+
/*
* create mixer controls
*
@@ -2264,9 +2555,8 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
{
struct mixer_build state;
- int err;
+ int err = -EINVAL;
const struct usbmix_ctl_map *map;
- void *p;
memset(&state, 0, sizeof(state));
state.chip = mixer->chip;
@@ -2284,44 +2574,108 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
}
}
- p = NULL;
- while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
- mixer->hostif->extralen,
- p, UAC_OUTPUT_TERMINAL)) != NULL) {
- if (mixer->protocol == UAC_VERSION_1) {
- struct uac1_output_terminal_descriptor *desc = p;
-
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
- /* mark terminal ID as visited */
- set_bit(desc->bTerminalID, state.unitbitmap);
- state.oterm.id = desc->bTerminalID;
- state.oterm.type = le16_to_cpu(desc->wTerminalType);
- state.oterm.name = desc->iTerminal;
- err = parse_audio_unit(&state, desc->bSourceID);
+ if (mixer->protocol == UAC_VERSION_3) {
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(mixer->chip->dev,
+ get_iface_desc(mixer->hostif)->bInterfaceNumber);
+ struct usb_interface_assoc_descriptor *assoc =
+ usb_iface->intf_assoc;
+
+ switch (assoc->bFunctionSubClass) {
+ case PROF_GENERIC_IO: {
+ if (assoc->bInterfaceCount == 0x02) {
+ if (get_endpoint(mixer->hostif,
+ 0)->bEndpointAddress | USB_DIR_IN)
+ err = make_out_term(state,
+ UAC_TERMINAL_STREAMING);
+ else
+ err = make_out_term(state,
+ UAC_OUTPUT_TERMINAL_UNDEFINED);
+ } else {
+ err = make_out_term(state,
+ UAC_OUTPUT_TERMINAL_UNDEFINED);
+ if (err < 0 && err != -EINVAL)
+ return err;
+ err = make_out_term(state,
+ UAC_TERMINAL_STREAMING);
+ }
+ break;
+ }
+
+ case PROF_HEADPHONE:
+ err = make_out_term(state,
+ UAC_OUTPUT_TERMINAL_HEADPHONES);
+ break;
+ case PROF_SPEAKER:
+ err = make_out_term(state, UAC_OUTPUT_TERMINAL_SPEAKER);
+ break;
+ case PROF_MICROPHONE:
+ err = make_out_term(state, UAC_TERMINAL_STREAMING);
+ break;
+ case PROF_HEADSET:
+ case PROF_HEADSET_ADAPTER:
+ err = make_out_term(state, UAC_BIDIR_TERMINAL_HEADSET);
if (err < 0 && err != -EINVAL)
return err;
- } else { /* UAC_VERSION_2 */
- struct uac2_output_terminal_descriptor *desc = p;
-
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
- /* mark terminal ID as visited */
- set_bit(desc->bTerminalID, state.unitbitmap);
- state.oterm.id = desc->bTerminalID;
- state.oterm.type = le16_to_cpu(desc->wTerminalType);
- state.oterm.name = desc->iTerminal;
- err = parse_audio_unit(&state, desc->bSourceID);
+ err = make_out_term(state, UAC_TERMINAL_STREAMING);
+ break;
+ case PROF_SPEAKERPHONE:
+ err = make_out_term(state,
+ UAC_BIDIR_TERMINAL_SPEAKERPHONE);
if (err < 0 && err != -EINVAL)
return err;
+ err = make_out_term(state, UAC_TERMINAL_STREAMING);
+ break;
+ }
+ if (err < 0 && err != -EINVAL)
+ return err;
+ } else {
+ void *p;
+
+ p = NULL;
+ while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
+ mixer->hostif->extralen, p,
+ UAC_OUTPUT_TERMINAL)) != NULL) {
+ if (mixer->protocol == UAC_VERSION_1) {
+ struct uac1_output_terminal_descriptor *desc =
+ p;
+
+ if (desc->bLength < sizeof(*desc))
+ continue; /* invalid descriptor? */
+ /* mark terminal ID as visited */
+ set_bit(desc->bTerminalID, state.unitbitmap);
+ state.oterm.id = desc->bTerminalID;
+ state.oterm.type =
+ le16_to_cpu(desc->wTerminalType);
+ state.oterm.name = desc->iTerminal;
+ err = parse_audio_unit(&state, desc->bSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
+ } else { /* UAC_VERSION_2 */
+ struct uac2_output_terminal_descriptor *desc =
+ p;
+
+ if (desc->bLength < sizeof(*desc))
+ continue; /* invalid descriptor? */
+ /* mark terminal ID as visited */
+ set_bit(desc->bTerminalID, state.unitbitmap);
+ state.oterm.id = desc->bTerminalID;
+ state.oterm.type =
+ le16_to_cpu(desc->wTerminalType);
+ state.oterm.name = desc->iTerminal;
+ err = parse_audio_unit(&state, desc->bSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
- /*
- * For UAC2, use the same approach to also add the
- * clock selectors
- */
- err = parse_audio_unit(&state, desc->bCSourceID);
- if (err < 0 && err != -EINVAL)
- return err;
+ /*
+ * For UAC2, use the same approach to also add
+ * the clock selectors
+ */
+ err = parse_audio_unit(&state,
+ desc->bCSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
+ }
}
}
@@ -2572,6 +2926,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
case UAC_VERSION_2:
mixer->protocol = UAC_VERSION_2;
break;
+ case UAC_VERSION_3:
+ mixer->protocol = UAC_VERSION_3;
+ break;
}
if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 24df26df81db..a56eb871784b 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -228,7 +228,7 @@ static int start_endpoints(struct snd_usb_substream *subs)
if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
struct snd_usb_endpoint *ep = subs->data_endpoint;
- dev_dbg(&subs->dev->dev, "Starting data EP @%p\n", ep);
+ dev_dbg(&subs->dev->dev, "Starting data EP @%pK\n", ep);
ep->data_subs = subs;
err = snd_usb_endpoint_start(ep);
@@ -257,7 +257,7 @@ static int start_endpoints(struct snd_usb_substream *subs)
}
}
- dev_dbg(&subs->dev->dev, "Starting sync EP @%p\n", ep);
+ dev_dbg(&subs->dev->dev, "Starting sync EP @%pK\n", ep);
ep->sync_slave = subs->data_endpoint;
err = snd_usb_endpoint_start(ep);
@@ -566,6 +566,64 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
return 0;
}
+int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
+ bool enable)
+{
+ struct audioformat *fmt;
+ struct usb_host_interface *alts;
+ struct usb_interface *iface;
+ int ret;
+
+ if (!enable) {
+ if (subs->interface >= 0) {
+ usb_set_interface(subs->dev, subs->interface, 0);
+ subs->altset_idx = 0;
+ subs->interface = -1;
+ subs->cur_audiofmt = NULL;
+ }
+
+ snd_usb_autosuspend(subs->stream->chip);
+ return 0;
+ }
+
+ snd_usb_autoresume(subs->stream->chip);
+ fmt = find_format(subs);
+ if (!fmt) {
+ dev_err(&subs->dev->dev,
+ "cannot set format: format = %#x, rate = %d, channels = %d\n",
+ subs->pcm_format, subs->cur_rate, subs->channels);
+ return -EINVAL;
+ }
+
+ subs->altset_idx = 0;
+ subs->interface = -1;
+ if (atomic_read(&subs->stream->chip->shutdown)) {
+ ret = -ENODEV;
+ } else {
+ ret = set_format(subs, fmt);
+ if (ret < 0)
+ return ret;
+
+ iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
+ alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
+ ret = snd_usb_init_sample_rate(subs->stream->chip,
+ subs->cur_audiofmt->iface,
+ alts,
+ subs->cur_audiofmt,
+ subs->cur_rate);
+ if (ret < 0) {
+ dev_err(&subs->dev->dev, "failed to set rate %d\n",
+ subs->cur_rate);
+ return ret;
+ }
+ }
+
+ subs->interface = fmt->iface;
+ subs->altset_idx = fmt->altset_idx;
+
+ return 0;
+}
+
/*
* Return the score of matching two audioformats.
* Veto the audioformat if:
@@ -583,13 +641,13 @@ static int match_endpoint_audioformats(struct snd_usb_substream *subs,
if (fp->channels < 1) {
dev_dbg(&subs->dev->dev,
- "%s: (fmt @%p) no channels\n", __func__, fp);
+ "%s: (fmt @%pK) no channels\n", __func__, fp);
return 0;
}
if (!(fp->formats & pcm_format_to_bits(pcm_format))) {
dev_dbg(&subs->dev->dev,
- "%s: (fmt @%p) no match for format %d\n", __func__,
+ "%s: (fmt @%pK) no match for format %d\n", __func__,
fp, pcm_format);
return 0;
}
@@ -602,7 +660,7 @@ static int match_endpoint_audioformats(struct snd_usb_substream *subs,
}
if (!score) {
dev_dbg(&subs->dev->dev,
- "%s: (fmt @%p) no match for rate %d\n", __func__,
+ "%s: (fmt @%pK) no match for rate %d\n", __func__,
fp, rate);
return 0;
}
@@ -611,7 +669,7 @@ static int match_endpoint_audioformats(struct snd_usb_substream *subs,
score++;
dev_dbg(&subs->dev->dev,
- "%s: (fmt @%p) score %d\n", __func__, fp, score);
+ "%s: (fmt @%pK) score %d\n", __func__, fp, score);
return score;
}
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h
index df7a003682ad..d581f94b3fbf 100644
--- a/sound/usb/pcm.h
+++ b/sound/usb/pcm.h
@@ -9,6 +9,7 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt);
-
+int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
+ bool enable);
#endif /* __USBAUDIO_PCM_H */
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index bc8e4702e9ed..f1186ba3958c 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -20,6 +20,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -69,9 +70,14 @@ static void snd_usb_audio_stream_free(struct snd_usb_stream *stream)
static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
{
struct snd_usb_stream *stream = pcm->private_data;
+ struct snd_usb_audio *chip;
+
if (stream) {
+ mutex_lock(&stream->chip->dev_lock);
+ chip = stream->chip;
stream->pcm = NULL;
snd_usb_audio_stream_free(stream);
+ mutex_unlock(&chip->dev_lock);
}
}
@@ -280,8 +286,6 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
0 /* terminator */
};
struct snd_pcm_chmap_elem *chmap;
- const unsigned int *maps;
- int c;
if (channels > ARRAY_SIZE(chmap->map))
return NULL;
@@ -290,27 +294,42 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
if (!chmap)
return NULL;
- maps = protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps;
chmap->channels = channels;
- c = 0;
- if (bits) {
- for (; bits && *maps; maps++, bits >>= 1)
- if (bits & 1)
- chmap->map[c++] = *maps;
+ if (protocol == UAC_VERSION_3) {
+ switch (channels) {
+ case 1:
+ chmap->map[0] = SNDRV_CHMAP_MONO;
+ break;
+ case 2:
+ chmap->map[0] = SNDRV_CHMAP_FL;
+ chmap->map[1] = SNDRV_CHMAP_FR;
+ break;
+ }
} else {
- /* If we're missing wChannelConfig, then guess something
- to make sure the channel map is not skipped entirely */
- if (channels == 1)
- chmap->map[c++] = SNDRV_CHMAP_MONO;
- else
- for (; c < channels && *maps; maps++)
- chmap->map[c++] = *maps;
+ int c = 0;
+ const unsigned int *maps =
+ protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps;
+
+ if (bits) {
+ for (; bits && *maps; maps++, bits >>= 1)
+ if (bits & 1)
+ chmap->map[c++] = *maps;
+ } else {
+ /*
+ * If we're missing wChannelConfig, then guess something
+ * to make sure the channel map is not skipped entirely
+ */
+ if (channels == 1)
+ chmap->map[c++] = SNDRV_CHMAP_MONO;
+ else
+ for (; c < channels && *maps; maps++)
+ chmap->map[c++] = *maps;
+ }
+ for (; c < channels; c++)
+ chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
}
- for (; c < channels; c++)
- chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
-
return chmap;
}
@@ -407,6 +426,9 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
struct usb_interface_descriptor *altsd = get_iface_desc(alts);
int attributes = 0;
+ if (protocol == UAC_VERSION_3)
+ return 0;
+
csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
/* Creamware Noah has this descriptor after the 2nd endpoint */
@@ -627,6 +649,50 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
iface_no, altno, as->bTerminalLink);
continue;
}
+
+ case UAC_VERSION_3: {
+ int wMaxPacketSize;
+
+ /*
+ * Allocate a dummy instance of fmt and set format type
+ * to UAC_FORMAT_TYPE_I for BADD support; free fmt
+ * after its last usage
+ */
+ fmt = kzalloc(sizeof(*fmt), GFP_KERNEL);
+ if (!fmt)
+ return -ENOMEM;
+
+ fmt->bFormatType = UAC_FORMAT_TYPE_I;
+ format = UAC_FORMAT_TYPE_I_PCM;
+ clock = BADD_CLOCK_SOURCE;
+ wMaxPacketSize = le16_to_cpu(get_endpoint(alts, 0)
+ ->wMaxPacketSize);
+ switch (wMaxPacketSize) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_24: {
+ num_channels = NUM_CHANNELS_MONO;
+ chconfig = BADD_CH_CONFIG_MONO;
+ goto populate_fp;
+ }
+
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24: {
+ num_channels = NUM_CHANNELS_STEREO;
+ chconfig = BADD_CH_CONFIG_STEREO;
+ goto populate_fp;
+ }
+ default:
+ dev_err(&dev->dev,
+ "%u:%d: invalid wMaxPacketSize\n",
+ iface_no, altno);
+ kfree(fmt);
+ continue;
+ }
+ }
}
/* get format type */
@@ -660,6 +726,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
fp->maxpacksize * 2)
continue;
+populate_fp:
fp = kzalloc(sizeof(*fp), GFP_KERNEL);
if (! fp) {
dev_err(&dev->dev, "cannot malloc\n");
@@ -721,6 +788,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
continue;
}
+ if (protocol == UAC_VERSION_3)
+ kfree(fmt);
/* Create chmap */
if (fp->channels != num_channels)
chconfig = 0;
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
new file mode 100644
index 000000000000..dc66b6016f8d
--- /dev/null
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -0,0 +1,1357 @@
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/uaccess.h>
+#include <sound/pcm.h>
+#include <sound/core.h>
+#include <sound/asound.h>
+#include <linux/usb.h>
+#include <linux/qmi_encdec.h>
+#include <soc/qcom/msm_qmi_interface.h>
+#include <linux/iommu.h>
+#include <linux/qcom_iommu.h>
+#include <linux/platform_device.h>
+#include <linux/usb/audio-v3.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "helper.h"
+#include "pcm.h"
+#include "usb_audio_qmi_v01.h"
+
+#define SND_PCM_CARD_NUM_MASK 0xffff0000
+#define SND_PCM_DEV_NUM_MASK 0xff00
+#define SND_PCM_STREAM_DIRECTION 0xff
+
+#define PREPEND_SID_TO_IOVA(iova, sid) (u64)(((u64)(iova)) | \
+ (((u64)sid) << 32))
+
+/* event ring iova base address */
+#define IOVA_BASE 0x1000
+
+#define IOVA_DCBA_BASE 0x2000
+#define IOVA_XFER_RING_BASE (IOVA_DCBA_BASE + PAGE_SIZE * (SNDRV_CARDS + 1))
+#define IOVA_XFER_BUF_BASE (IOVA_XFER_RING_BASE + PAGE_SIZE * SNDRV_CARDS * 32)
+#define IOVA_XFER_RING_MAX (IOVA_XFER_BUF_BASE - PAGE_SIZE)
+#define IOVA_XFER_BUF_MAX (0xfffff000 - PAGE_SIZE)
+
+#define MAX_XFER_BUFF_LEN (24 * PAGE_SIZE)
+
+struct iova_info {
+ struct list_head list;
+ unsigned long start_iova;
+ size_t size;
+ bool in_use;
+};
+
+struct intf_info {
+ unsigned long data_xfer_ring_va;
+ size_t data_xfer_ring_size;
+ unsigned long sync_xfer_ring_va;
+ size_t sync_xfer_ring_size;
+ unsigned long xfer_buf_va;
+ size_t xfer_buf_size;
+ phys_addr_t xfer_buf_pa;
+ u8 *xfer_buf;
+ u8 intf_num;
+ u8 pcm_card_num;
+ u8 pcm_dev_num;
+ u8 direction;
+ bool in_use;
+};
+
+struct uaudio_dev {
+ struct usb_device *udev;
+ /* audio control interface */
+ struct usb_host_interface *ctrl_intf;
+ unsigned int card_num;
+ atomic_t in_use;
+ struct kref kref;
+ unsigned long dcba_iova;
+ size_t dcba_size;
+ wait_queue_head_t disconnect_wq;
+
+ /* interface specific */
+ int num_intf;
+ struct intf_info *info;
+};
+
+static struct uaudio_dev uadev[SNDRV_CARDS];
+
+struct uaudio_qmi_dev {
+ struct device *dev;
+ u32 sid;
+ u32 intr_num;
+ struct iommu_domain *domain;
+
+ /* list to keep track of available iova */
+ struct list_head dcba_list;
+ size_t dcba_iova_size;
+ unsigned long curr_dcba_iova;
+ struct list_head xfer_ring_list;
+ size_t xfer_ring_iova_size;
+ unsigned long curr_xfer_ring_iova;
+ struct list_head xfer_buf_list;
+ size_t xfer_buf_iova_size;
+ unsigned long curr_xfer_buf_iova;
+ /* bit fields representing pcm card enabled */
+ unsigned long card_slot;
+ /* cache event ring phys addr */
+ u64 er_phys_addr;
+};
+
+static struct uaudio_qmi_dev *uaudio_qdev;
+
+struct uaudio_qmi_svc {
+ struct qmi_handle *uaudio_svc_hdl;
+ void *curr_conn;
+ struct work_struct recv_msg_work;
+ struct work_struct qmi_disconnect_work;
+ struct workqueue_struct *uaudio_wq;
+ ktime_t t_request_recvd;
+ ktime_t t_resp_sent;
+};
+
+static struct uaudio_qmi_svc *uaudio_svc;
+
+static struct msg_desc uaudio_stream_req_desc = {
+ .max_msg_len = QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN,
+ .msg_id = QMI_UAUDIO_STREAM_REQ_V01,
+ .ei_array = qmi_uaudio_stream_req_msg_v01_ei,
+};
+
+static struct msg_desc uaudio_stream_resp_desc = {
+ .max_msg_len = QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN,
+ .msg_id = QMI_UAUDIO_STREAM_RESP_V01,
+ .ei_array = qmi_uaudio_stream_resp_msg_v01_ei,
+};
+
+static struct msg_desc uaudio_stream_ind_desc = {
+ .max_msg_len = QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN,
+ .msg_id = QMI_UADUIO_STREAM_IND_V01,
+ .ei_array = qmi_uaudio_stream_ind_msg_v01_ei,
+};
+
+enum mem_type {
+ MEM_EVENT_RING,
+ MEM_DCBA,
+ MEM_XFER_RING,
+ MEM_XFER_BUF,
+};
+
+enum usb_qmi_audio_format {
+ USB_QMI_PCM_FORMAT_S8 = 0,
+ USB_QMI_PCM_FORMAT_U8,
+ USB_QMI_PCM_FORMAT_S16_LE,
+ USB_QMI_PCM_FORMAT_S16_BE,
+ USB_QMI_PCM_FORMAT_U16_LE,
+ USB_QMI_PCM_FORMAT_U16_BE,
+ USB_QMI_PCM_FORMAT_S24_LE,
+ USB_QMI_PCM_FORMAT_S24_BE,
+ USB_QMI_PCM_FORMAT_U24_LE,
+ USB_QMI_PCM_FORMAT_U24_BE,
+ USB_QMI_PCM_FORMAT_S24_3LE,
+ USB_QMI_PCM_FORMAT_S24_3BE,
+ USB_QMI_PCM_FORMAT_U24_3LE,
+ USB_QMI_PCM_FORMAT_U24_3BE,
+ USB_QMI_PCM_FORMAT_S32_LE,
+ USB_QMI_PCM_FORMAT_S32_BE,
+ USB_QMI_PCM_FORMAT_U32_LE,
+ USB_QMI_PCM_FORMAT_U32_BE,
+};
+
+static unsigned long uaudio_get_iova(unsigned long *curr_iova,
+ size_t *curr_iova_size, struct list_head *head, size_t size)
+{
+ struct iova_info *info, *new_info = NULL;
+ struct list_head *curr_head;
+ unsigned long va = 0;
+ size_t tmp_size = size;
+ bool found = false;
+
+ if (size % PAGE_SIZE) {
+ pr_err("%s: size %zu is not page size multiple\n", __func__,
+ size);
+ goto done;
+ }
+
+ if (size > *curr_iova_size) {
+ pr_err("%s: size %zu > curr size %zu\n", __func__, size,
+ *curr_iova_size);
+ goto done;
+ }
+ if (*curr_iova_size == 0) {
+ pr_err("%s: iova mapping is full\n", __func__);
+ goto done;
+ }
+
+ list_for_each_entry(info, head, list) {
+ /* exact size iova_info */
+ if (!info->in_use && info->size == size) {
+ info->in_use = true;
+ va = info->start_iova;
+ *curr_iova_size -= size;
+ found = true;
+ pr_debug("%s: exact size :%zu found\n", __func__, size);
+ goto done;
+ } else if (!info->in_use && tmp_size >= info->size) {
+ if (!new_info)
+ new_info = info;
+ pr_debug("%s: partial size: %zu found\n", __func__,
+ info->size);
+ tmp_size -= info->size;
+ if (tmp_size)
+ continue;
+
+ va = new_info->start_iova;
+ for (curr_head = &new_info->list; curr_head !=
+ &info->list; curr_head = curr_head->next) {
+ new_info = list_entry(curr_head, struct
+ iova_info, list);
+ new_info->in_use = true;
+ }
+ info->in_use = true;
+ *curr_iova_size -= size;
+ found = true;
+ goto done;
+ } else {
+ /* iova region in use */
+ new_info = NULL;
+ tmp_size = size;
+ }
+ }
+
+ info = kzalloc(sizeof(struct iova_info), GFP_KERNEL);
+ if (!info) {
+ va = 0;
+ goto done;
+ }
+
+ va = info->start_iova = *curr_iova;
+ info->size = size;
+ info->in_use = true;
+ *curr_iova += size;
+ *curr_iova_size -= size;
+ found = true;
+ list_add_tail(&info->list, head);
+
+done:
+ if (!found)
+ pr_err("%s: unable to find %zu size iova\n", __func__, size);
+ else
+ pr_debug("%s: va:%lu curr_iova:%lu curr_iova_size:%zu\n",
+ __func__, va, *curr_iova, *curr_iova_size);
+
+ return va;
+}
+
+static unsigned long uaudio_iommu_map(enum mem_type mtype, phys_addr_t pa,
+ size_t size)
+{
+ unsigned long va = 0;
+ bool map = true;
+ int ret;
+
+ switch (mtype) {
+ case MEM_EVENT_RING:
+ va = IOVA_BASE;
+ /* er already mapped */
+ if (uaudio_qdev->er_phys_addr == pa)
+ map = false;
+ break;
+ case MEM_DCBA:
+ va = uaudio_get_iova(&uaudio_qdev->curr_dcba_iova,
+ &uaudio_qdev->dcba_iova_size, &uaudio_qdev->dcba_list, size);
+ break;
+ case MEM_XFER_RING:
+ va = uaudio_get_iova(&uaudio_qdev->curr_xfer_ring_iova,
+ &uaudio_qdev->xfer_ring_iova_size, &uaudio_qdev->xfer_ring_list,
+ size);
+ break;
+ case MEM_XFER_BUF:
+ va = uaudio_get_iova(&uaudio_qdev->curr_xfer_buf_iova,
+ &uaudio_qdev->xfer_buf_iova_size, &uaudio_qdev->xfer_buf_list,
+ size);
+ break;
+ default:
+ pr_err("%s: unknown mem type %d\n", __func__, mtype);
+ }
+
+ if (!va)
+ map = false;
+
+ if (!map)
+ goto done;
+
+ pr_debug("%s: map pa %pa to iova %lu for memtype %d\n", __func__, &pa,
+ va, mtype);
+ ret = iommu_map(uaudio_qdev->domain, va, pa, size,
+ IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE);
+ if (ret)
+ pr_err("%s:failed to map pa:%pa iova:%lu memtype:%d ret:%d\n",
+ __func__, &pa, va, mtype, ret);
+done:
+ return va;
+}
+
+static void uaudio_put_iova(unsigned long va, size_t size, struct list_head
+ *head, size_t *curr_iova_size)
+{
+ struct iova_info *info;
+ size_t tmp_size = size;
+ bool found = false;
+
+ list_for_each_entry(info, head, list) {
+ if (info->start_iova == va) {
+ if (!info->in_use) {
+ pr_err("%s: va %lu is not in use\n", __func__,
+ va);
+ return;
+ }
+ found = true;
+ info->in_use = false;
+ if (info->size == size)
+ goto done;
+ }
+
+ if (found && tmp_size >= info->size) {
+ info->in_use = false;
+ tmp_size -= info->size;
+ if (!tmp_size)
+ goto done;
+ }
+ }
+
+ if (!found) {
+ pr_err("%s: unable to find the va %lu\n", __func__, va);
+ return;
+ }
+done:
+ *curr_iova_size += size;
+ pr_debug("%s: curr_iova_size %zu\n", __func__, *curr_iova_size);
+}
+
+static void uaudio_iommu_unmap(enum mem_type mtype, unsigned long va,
+ size_t size)
+{
+ size_t umap_size;
+ bool unmap = true;
+
+ if (!va || !size)
+ return;
+
+ switch (mtype) {
+ case MEM_EVENT_RING:
+ if (uaudio_qdev->er_phys_addr)
+ uaudio_qdev->er_phys_addr = 0;
+ else
+ unmap = false;
+ break;
+ case MEM_DCBA:
+ uaudio_put_iova(va, size, &uaudio_qdev->dcba_list,
+ &uaudio_qdev->dcba_iova_size);
+ break;
+ case MEM_XFER_RING:
+ uaudio_put_iova(va, size, &uaudio_qdev->xfer_ring_list,
+ &uaudio_qdev->xfer_ring_iova_size);
+ break;
+ case MEM_XFER_BUF:
+ uaudio_put_iova(va, size, &uaudio_qdev->xfer_buf_list,
+ &uaudio_qdev->xfer_buf_iova_size);
+ break;
+ default:
+ pr_err("%s: unknown mem type %d\n", __func__, mtype);
+ unmap = false;
+ }
+
+ if (!unmap)
+ return;
+
+ pr_debug("%s: unmap iova %lu for memtype %d\n", __func__, va, mtype);
+
+ umap_size = iommu_unmap(uaudio_qdev->domain, va, size);
+ if (umap_size != size)
+ pr_err("%s: unmapped size %zu for iova %lu\n", __func__,
+ umap_size, va);
+}
+
+static int prepare_qmi_response(struct snd_usb_substream *subs,
+ struct qmi_uaudio_stream_req_msg_v01 *req_msg,
+ struct qmi_uaudio_stream_resp_msg_v01 *resp, int info_idx)
+{
+ struct usb_interface *iface;
+ struct usb_host_interface *alts;
+ struct usb_interface_descriptor *altsd;
+ struct usb_host_endpoint *ep;
+ struct uac_format_type_i_continuous_descriptor *fmt;
+ struct uac_format_type_i_discrete_descriptor *fmt_v1;
+ struct uac_format_type_i_ext_descriptor *fmt_v2;
+ struct uac1_as_header_descriptor *as;
+ int ret = -ENODEV;
+ int protocol, card_num, pcm_dev_num;
+ void *hdr_ptr;
+ u8 *xfer_buf;
+ u32 len, mult, remainder, xfer_buf_len;
+ unsigned long va, tr_data_va = 0, tr_sync_va = 0, dcba_va = 0,
+ xfer_buf_va = 0;
+ phys_addr_t xhci_pa, xfer_buf_pa;
+
+ iface = usb_ifnum_to_if(subs->dev, subs->interface);
+ if (!iface) {
+ pr_err("%s: interface # %d does not exist\n", __func__,
+ subs->interface);
+ goto err;
+ }
+
+ pcm_dev_num = (req_msg->usb_token & SND_PCM_DEV_NUM_MASK) >> 8;
+ card_num = (req_msg->usb_token & SND_PCM_CARD_NUM_MASK) >> 16;
+ xfer_buf_len = req_msg->xfer_buff_size;
+
+ alts = &iface->altsetting[subs->altset_idx];
+ altsd = get_iface_desc(alts);
+ protocol = altsd->bInterfaceProtocol;
+
+ /* get format type */
+ if (protocol != UAC_VERSION_3) {
+ fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
+ UAC_FORMAT_TYPE);
+ if (!fmt) {
+ pr_err("%s: %u:%d : no UAC_FORMAT_TYPE desc\n",
+ __func__, subs->interface, subs->altset_idx);
+ goto err;
+ }
+ }
+
+ if (!uadev[card_num].ctrl_intf) {
+ pr_err("%s: audio ctrl intf info not cached\n", __func__);
+ goto err;
+ }
+
+ if (protocol != UAC_VERSION_3) {
+ hdr_ptr = snd_usb_find_csint_desc(
+ uadev[card_num].ctrl_intf->extra,
+ uadev[card_num].ctrl_intf->extralen,
+ NULL, UAC_HEADER);
+ if (!hdr_ptr) {
+ pr_err("%s: no UAC_HEADER desc\n", __func__);
+ goto err;
+ }
+ }
+
+ if (protocol == UAC_VERSION_1) {
+ as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
+ UAC_AS_GENERAL);
+ if (!as) {
+ pr_err("%s: %u:%d : no UAC_AS_GENERAL desc\n", __func__,
+ subs->interface, subs->altset_idx);
+ goto err;
+ }
+ resp->data_path_delay = as->bDelay;
+ resp->data_path_delay_valid = 1;
+ fmt_v1 = (struct uac_format_type_i_discrete_descriptor *)fmt;
+ resp->usb_audio_subslot_size = fmt_v1->bSubframeSize;
+ resp->usb_audio_subslot_size_valid = 1;
+
+ resp->usb_audio_spec_revision =
+ ((struct uac1_ac_header_descriptor *)hdr_ptr)->bcdADC;
+ resp->usb_audio_spec_revision_valid = 1;
+ } else if (protocol == UAC_VERSION_2) {
+ fmt_v2 = (struct uac_format_type_i_ext_descriptor *)fmt;
+ resp->usb_audio_subslot_size = fmt_v2->bSubslotSize;
+ resp->usb_audio_subslot_size_valid = 1;
+
+ resp->usb_audio_spec_revision =
+ ((struct uac2_ac_header_descriptor *)hdr_ptr)->bcdADC;
+ resp->usb_audio_spec_revision_valid = 1;
+ } else if (protocol == UAC_VERSION_3) {
+ switch (le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize)) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16: {
+ resp->usb_audio_subslot_size = SUBSLOTSIZE_16_BIT;
+ break;
+ }
+
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24: {
+ resp->usb_audio_subslot_size = SUBSLOTSIZE_24_BIT;
+ break;
+ }
+
+ default:
+ pr_err("%d: %u: Invalid wMaxPacketSize\n",
+ subs->interface, subs->altset_idx);
+ ret = -EINVAL;
+ goto err;
+ }
+ resp->usb_audio_subslot_size_valid = 1;
+ } else {
+ pr_err("%s: unknown protocol version %x\n", __func__, protocol);
+ goto err;
+ }
+
+ resp->slot_id = subs->dev->slot_id;
+ resp->slot_id_valid = 1;
+
+ memcpy(&resp->std_as_opr_intf_desc, &alts->desc, sizeof(alts->desc));
+ resp->std_as_opr_intf_desc_valid = 1;
+
+ ep = usb_pipe_endpoint(subs->dev, subs->data_endpoint->pipe);
+ if (!ep) {
+ pr_err("%s: data ep # %d context is null\n", __func__,
+ subs->data_endpoint->ep_num);
+ goto err;
+ }
+ memcpy(&resp->std_as_data_ep_desc, &ep->desc, sizeof(ep->desc));
+ resp->std_as_data_ep_desc_valid = 1;
+
+ xhci_pa = usb_get_xfer_ring_dma_addr(subs->dev, ep);
+ if (!xhci_pa) {
+ pr_err("%s:failed to get data ep ring dma address\n", __func__);
+ goto err;
+ }
+
+ resp->xhci_mem_info.tr_data.pa = xhci_pa;
+
+ if (subs->sync_endpoint) {
+ ep = usb_pipe_endpoint(subs->dev, subs->sync_endpoint->pipe);
+ if (!ep) {
+ pr_debug("%s: implicit fb on data ep\n", __func__);
+ goto skip_sync_ep;
+ }
+ memcpy(&resp->std_as_sync_ep_desc, &ep->desc, sizeof(ep->desc));
+ resp->std_as_sync_ep_desc_valid = 1;
+
+ xhci_pa = usb_get_xfer_ring_dma_addr(subs->dev, ep);
+ if (!xhci_pa) {
+ pr_err("%s:failed to get sync ep ring dma address\n",
+ __func__);
+ goto err;
+ }
+ resp->xhci_mem_info.tr_sync.pa = xhci_pa;
+ }
+
+skip_sync_ep:
+ resp->interrupter_num = uaudio_qdev->intr_num;
+ resp->interrupter_num_valid = 1;
+
+ /* map xhci data structures PA memory to iova */
+
+ /* event ring */
+ ret = usb_sec_event_ring_setup(subs->dev, resp->interrupter_num);
+ if (ret) {
+ pr_err("%s: failed to setup sec event ring ret %d\n", __func__,
+ ret);
+ goto err;
+ }
+ xhci_pa = usb_get_sec_event_ring_dma_addr(subs->dev,
+ resp->interrupter_num);
+ if (!xhci_pa) {
+ pr_err("%s: failed to get sec event ring dma address\n",
+ __func__);
+ goto err;
+ }
+
+ va = uaudio_iommu_map(MEM_EVENT_RING, xhci_pa, PAGE_SIZE);
+ if (!va)
+ goto err;
+
+ resp->xhci_mem_info.evt_ring.va = PREPEND_SID_TO_IOVA(va,
+ uaudio_qdev->sid);
+ resp->xhci_mem_info.evt_ring.pa = xhci_pa;
+ resp->xhci_mem_info.evt_ring.size = PAGE_SIZE;
+ uaudio_qdev->er_phys_addr = xhci_pa;
+
+ /* dcba */
+ xhci_pa = usb_get_dcba_dma_addr(subs->dev);
+ if (!xhci_pa) {
+ pr_err("%s:failed to get dcba dma address\n", __func__);
+ goto unmap_er;
+ }
+
+ if (!uadev[card_num].dcba_iova) { /* mappped per usb device */
+ va = uaudio_iommu_map(MEM_DCBA, xhci_pa, PAGE_SIZE);
+ if (!va)
+ goto unmap_er;
+
+ uadev[card_num].dcba_iova = va;
+ uadev[card_num].dcba_size = PAGE_SIZE;
+ }
+
+ dcba_va = uadev[card_num].dcba_iova;
+ resp->xhci_mem_info.dcba.va = PREPEND_SID_TO_IOVA(dcba_va,
+ uaudio_qdev->sid);
+ resp->xhci_mem_info.dcba.pa = xhci_pa;
+ resp->xhci_mem_info.dcba.size = PAGE_SIZE;
+
+ /* data transfer ring */
+ xhci_pa = resp->xhci_mem_info.tr_data.pa;
+ va = uaudio_iommu_map(MEM_XFER_RING, xhci_pa, PAGE_SIZE);
+ if (!va)
+ goto unmap_dcba;
+
+ tr_data_va = va;
+ resp->xhci_mem_info.tr_data.va = PREPEND_SID_TO_IOVA(va,
+ uaudio_qdev->sid);
+ resp->xhci_mem_info.tr_data.size = PAGE_SIZE;
+
+ /* sync transfer ring */
+ if (!resp->xhci_mem_info.tr_sync.pa)
+ goto skip_sync;
+
+ xhci_pa = resp->xhci_mem_info.tr_sync.pa;
+ va = uaudio_iommu_map(MEM_XFER_RING, xhci_pa, PAGE_SIZE);
+ if (!va)
+ goto unmap_data;
+
+ tr_sync_va = va;
+ resp->xhci_mem_info.tr_sync.va = PREPEND_SID_TO_IOVA(va,
+ uaudio_qdev->sid);
+ resp->xhci_mem_info.tr_sync.size = PAGE_SIZE;
+
+skip_sync:
+ /* xfer buffer, multiple of 4K only */
+ if (!xfer_buf_len)
+ xfer_buf_len = PAGE_SIZE;
+
+ mult = xfer_buf_len / PAGE_SIZE;
+ remainder = xfer_buf_len % PAGE_SIZE;
+ len = mult * PAGE_SIZE;
+ len += remainder ? PAGE_SIZE : 0;
+
+ if (len > MAX_XFER_BUFF_LEN) {
+ pr_err("%s: req buf len %d > max buf len %lu, setting %lu\n",
+ __func__, len, MAX_XFER_BUFF_LEN, MAX_XFER_BUFF_LEN);
+ len = MAX_XFER_BUFF_LEN;
+ }
+
+ xfer_buf = usb_alloc_coherent(subs->dev, len, GFP_KERNEL, &xfer_buf_pa);
+ if (!xfer_buf)
+ goto unmap_sync;
+
+ resp->xhci_mem_info.xfer_buff.pa = xfer_buf_pa;
+ resp->xhci_mem_info.xfer_buff.size = len;
+
+ va = uaudio_iommu_map(MEM_XFER_BUF, xfer_buf_pa, len);
+ if (!va)
+ goto unmap_sync;
+
+ xfer_buf_va = va;
+ resp->xhci_mem_info.xfer_buff.va = PREPEND_SID_TO_IOVA(va,
+ uaudio_qdev->sid);
+
+ resp->xhci_mem_info_valid = 1;
+
+ if (!atomic_read(&uadev[card_num].in_use)) {
+ kref_init(&uadev[card_num].kref);
+ init_waitqueue_head(&uadev[card_num].disconnect_wq);
+ uadev[card_num].num_intf =
+ subs->dev->config->desc.bNumInterfaces;
+ uadev[card_num].info =
+ kzalloc(sizeof(struct intf_info) *
+ uadev[card_num].num_intf, GFP_KERNEL);
+ if (!uadev[card_num].info) {
+ ret = -ENOMEM;
+ goto unmap_xfer_buf;
+ }
+ uadev[card_num].udev = subs->dev;
+ atomic_set(&uadev[card_num].in_use, 1);
+ } else {
+ kref_get(&uadev[card_num].kref);
+ }
+
+ uadev[card_num].card_num = card_num;
+
+ /* cache intf specific info to use it for unmap and free xfer buf */
+ uadev[card_num].info[info_idx].data_xfer_ring_va = tr_data_va;
+ uadev[card_num].info[info_idx].data_xfer_ring_size = PAGE_SIZE;
+ uadev[card_num].info[info_idx].sync_xfer_ring_va = tr_sync_va;
+ uadev[card_num].info[info_idx].sync_xfer_ring_size = PAGE_SIZE;
+ uadev[card_num].info[info_idx].xfer_buf_va = xfer_buf_va;
+ uadev[card_num].info[info_idx].xfer_buf_pa = xfer_buf_pa;
+ uadev[card_num].info[info_idx].xfer_buf_size = len;
+ uadev[card_num].info[info_idx].xfer_buf = xfer_buf;
+ uadev[card_num].info[info_idx].pcm_card_num = card_num;
+ uadev[card_num].info[info_idx].pcm_dev_num = pcm_dev_num;
+ uadev[card_num].info[info_idx].direction = subs->direction;
+ uadev[card_num].info[info_idx].intf_num = subs->interface;
+ uadev[card_num].info[info_idx].in_use = true;
+
+ set_bit(card_num, &uaudio_qdev->card_slot);
+
+ return 0;
+
+unmap_xfer_buf:
+ uaudio_iommu_unmap(MEM_XFER_BUF, xfer_buf_va, len);
+unmap_sync:
+ usb_free_coherent(subs->dev, len, xfer_buf, xfer_buf_pa);
+ uaudio_iommu_unmap(MEM_XFER_RING, tr_sync_va, PAGE_SIZE);
+unmap_data:
+ uaudio_iommu_unmap(MEM_XFER_RING, tr_data_va, PAGE_SIZE);
+unmap_dcba:
+ uaudio_iommu_unmap(MEM_DCBA, dcba_va, PAGE_SIZE);
+unmap_er:
+ uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE);
+err:
+ return ret;
+}
+
+static void uaudio_dev_intf_cleanup(struct usb_device *udev,
+ struct intf_info *info)
+{
+ uaudio_iommu_unmap(MEM_XFER_RING, info->data_xfer_ring_va,
+ info->data_xfer_ring_size);
+ info->data_xfer_ring_va = 0;
+ info->data_xfer_ring_size = 0;
+
+ uaudio_iommu_unmap(MEM_XFER_RING, info->sync_xfer_ring_va,
+ info->sync_xfer_ring_size);
+ info->sync_xfer_ring_va = 0;
+ info->sync_xfer_ring_size = 0;
+
+ uaudio_iommu_unmap(MEM_XFER_BUF, info->xfer_buf_va,
+ info->xfer_buf_size);
+ info->xfer_buf_va = 0;
+
+ usb_free_coherent(udev, info->xfer_buf_size,
+ info->xfer_buf, info->xfer_buf_pa);
+ info->xfer_buf_size = 0;
+ info->xfer_buf = NULL;
+ info->xfer_buf_pa = 0;
+
+ info->in_use = false;
+}
+
+static void uaudio_dev_cleanup(struct uaudio_dev *dev)
+{
+ int if_idx;
+
+ /* free xfer buffer and unmap xfer ring and buf per interface */
+ for (if_idx = 0; if_idx < dev->num_intf; if_idx++) {
+ if (!dev->info[if_idx].in_use)
+ continue;
+ uaudio_dev_intf_cleanup(dev->udev, &dev->info[if_idx]);
+ pr_debug("%s: release resources: intf# %d card# %d\n", __func__,
+ dev->info[if_idx].intf_num, dev->card_num);
+ }
+
+ /* iommu_unmap dcba iova for a usb device */
+ uaudio_iommu_unmap(MEM_DCBA, dev->dcba_iova, dev->dcba_size);
+
+ dev->dcba_iova = 0;
+ dev->dcba_size = 0;
+ dev->num_intf = 0;
+
+ /* free interface info */
+ kfree(dev->info);
+ dev->info = NULL;
+
+ clear_bit(dev->card_num, &uaudio_qdev->card_slot);
+
+ /* all audio devices are disconnected */
+ if (!uaudio_qdev->card_slot) {
+ uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE);
+ usb_sec_event_ring_cleanup(dev->udev, uaudio_qdev->intr_num);
+ pr_debug("%s: all audio devices disconnected\n", __func__);
+ }
+
+ dev->udev = NULL;
+}
+
+static void uaudio_disconnect_cb(struct snd_usb_audio *chip)
+{
+ int ret;
+ struct uaudio_dev *dev;
+ int card_num = chip->card_num;
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+ struct qmi_uaudio_stream_ind_msg_v01 disconnect_ind = {0};
+
+ pr_debug("%s: for card# %d\n", __func__, card_num);
+
+ if (card_num >= SNDRV_CARDS) {
+ pr_err("%s: invalid card number\n", __func__);
+ return;
+ }
+
+ mutex_lock(&chip->dev_lock);
+ dev = &uadev[card_num];
+
+ /* clean up */
+ if (!dev->udev) {
+ pr_debug("%s: no clean up required\n", __func__);
+ goto done;
+ }
+
+ if (atomic_read(&dev->in_use)) {
+ mutex_unlock(&chip->dev_lock);
+
+ pr_debug("%s: sending qmi indication disconnect\n", __func__);
+ disconnect_ind.dev_event = USB_AUDIO_DEV_DISCONNECT_V01;
+ disconnect_ind.slot_id = dev->udev->slot_id;
+ ret = qmi_send_ind(svc->uaudio_svc_hdl, svc->curr_conn,
+ &uaudio_stream_ind_desc, &disconnect_ind,
+ sizeof(disconnect_ind));
+ if (ret < 0) {
+ pr_err("%s: qmi send failed wiht err: %d\n",
+ __func__, ret);
+ return;
+ }
+
+ ret = wait_event_interruptible(dev->disconnect_wq,
+ !atomic_read(&dev->in_use));
+ if (ret < 0) {
+ pr_debug("%s: failed with ret %d\n", __func__, ret);
+ return;
+ }
+ mutex_lock(&chip->dev_lock);
+ }
+
+ uaudio_dev_cleanup(dev);
+done:
+ mutex_unlock(&chip->dev_lock);
+}
+
+static void uaudio_dev_release(struct kref *kref)
+{
+ struct uaudio_dev *dev = container_of(kref, struct uaudio_dev, kref);
+
+ pr_debug("%s for dev %pK\n", __func__, dev);
+
+ atomic_set(&dev->in_use, 0);
+
+ clear_bit(dev->card_num, &uaudio_qdev->card_slot);
+
+ /* all audio devices are disconnected */
+ if (!uaudio_qdev->card_slot) {
+ usb_sec_event_ring_cleanup(dev->udev, uaudio_qdev->intr_num);
+ uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE);
+ pr_debug("%s: all audio devices disconnected\n", __func__);
+ }
+
+ wake_up(&dev->disconnect_wq);
+}
+
+/* maps audio format received over QMI to asound.h based pcm format */
+static int map_pcm_format(unsigned int fmt_received)
+{
+ switch (fmt_received) {
+ case USB_QMI_PCM_FORMAT_S8:
+ return SNDRV_PCM_FORMAT_S8;
+ case USB_QMI_PCM_FORMAT_U8:
+ return SNDRV_PCM_FORMAT_U8;
+ case USB_QMI_PCM_FORMAT_S16_LE:
+ return SNDRV_PCM_FORMAT_S16_LE;
+ case USB_QMI_PCM_FORMAT_S16_BE:
+ return SNDRV_PCM_FORMAT_S16_BE;
+ case USB_QMI_PCM_FORMAT_U16_LE:
+ return SNDRV_PCM_FORMAT_U16_LE;
+ case USB_QMI_PCM_FORMAT_U16_BE:
+ return SNDRV_PCM_FORMAT_U16_BE;
+ case USB_QMI_PCM_FORMAT_S24_LE:
+ return SNDRV_PCM_FORMAT_S24_LE;
+ case USB_QMI_PCM_FORMAT_S24_BE:
+ return SNDRV_PCM_FORMAT_S24_BE;
+ case USB_QMI_PCM_FORMAT_U24_LE:
+ return SNDRV_PCM_FORMAT_U24_LE;
+ case USB_QMI_PCM_FORMAT_U24_BE:
+ return SNDRV_PCM_FORMAT_U24_BE;
+ case USB_QMI_PCM_FORMAT_S24_3LE:
+ return SNDRV_PCM_FORMAT_S24_3LE;
+ case USB_QMI_PCM_FORMAT_S24_3BE:
+ return SNDRV_PCM_FORMAT_S24_3BE;
+ case USB_QMI_PCM_FORMAT_U24_3LE:
+ return SNDRV_PCM_FORMAT_U24_3LE;
+ case USB_QMI_PCM_FORMAT_U24_3BE:
+ return SNDRV_PCM_FORMAT_U24_3BE;
+ case USB_QMI_PCM_FORMAT_S32_LE:
+ return SNDRV_PCM_FORMAT_S32_LE;
+ case USB_QMI_PCM_FORMAT_S32_BE:
+ return SNDRV_PCM_FORMAT_S32_BE;
+ case USB_QMI_PCM_FORMAT_U32_LE:
+ return SNDRV_PCM_FORMAT_U32_LE;
+ case USB_QMI_PCM_FORMAT_U32_BE:
+ return SNDRV_PCM_FORMAT_U32_BE;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int info_idx_from_ifnum(int card_num, int intf_num, bool enable)
+{
+ int i;
+
+ /*
+ * default index 0 is used when info is allocated upon
+ * first enable audio stream req for a pcm device
+ */
+ if (enable && !uadev[card_num].info)
+ return 0;
+
+ for (i = 0; i < uadev[card_num].num_intf; i++) {
+ if (enable && !uadev[card_num].info[i].in_use)
+ return i;
+ else if (!enable &&
+ uadev[card_num].info[i].intf_num == intf_num)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int handle_uaudio_stream_req(void *req_h, void *req)
+{
+ struct qmi_uaudio_stream_req_msg_v01 *req_msg;
+ struct qmi_uaudio_stream_resp_msg_v01 resp = {{0}, 0};
+ struct snd_usb_substream *subs;
+ struct snd_usb_audio *chip = NULL;
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+ struct intf_info *info;
+ int pcm_format;
+ u8 pcm_card_num, pcm_dev_num, direction;
+ int info_idx = -EINVAL, ret = 0;
+
+ req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)req;
+
+ if (!req_msg->audio_format_valid || !req_msg->bit_rate_valid ||
+ !req_msg->number_of_ch_valid || !req_msg->xfer_buff_size_valid) {
+ pr_err("%s: invalid request msg\n", __func__);
+ ret = -EINVAL;
+ goto response;
+ }
+
+ direction = req_msg->usb_token & SND_PCM_STREAM_DIRECTION;
+ pcm_dev_num = (req_msg->usb_token & SND_PCM_DEV_NUM_MASK) >> 8;
+ pcm_card_num = (req_msg->usb_token & SND_PCM_CARD_NUM_MASK) >> 16;
+
+ pr_debug("%s:card#:%d dev#:%d dir:%d en:%d fmt:%d rate:%d #ch:%d\n",
+ __func__, pcm_card_num, pcm_dev_num, direction, req_msg->enable,
+ req_msg->audio_format, req_msg->bit_rate,
+ req_msg->number_of_ch);
+
+ if (pcm_card_num >= SNDRV_CARDS) {
+ pr_err("%s: invalid card # %u", __func__, pcm_card_num);
+ ret = -EINVAL;
+ goto response;
+ }
+
+ pcm_format = map_pcm_format(req_msg->audio_format);
+ if (pcm_format == -EINVAL) {
+ pr_err("%s: unsupported pcm format received %d\n",
+ __func__, req_msg->audio_format);
+ ret = -EINVAL;
+ goto response;
+ }
+
+ subs = find_snd_usb_substream(pcm_card_num, pcm_dev_num, direction,
+ &chip, uaudio_disconnect_cb);
+ if (!subs || !chip || atomic_read(&chip->shutdown)) {
+ pr_err("%s: can't find substream for card# %u, dev# %u dir%u\n",
+ __func__, pcm_card_num, pcm_dev_num, direction);
+ ret = -ENODEV;
+ goto response;
+ }
+
+ mutex_lock(&chip->dev_lock);
+ info_idx = info_idx_from_ifnum(pcm_card_num, subs->interface,
+ req_msg->enable);
+ if (atomic_read(&chip->shutdown) || !subs->stream || !subs->stream->pcm
+ || !subs->stream->chip) {
+ ret = -ENODEV;
+ mutex_unlock(&chip->dev_lock);
+ goto response;
+ }
+
+ if (req_msg->enable) {
+ if (info_idx < 0) {
+ pr_err("%s interface# %d already in use card# %d\n",
+ __func__, subs->interface, pcm_card_num);
+ ret = -EBUSY;
+ mutex_unlock(&chip->dev_lock);
+ goto response;
+ }
+ }
+
+ subs->pcm_format = pcm_format;
+ subs->channels = req_msg->number_of_ch;
+ subs->cur_rate = req_msg->bit_rate;
+ uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf;
+
+ ret = snd_usb_enable_audio_stream(subs, req_msg->enable);
+
+ if (!ret && req_msg->enable)
+ ret = prepare_qmi_response(subs, req_msg, &resp, info_idx);
+
+ mutex_unlock(&chip->dev_lock);
+
+response:
+ if (!req_msg->enable && ret != -EINVAL) {
+ if (info_idx >= 0) {
+ mutex_lock(&chip->dev_lock);
+ info = &uadev[pcm_card_num].info[info_idx];
+ uaudio_dev_intf_cleanup(uadev[pcm_card_num].udev, info);
+ pr_debug("%s:release resources: intf# %d card# %d\n",
+ __func__, subs->interface, pcm_card_num);
+ mutex_unlock(&chip->dev_lock);
+ }
+ if (atomic_read(&uadev[pcm_card_num].in_use))
+ kref_put(&uadev[pcm_card_num].kref,
+ uaudio_dev_release);
+ }
+
+ resp.usb_token = req_msg->usb_token;
+ resp.usb_token_valid = 1;
+ resp.internal_status = ret;
+ resp.internal_status_valid = 1;
+ resp.status = ret ? USB_AUDIO_STREAM_REQ_FAILURE_V01 : ret;
+ resp.status_valid = 1;
+ ret = qmi_send_resp_from_cb(svc->uaudio_svc_hdl, svc->curr_conn, req_h,
+ &uaudio_stream_resp_desc, &resp, sizeof(resp));
+
+ svc->t_resp_sent = ktime_get();
+
+ pr_debug("%s: t_resp sent - t_req recvd (in ms) %lld\n", __func__,
+ ktime_to_ms(ktime_sub(svc->t_resp_sent, svc->t_request_recvd)));
+
+ return ret;
+}
+
+static int uaudio_qmi_svc_connect_cb(struct qmi_handle *handle,
+ void *conn_h)
+{
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+
+ if (svc->uaudio_svc_hdl != handle || !conn_h) {
+ pr_err("%s: handle mismatch\n", __func__);
+ return -EINVAL;
+ }
+ if (svc->curr_conn) {
+ pr_err("%s: Service is busy\n", __func__);
+ return -ECONNREFUSED;
+ }
+ svc->curr_conn = conn_h;
+ return 0;
+}
+
+static void uaudio_qmi_disconnect_work(struct work_struct *w)
+{
+ struct intf_info *info;
+ int idx, if_idx;
+ struct snd_usb_substream *subs;
+ struct snd_usb_audio *chip = NULL;
+
+ /* find all active intf for set alt 0 and cleanup usb audio dev */
+ for (idx = 0; idx < SNDRV_CARDS; idx++) {
+ if (!atomic_read(&uadev[idx].in_use))
+ continue;
+
+ for (if_idx = 0; if_idx < uadev[idx].num_intf; if_idx++) {
+ if (!uadev[idx].info || !uadev[idx].info[if_idx].in_use)
+ continue;
+ info = &uadev[idx].info[if_idx];
+ subs = find_snd_usb_substream(info->pcm_card_num,
+ info->pcm_dev_num,
+ info->direction,
+ &chip,
+ uaudio_disconnect_cb);
+ if (!subs || !chip || atomic_read(&chip->shutdown)) {
+ pr_debug("%s:no subs for c#%u, dev#%u dir%u\n",
+ __func__, info->pcm_card_num,
+ info->pcm_dev_num,
+ info->direction);
+ continue;
+ }
+ snd_usb_enable_audio_stream(subs, 0);
+ }
+ atomic_set(&uadev[idx].in_use, 0);
+ mutex_lock(&chip->dev_lock);
+ uaudio_dev_cleanup(&uadev[idx]);
+ mutex_unlock(&chip->dev_lock);
+ }
+}
+
+static int uaudio_qmi_svc_disconnect_cb(struct qmi_handle *handle,
+ void *conn_h)
+{
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+
+ if (svc->uaudio_svc_hdl != handle || svc->curr_conn != conn_h) {
+ pr_err("%s: handle mismatch\n", __func__);
+ return -EINVAL;
+ }
+
+ svc->curr_conn = NULL;
+ queue_work(svc->uaudio_wq, &svc->qmi_disconnect_work);
+
+ return 0;
+}
+
+static int uaudio_qmi_svc_req_cb(struct qmi_handle *handle, void *conn_h,
+ void *req_h, unsigned int msg_id, void *req)
+{
+ int ret;
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+
+ if (svc->uaudio_svc_hdl != handle || svc->curr_conn != conn_h) {
+ pr_err("%s: handle mismatch\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (msg_id) {
+ case QMI_UAUDIO_STREAM_REQ_V01:
+ ret = handle_uaudio_stream_req(req_h, req);
+ break;
+
+ default:
+ ret = -ENOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+static int uaudio_qmi_svc_req_desc_cb(unsigned int msg_id,
+ struct msg_desc **req_desc)
+{
+ int ret;
+
+ pr_debug("%s: msg_id %d\n", __func__, msg_id);
+
+ switch (msg_id) {
+ case QMI_UAUDIO_STREAM_REQ_V01:
+ *req_desc = &uaudio_stream_req_desc;
+ ret = sizeof(struct qmi_uaudio_stream_req_msg_v01);
+ break;
+
+ default:
+ ret = -ENOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+static void uaudio_qmi_svc_recv_msg(struct work_struct *w)
+{
+ int ret;
+ struct uaudio_qmi_svc *svc = container_of(w, struct uaudio_qmi_svc,
+ recv_msg_work);
+
+ do {
+ pr_debug("%s: Notified about a Receive Event", __func__);
+ } while ((ret = qmi_recv_msg(svc->uaudio_svc_hdl)) == 0);
+
+ if (ret != -ENOMSG)
+ pr_err("%s: Error receiving message\n", __func__);
+}
+
+static void uaudio_qmi_svc_ntfy(struct qmi_handle *handle,
+ enum qmi_event_type event, void *priv)
+{
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+
+ pr_debug("%s: event %d", __func__, event);
+
+ svc->t_request_recvd = ktime_get();
+
+ switch (event) {
+ case QMI_RECV_MSG:
+ queue_work(svc->uaudio_wq, &svc->recv_msg_work);
+ break;
+ default:
+ break;
+ }
+}
+
+static struct qmi_svc_ops_options uaudio_svc_ops_options = {
+ .version = 1,
+ .service_id = UAUDIO_STREAM_SERVICE_ID_V01,
+ .service_vers = UAUDIO_STREAM_SERVICE_VERS_V01,
+ .connect_cb = uaudio_qmi_svc_connect_cb,
+ .disconnect_cb = uaudio_qmi_svc_disconnect_cb,
+ .req_desc_cb = uaudio_qmi_svc_req_desc_cb,
+ .req_cb = uaudio_qmi_svc_req_cb,
+};
+
+static int uaudio_qmi_plat_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct device_node *node = pdev->dev.of_node;
+
+ uaudio_qdev = devm_kzalloc(&pdev->dev, sizeof(struct uaudio_qmi_dev),
+ GFP_KERNEL);
+ if (!uaudio_qdev)
+ return -ENOMEM;
+
+ uaudio_qdev->dev = &pdev->dev;
+
+ ret = of_property_read_u32(node, "qcom,usb-audio-stream-id",
+ &uaudio_qdev->sid);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read sid.\n");
+ return -ENODEV;
+ }
+
+ ret = of_property_read_u32(node, "qcom,usb-audio-intr-num",
+ &uaudio_qdev->intr_num);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read intr num.\n");
+ return -ENODEV;
+ }
+
+ uaudio_qdev->domain = iommu_domain_alloc(msm_iommu_get_bus(&pdev->dev));
+ if (!uaudio_qdev->domain) {
+ dev_err(&pdev->dev, "failed to callocate iommu domain\n");
+ return -ENODEV;
+ }
+
+ /* attach to external processor iommu */
+ ret = iommu_attach_device(uaudio_qdev->domain, &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to attach device ret = %d\n", ret);
+ goto free_domain;
+ }
+
+ /* initialize dcba, xfer ring and xfer buf iova list */
+ INIT_LIST_HEAD(&uaudio_qdev->dcba_list);
+ uaudio_qdev->curr_dcba_iova = IOVA_DCBA_BASE;
+ uaudio_qdev->dcba_iova_size = SNDRV_CARDS * PAGE_SIZE;
+
+ INIT_LIST_HEAD(&uaudio_qdev->xfer_ring_list);
+ uaudio_qdev->curr_xfer_ring_iova = IOVA_XFER_RING_BASE;
+ uaudio_qdev->xfer_ring_iova_size =
+ IOVA_XFER_RING_MAX - IOVA_XFER_RING_BASE;
+
+ INIT_LIST_HEAD(&uaudio_qdev->xfer_buf_list);
+ uaudio_qdev->curr_xfer_buf_iova = IOVA_XFER_BUF_BASE;
+ uaudio_qdev->xfer_buf_iova_size =
+ IOVA_XFER_BUF_MAX - IOVA_XFER_BUF_BASE;
+
+ return 0;
+
+free_domain:
+ iommu_domain_free(uaudio_qdev->domain);
+ return ret;
+}
+
+static int uaudio_qmi_plat_remove(struct platform_device *pdev)
+{
+ iommu_detach_device(uaudio_qdev->domain, &pdev->dev);
+ iommu_domain_free(uaudio_qdev->domain);
+ uaudio_qdev->domain = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id of_uaudio_matach[] = {
+ {
+ .compatible = "qcom,usb-audio-qmi-dev",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_uaudio_matach);
+
+static struct platform_driver uaudio_qmi_driver = {
+ .probe = uaudio_qmi_plat_probe,
+ .remove = uaudio_qmi_plat_remove,
+ .driver = {
+ .name = "uaudio-qmi",
+ .of_match_table = of_uaudio_matach,
+ },
+};
+
+static int uaudio_qmi_svc_init(void)
+{
+ int ret;
+ struct uaudio_qmi_svc *svc;
+
+ svc = kzalloc(sizeof(struct uaudio_qmi_svc), GFP_KERNEL);
+ if (!svc)
+ return -ENOMEM;
+
+ svc->uaudio_wq = create_singlethread_workqueue("uaudio_svc");
+ if (!svc->uaudio_wq) {
+ ret = -ENOMEM;
+ goto free_svc;
+ }
+
+ svc->uaudio_svc_hdl = qmi_handle_create(uaudio_qmi_svc_ntfy, NULL);
+ if (!svc->uaudio_svc_hdl) {
+ pr_err("%s: Error creating svc_hdl\n", __func__);
+ ret = -EFAULT;
+ goto destroy_uaudio_wq;
+ }
+
+ ret = qmi_svc_register(svc->uaudio_svc_hdl, &uaudio_svc_ops_options);
+ if (ret < 0) {
+ pr_err("%s:Error registering uaudio svc %d\n", __func__, ret);
+ goto destroy_svc_handle;
+ }
+
+ INIT_WORK(&svc->recv_msg_work, uaudio_qmi_svc_recv_msg);
+ INIT_WORK(&svc->qmi_disconnect_work, uaudio_qmi_disconnect_work);
+
+ uaudio_svc = svc;
+
+ return 0;
+
+destroy_svc_handle:
+ qmi_handle_destroy(svc->uaudio_svc_hdl);
+destroy_uaudio_wq:
+ destroy_workqueue(svc->uaudio_wq);
+free_svc:
+ kfree(svc);
+ return ret;
+}
+
+static void uaudio_qmi_svc_exit(void)
+{
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+
+ qmi_svc_unregister(svc->uaudio_svc_hdl);
+ flush_workqueue(svc->uaudio_wq);
+ qmi_handle_destroy(svc->uaudio_svc_hdl);
+ destroy_workqueue(svc->uaudio_wq);
+ kfree(svc);
+ uaudio_svc = NULL;
+}
+
+static int __init uaudio_qmi_plat_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&uaudio_qmi_driver);
+ if (ret)
+ return ret;
+
+ return uaudio_qmi_svc_init();
+}
+
+static void __exit uaudio_qmi_plat_exit(void)
+{
+ uaudio_qmi_svc_exit();
+ platform_driver_unregister(&uaudio_qmi_driver);
+}
+
+module_init(uaudio_qmi_plat_init);
+module_exit(uaudio_qmi_plat_exit);
+
+MODULE_DESCRIPTION("USB AUDIO QMI Service Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/usb/usb_audio_qmi_v01.c b/sound/usb/usb_audio_qmi_v01.c
new file mode 100644
index 000000000000..beff1aaf4981
--- /dev/null
+++ b/sound/usb/usb_audio_qmi_v01.c
@@ -0,0 +1,833 @@
+ /* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/qmi_encdec.h>
+
+#include <soc/qcom/msm_qmi_interface.h>
+
+#include "usb_audio_qmi_v01.h"
+
+static struct elem_info mem_info_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct mem_info_v01,
+ va),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct mem_info_v01,
+ pa),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct mem_info_v01,
+ size),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info apps_mem_info_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct mem_info_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct apps_mem_info_v01,
+ evt_ring),
+ .ei_array = mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct mem_info_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct apps_mem_info_v01,
+ tr_data),
+ .ei_array = mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct mem_info_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct apps_mem_info_v01,
+ tr_sync),
+ .ei_array = mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct mem_info_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct apps_mem_info_v01,
+ xfer_buff),
+ .ei_array = mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct mem_info_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct apps_mem_info_v01,
+ dcba),
+ .ei_array = mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info usb_endpoint_descriptor_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bLength),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bDescriptorType),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bEndpointAddress),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bmAttributes),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ wMaxPacketSize),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bInterval),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bRefresh),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bSynchAddress),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info usb_interface_descriptor_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bLength),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bDescriptorType),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bInterfaceNumber),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bAlternateSetting),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bNumEndpoints),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bInterfaceClass),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bInterfaceSubClass),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bInterfaceProtocol),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ iInterface),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info qmi_uaudio_stream_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ enable),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ usb_token),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ audio_format_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ audio_format),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ number_of_ch_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ number_of_ch),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ bit_rate_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ bit_rate),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ xfer_buff_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ xfer_buff_size),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ status_valid),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum usb_audio_stream_status_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ status),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ internal_status_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ internal_status),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ slot_id_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ slot_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ usb_token_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ usb_token),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_opr_intf_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_interface_descriptor_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_opr_intf_desc),
+ .ei_array = usb_interface_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_data_ep_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_endpoint_descriptor_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_data_ep_desc),
+ .ei_array = usb_endpoint_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_sync_ep_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_endpoint_descriptor_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_sync_ep_desc),
+ .ei_array = usb_endpoint_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ usb_audio_spec_revision_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ usb_audio_spec_revision),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ data_path_delay_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ data_path_delay),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ usb_audio_subslot_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ usb_audio_subslot_size),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1A,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ xhci_mem_info_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct apps_mem_info_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1A,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ xhci_mem_info),
+ .ei_array = apps_mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1B,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ interrupter_num_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1B,
+ .offset = offsetof(
+ struct qmi_uaudio_stream_resp_msg_v01,
+ interrupter_num),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[] = {
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(
+ enum usb_audio_device_indication_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ dev_event),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ slot_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_token_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_token),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_opr_intf_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_interface_descriptor_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_opr_intf_desc),
+ .ei_array = usb_interface_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_data_ep_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_endpoint_descriptor_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_data_ep_desc),
+ .ei_array = usb_endpoint_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_sync_ep_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_endpoint_descriptor_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_sync_ep_desc),
+ .ei_array = usb_endpoint_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_spec_revision_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_spec_revision),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ data_path_delay_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ data_path_delay),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_subslot_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_subslot_size),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ xhci_mem_info_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct apps_mem_info_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ xhci_mem_info),
+ .ei_array = apps_mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ interrupter_num_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ interrupter_num),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
diff --git a/sound/usb/usb_audio_qmi_v01.h b/sound/usb/usb_audio_qmi_v01.h
new file mode 100644
index 000000000000..f3b6eb05d5f0
--- /dev/null
+++ b/sound/usb/usb_audio_qmi_v01.h
@@ -0,0 +1,150 @@
+ /* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef USB_QMI_V01_H
+#define USB_QMI_V01_H
+
+#define UAUDIO_STREAM_SERVICE_ID_V01 0x41D
+#define UAUDIO_STREAM_SERVICE_VERS_V01 0x01
+
+#define QMI_UAUDIO_STREAM_RESP_V01 0x0001
+#define QMI_UAUDIO_STREAM_REQ_V01 0x0001
+#define QMI_UADUIO_STREAM_IND_V01 0x0001
+
+
+struct mem_info_v01 {
+ uint64_t va;
+ uint64_t pa;
+ uint32_t size;
+};
+
+struct apps_mem_info_v01 {
+ struct mem_info_v01 evt_ring;
+ struct mem_info_v01 tr_data;
+ struct mem_info_v01 tr_sync;
+ struct mem_info_v01 xfer_buff;
+ struct mem_info_v01 dcba;
+};
+
+struct usb_endpoint_descriptor_v01 {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bEndpointAddress;
+ uint8_t bmAttributes;
+ uint16_t wMaxPacketSize;
+ uint8_t bInterval;
+ uint8_t bRefresh;
+ uint8_t bSynchAddress;
+};
+
+struct usb_interface_descriptor_v01 {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t iInterface;
+};
+
+enum usb_audio_stream_status_enum_v01 {
+ USB_AUDIO_STREAM_STATUS_ENUM_MIN_VAL_V01 = INT_MIN,
+ USB_AUDIO_STREAM_REQ_SUCCESS_V01 = 0,
+ USB_AUDIO_STREAM_REQ_FAILURE_V01 = 1,
+ USB_AUDIO_STREAM_REQ_FAILURE_NOT_FOUND_V01 = 2,
+ USB_AUDIO_STREAM_REQ_FAILURE_INVALID_PARAM_V01 = 3,
+ USB_AUDIO_STREAM_REQ_FAILURE_MEMALLOC_V01 = 4,
+ USB_AUDIO_STREAM_STATUS_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
+enum usb_audio_device_indication_enum_v01 {
+ USB_AUDIO_DEVICE_INDICATION_ENUM_MIN_VAL_V01 = INT_MIN,
+ USB_AUDIO_DEV_CONNECT_V01 = 0,
+ USB_AUDIO_DEV_DISCONNECT_V01 = 1,
+ USB_AUDIO_DEV_SUSPEND_V01 = 2,
+ USB_AUDIO_DEV_RESUME_V01 = 3,
+ USB_AUDIO_DEVICE_INDICATION_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
+struct qmi_uaudio_stream_req_msg_v01 {
+ uint8_t enable;
+ uint32_t usb_token;
+ uint8_t audio_format_valid;
+ uint32_t audio_format;
+ uint8_t number_of_ch_valid;
+ uint32_t number_of_ch;
+ uint8_t bit_rate_valid;
+ uint32_t bit_rate;
+ uint8_t xfer_buff_size_valid;
+ uint32_t xfer_buff_size;
+};
+#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 39
+extern struct elem_info qmi_uaudio_stream_req_msg_v01_ei[];
+
+struct qmi_uaudio_stream_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+ uint8_t status_valid;
+ enum usb_audio_stream_status_enum_v01 status;
+ uint8_t internal_status_valid;
+ uint32_t internal_status;
+ uint8_t slot_id_valid;
+ uint32_t slot_id;
+ uint8_t usb_token_valid;
+ uint32_t usb_token;
+ uint8_t std_as_opr_intf_desc_valid;
+ struct usb_interface_descriptor_v01 std_as_opr_intf_desc;
+ uint8_t std_as_data_ep_desc_valid;
+ struct usb_endpoint_descriptor_v01 std_as_data_ep_desc;
+ uint8_t std_as_sync_ep_desc_valid;
+ struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc;
+ uint8_t usb_audio_spec_revision_valid;
+ uint16_t usb_audio_spec_revision;
+ uint8_t data_path_delay_valid;
+ uint8_t data_path_delay;
+ uint8_t usb_audio_subslot_size_valid;
+ uint8_t usb_audio_subslot_size;
+ uint8_t xhci_mem_info_valid;
+ struct apps_mem_info_v01 xhci_mem_info;
+ uint8_t interrupter_num_valid;
+ uint8_t interrupter_num;
+};
+#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 191
+extern struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[];
+
+struct qmi_uaudio_stream_ind_msg_v01 {
+ enum usb_audio_device_indication_enum_v01 dev_event;
+ uint32_t slot_id;
+ uint8_t usb_token_valid;
+ uint32_t usb_token;
+ uint8_t std_as_opr_intf_desc_valid;
+ struct usb_interface_descriptor_v01 std_as_opr_intf_desc;
+ uint8_t std_as_data_ep_desc_valid;
+ struct usb_endpoint_descriptor_v01 std_as_data_ep_desc;
+ uint8_t std_as_sync_ep_desc_valid;
+ struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc;
+ uint8_t usb_audio_spec_revision_valid;
+ uint16_t usb_audio_spec_revision;
+ uint8_t data_path_delay_valid;
+ uint8_t data_path_delay;
+ uint8_t usb_audio_subslot_size_valid;
+ uint8_t usb_audio_subslot_size;
+ uint8_t xhci_mem_info_valid;
+ struct apps_mem_info_v01 xhci_mem_info;
+ uint8_t interrupter_num_valid;
+ uint8_t interrupter_num;
+};
+#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 177
+extern struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[];
+
+#endif
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 09ecc7afdc4f..2c70e0961e3d 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -60,6 +60,10 @@ struct snd_usb_audio {
bool autoclock; /* from the 'autoclock' module param */
struct usb_host_interface *ctrl_intf; /* the audio control interface */
+
+ struct mutex dev_lock; /* to protect any race with disconnect */
+ int card_num; /* cache pcm card number to use upon disconnect */
+ void (*disconnect_cb)(struct snd_usb_audio *chip);
};
#define USB_AUDIO_IFACE_UNUSED ((void *)-1L)